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 background of the image, since you then have a fourth channel (A channel in BGRA) 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:
Cropped ROI:
Would you mind posting the images and how they should be aligned to each other?