Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

Ok, I have no idea if you have any performance-requirements, attached is a straight-forward algorithm based on trial-and-error. It continuously decreases the size of the cropped image and checks if the current region of interest is valid (through examination of the image's borders, namely if the background-color is contained in the border,, then the corresponding side of the rectangle has to be moved further towards the image's midpoint)

I'd further recommend to use instead of black a transparent background of the image, since you have a fourth channel (A channel in BGRA) then and don't have to implement a complex decision algorithm whether a detected black pixel now belongs to the image or it belongs to the background. (Could be done with examination of the local neighborhood for instance)

//============================================================================
// Name        : panoStitch.cpp
// Author      : Thomas Bergmueller
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

using namespace std;
using namespace cv;


#define BGRA_A      0


bool checkColumn(const cv::Mat& roi, int x)
{
    for(int y=0; y<roi.rows; y++)
    {
        if(roi.at<cv::Vec4b>(y,x)[3] ==  BGRA_A) // Check Transparency-Channel
        {
            // Background found..
            return false;
        }
    }

    return true;
}

bool checkLine(const cv::Mat& roi, int y)
{
    // check top and bottom
    // Mat is BGR-Coded
    for(int x=0; x<roi.cols; x++)
    {

        if(roi.at<cv::Vec4b>(y,x)[3]  == BGRA_A) // Check Transparency-Channel
        {
            // Background found..
            return false;
        }
    }

    return true;
}




bool cropLargestPossibleROI(const cv::Mat& source, cv::Mat& output, cv::Rect startROI)
{
    // evaluate start-ROI
    Mat possibleROI = source(startROI); // crop, writes a new Mat-Header

    bool topOk = checkLine(possibleROI, 0);
    bool leftOk = checkColumn(possibleROI, 0);

    bool bottomOk = checkLine(possibleROI, possibleROI.rows-1);
    bool rightOk = checkColumn(possibleROI, possibleROI.cols-1);


    if(topOk && leftOk && bottomOk && rightOk)
    {
        // Found!!
        output = source(startROI);
        return true;
    }

    // If not, scale ROI down



    Rect newROI(startROI.x, startROI.y, startROI.width, startROI.height);

    if(!leftOk) { newROI.x++; newROI.width--; } // if x is increased, width has to be decreased to compensate
    if(!topOk) { newROI.y++; newROI.height--; } // same is valid for y
    if(!rightOk) {newROI.width--; }
    if(!bottomOk) {newROI.height--; }

    cout << "Try it with ROI = " << newROI << endl;

    if(newROI.x + startROI.width < 0 || newROI.y + newROI.height < 0)
    {
        cerr << "Sorry no suitable ROI found" << endl;
        return false; // sorry...
    }

    return cropLargestPossibleROI(source,output,newROI);
}


int main()
{

    Mat src = imread("/home/tbergmueller/pano.png", CV_LOAD_IMAGE_UNCHANGED); // Image has BGRA

    assert(src.channels() == 4); // Check if transparency-Channel is there

    Mat roi;
    //Rect startROI(18,57,900, 200); // start as the source image - ROI is the complete SRC-Image
    Rect startROI(0,0,src.cols,src.rows); // start as the source image - ROI is the complete SRC-Image


    //roi = src(startROI);
    cropLargestPossibleROI(src,roi,startROI);

    imshow("ROI", roi);
    imshow("Source", src);
    waitKey();

    return 0;
}

Base image:

image description

Cropped ROI:

image description

Ok, I have no idea if you have any performance-requirements, attached is a straight-forward algorithm based on trial-and-error. It continuously decreases the size of the cropped image and checks if the current region of interest is valid (through examination of the image's borders, namely if the background-color is contained in the border,, then the corresponding side of the rectangle has to be moved further towards the image's midpoint)

I'd further recommend to use a transparent channel instead of the black a transparent background of the image, since you then have a fourth channel (A channel in BGRA) then and don't have to implement a complex decision algorithm whether a detected black pixel now belongs to the image or it belongs to the background. (Could be done with examination of the local neighborhood for instance)

//============================================================================
// Name        : panoStitch.cpp
// Author      : Thomas Bergmueller
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

using namespace std;
using namespace cv;


#define BGRA_A      0


bool checkColumn(const cv::Mat& roi, int x)
{
    for(int y=0; y<roi.rows; y++)
    {
        if(roi.at<cv::Vec4b>(y,x)[3] ==  BGRA_A) // Check Transparency-Channel
        {
            // Background found..
            return false;
        }
    }

    return true;
}

bool checkLine(const cv::Mat& roi, int y)
{
    // check top and bottom
    // Mat is BGR-Coded
    for(int x=0; x<roi.cols; x++)
    {

        if(roi.at<cv::Vec4b>(y,x)[3]  == BGRA_A) // Check Transparency-Channel
        {
            // Background found..
            return false;
        }
    }

    return true;
}




bool cropLargestPossibleROI(const cv::Mat& source, cv::Mat& output, cv::Rect startROI)
{
    // evaluate start-ROI
    Mat possibleROI = source(startROI); // crop, writes a new Mat-Header

    bool topOk = checkLine(possibleROI, 0);
    bool leftOk = checkColumn(possibleROI, 0);

    bool bottomOk = checkLine(possibleROI, possibleROI.rows-1);
    bool rightOk = checkColumn(possibleROI, possibleROI.cols-1);


    if(topOk && leftOk && bottomOk && rightOk)
    {
        // Found!!
        output = source(startROI);
        return true;
    }

    // If not, scale ROI down



    Rect newROI(startROI.x, startROI.y, startROI.width, startROI.height);

    if(!leftOk) { newROI.x++; newROI.width--; } // if x is increased, width has to be decreased to compensate
    if(!topOk) { newROI.y++; newROI.height--; } // same is valid for y
    if(!rightOk) {newROI.width--; }
    if(!bottomOk) {newROI.height--; }

    cout << "Try it with ROI = " << newROI << endl;

    if(newROI.x + startROI.width < 0 || newROI.y + newROI.height < 0)
    {
        cerr << "Sorry no suitable ROI found" << endl;
        return false; // sorry...
    }

    return cropLargestPossibleROI(source,output,newROI);
}


int main()
{

    Mat src = imread("/home/tbergmueller/pano.png", CV_LOAD_IMAGE_UNCHANGED); // Image has BGRA

    assert(src.channels() == 4); // Check if transparency-Channel is there

    Mat roi;
    //Rect startROI(18,57,900, 200); // start as the source image - ROI is the complete SRC-Image
    Rect startROI(0,0,src.cols,src.rows); // start as the source image - ROI is the complete SRC-Image


    //roi = src(startROI);
    cropLargestPossibleROI(src,roi,startROI);

    imshow("ROI", roi);
    imshow("Source", src);
    waitKey();

    return 0;
}

Base image:

image description

Cropped ROI:

image description