Ask Your Question

Revision history [back]

click to hide/show revision 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

image description This is the output of adaptive thresholding stage

image description Output of cca

image description 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

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

image description This is the output of adaptive thresholding stage

image description Output of cca

image description 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.

image description

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