1 | initial version |
Hello,
I don't know if there are any universal methods for such problems. But for the example above you can try this method. The output from each stage of the algorithm is given below
This is the output of adaptive thresholding stage
Output of cca
Final output showing bounding boxes overlapped over the original image
The algorithm code is given below
Mat image;
// read the input image
image = imread("D:\\work\\Forums\\opencv\\14635747219571405.png");
// convert to gray
Mat gray;
cvtColor(image, gray, CV_BGR2GRAY);
// pre processing
// remove shadows first
const int SHADOW_THRESHOLD = 30;
Mat preproc;
threshold(gray, preproc, SHADOW_THRESHOLD, 255, CV_THRESH_TOZERO);
imshow("preproc", preproc);
// blur the image two times to remove noise with 3x3 kernel
Mat blurred;
blur(preproc, blurred, Size(3,3));
blur(blurred, blurred, Size(3,3));
imshow("blurred", blurred);
waitKey();
// apply and adaptive threshold with a block size of 5x5 and c = 1
Mat thresh;
adaptiveThreshold(blurred, thresh, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, 5, 1);
// negative of the threshold output
thresh = 255 - thresh;
imshow("Threshold", thresh);
// apply a connected component analysis with statistics
// (from opencv 3.0 onwards)
//cca
Mat labels, stats, centroids;
Mat cca = thresh;
int numObjects = connectedComponentsWithStats(thresh, labels, stats, centroids);
// go through the whole image and remove all pixels which are part
// of objects whose area is less than MIN_OBJECT_AREA
const int MIN_OBJECT_AREA = 150;
int i,j;
int* p;
for( i = 0; i < cca.rows; ++i)
{
p = labels.ptr<int>(i);
for ( j = 0; j < cca.cols; ++j)
{
// area < MIN_OBJECT_AREA
if(stats.at<int>(p[j],CC_STAT_AREA ) < MIN_OBJECT_AREA )
cca.at<uchar>(i,j) = 0;
}
}
imshow("cca", cca);
// Draw bounding boxes for objects in the original image
// exclude background = 0
for(i=1; i<numObjects; i++)
{
if(stats.at<int>(i, CC_STAT_AREA) >= MIN_OBJECT_AREA)
{
int left = stats.at<int>(i, CC_STAT_LEFT);
int top = stats.at<int>(i, CC_STAT_TOP);
int width = stats.at<int>(i, CC_STAT_WIDTH);
int height = stats.at<int>(i, CC_STAT_HEIGHT);
// draw rectangle
rectangle(image, Rect(left, top, width, height), Scalar(0,255,0), 2);
}
}
imshow("BoundingBoxes", image);
Feel free to ask in case of any doubt
Thanks Amal
2 | No.2 Revision |
Hello,
I don't know if there are any universal methods for such problems. But for the example above you can try this method. The output from each stage of the algorithm is given below
This is the output of adaptive thresholding stage
Output of cca
Final output showing bounding boxes overlapped over the original image
The algorithm code is given below
Mat image;
// read the input image
image = imread("D:\\work\\Forums\\opencv\\14635747219571405.png");
// convert to gray
Mat gray;
cvtColor(image, gray, CV_BGR2GRAY);
// pre processing
// remove shadows first
const int SHADOW_THRESHOLD = 30;
Mat preproc;
threshold(gray, preproc, SHADOW_THRESHOLD, 255, CV_THRESH_TOZERO);
imshow("preproc", preproc);
// blur the image two times to remove noise with 3x3 kernel
Mat blurred;
blur(preproc, blurred, Size(3,3));
blur(blurred, blurred, Size(3,3));
imshow("blurred", blurred);
waitKey();
// apply and adaptive threshold with a block size of 5x5 and c = 1
Mat thresh;
adaptiveThreshold(blurred, thresh, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, 5, 1);
// negative of the threshold output
thresh = 255 - thresh;
imshow("Threshold", thresh);
// apply a connected component analysis with statistics
// (from opencv 3.0 onwards)
//cca
Mat labels, stats, centroids;
Mat cca = thresh;
int numObjects = connectedComponentsWithStats(thresh, labels, stats, centroids);
// go through the whole image and remove all pixels which are part
// of objects whose area is less than MIN_OBJECT_AREA
const int MIN_OBJECT_AREA = 150;
int i,j;
int* p;
for( i = 0; i < cca.rows; ++i)
{
p = labels.ptr<int>(i);
for ( j = 0; j < cca.cols; ++j)
{
// area < MIN_OBJECT_AREA
if(stats.at<int>(p[j],CC_STAT_AREA ) < MIN_OBJECT_AREA )
cca.at<uchar>(i,j) = 0;
}
}
imshow("cca", cca);
// Draw bounding boxes for objects in the original image
// exclude background = 0
for(i=1; i<numObjects; i++)
{
if(stats.at<int>(i, CC_STAT_AREA) >= MIN_OBJECT_AREA)
{
int left = stats.at<int>(i, CC_STAT_LEFT);
int top = stats.at<int>(i, CC_STAT_TOP);
int width = stats.at<int>(i, CC_STAT_WIDTH);
int height = stats.at<int>(i, CC_STAT_HEIGHT);
// draw rectangle
rectangle(image, Rect(left, top, width, height), Scalar(0,255,0), 2);
}
}
imshow("BoundingBoxes", image);
As per @sturkmen suggestion, here is the output using minAreaRect(). It shows the rotated bounding boxes in green.
The changed code is given below
// Draw bounding boxes for objects in the original image
const int MIN_CONTOUR_LENGTH = 50;
vector<vector<Point> > contours;
// find contours
findContours(thresh, contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
for( int i = 0; i < contours.size(); i++ )
{
if(contours[i].size() > MIN_CONTOUR_LENGTH )
{
RotatedRect minRect = minAreaRect( Mat(contours[i]) );
// rotated rectangle
Point2f rect_points[4];
minRect.points( rect_points );
for( int j = 0; j < 4; j++ )
line( image, rect_points[j], rect_points[(j+1)%4], Scalar(0,255,0), 2, 8 );
}
}
Feel free to ask in case of any doubt
Thanks Amal