Search code examples
c++opencvcropdetection

Opencv c++ detect and crop white region on image


I've searched the web nd I already found a few methods to do what I want, but these methods fail in efficiency compared to what I need.

I have a kinect(using Microsoft SDK) which iscurrently acquiring a person removing the background, saving the result in a 3 channel Mat with the person removed from the background. Now I need crop the image to fit only that person, ignoring the black region.

Here's the tricky part: I don't have many time to waste on every operation (I also need to do several other operations and this is supossed to work on real time. What I have currently implemented is a contours finder which give only this region, but it's really slow on real time. Since I only have a white region do detect and that region is really big (50% of the image area) I think there is some faster way to do this, as I only want the minimum and maximum values of x and y of this white area to crop it.

This is my currently my cropping function:

cv::Mat thresh_canny;
cv::vector<cv::vector<cv::Point> > contours;
cv::vector<cv::Vec4i> hierarchy;
cv::threshold(src, thresh_canny, 0, 255, 0);
cv::Canny(thresh_canny, thresh_canny, 20, 80, 3);
cv::findContours(thresh_canny, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0));

if (contours.size() != 1)
    return false;

cv::Rect r = cv::boundingRect(contours.at(0));
src(r).copyTo(dst);
return true;

Many thanks!!

EDIT: Input image

enter image description here


Solution

  • If your image has no non-black outlier (like noise) you can ignore the canny and the findContours and instead just create the bounding rect from all non-black pixel locations:

    int main()
    {
    cv::Mat in = cv::imread("CropWhite.jpg");
    
    // vector with all non-black point positions
    std::vector<cv::Point> nonBlackList;
    nonBlackList.reserve(in.rows*in.cols);
    
    // add all non-black points to the vector
    //TODO: there are more efficient ways to iterate through the image
    for(int j=0; j<in.rows; ++j)
        for(int i=0; i<in.cols; ++i)
        {
            // if not black: add to the list
            if(in.at<cv::Vec3b>(j,i) != cv::Vec3b(0,0,0))
            {
                nonBlackList.push_back(cv::Point(i,j));
            }
        }
    
    // create bounding rect around those points
    cv::Rect bb = cv::boundingRect(nonBlackList);
    
    // display result and save it
    cv::imshow("found rect", in(bb));
    cv::imwrite("CropWhiteResult.png", in(bb));
    
    
    cv::waitKey(-1);
    return 0;
    }
    

    don't know whether there are more efficient ways to create the vector, given in openCV, but this should still be much faster than canny and findContours.

    with this input:

    enter image description here

    I get this result:

    enter image description here

    there are some areas around the contour, because you provided a jpg image, where the borders of the contour aren't true black because of compression, I guess.