Search code examples
image-processingopencvjavacv

How to get size of an area in JavaCV


In my project I want to get the size from the greatest homogeneous area of a specific color (in my example below it's the blue sky).

My first idea is to convert the orginal image:

original image

to an binary image, detect the skycolor and create a mask with this area: mask image

But how can I get the size and the position of these white pixels? I want a efficient method, which says true, if the picture has a blue sky in the upper 1/3 of the picture. Any ideas? Should I create a "global mask" (see image 3 in comment) and compare it with the binary picture? Or is there an easier way?

Thank you.


Solution

  • The algorithm is the following:

    1. Convert input image to YCbCr color space which is good to detect blue (and also red) color: YCrCb image To convert some image to another color space use cvtColor.
    2. Extract blue channel from it: Blue image Use function extractChannel to extract needed channel.
    3. Detect regions with biggest value [0-255] of blue color. I used function minMaxIdx and then just multiplied maximum on 0.8 (this is threshold). You can use more complex methods like histogram analysation.
    4. Make a mask of blue color: binary For this I used threshold function with calculated in step 3 threshold (as parameter).
    5. Find all blue contours in mask. In OpenCV it's easy - just use findContours.
    6. And, finally, detect contour with biggest square and find its coordinates (center). To calculate contour with biggest square you can use function contourArea.

    Also instead of steps 1-4 you can convert image to HSV and using inRange detect blue color.

    Here's my c++ impementation:

    Mat inMat = imread("input.jpg"), blueMat, threshMat;
    
    cvtColor(inMat, blueMat, CV_BGR2YCrCb);//convert to YCrCb color space
    
    extractChannel(blueMat, blueMat, 2);//get blue channel
    
    //find max value of blue color
    //or you can use histograms
    //or more complex mathod
    double blueMax;
    minMaxIdx(blueMat, 0, &blueMax);
    
    blueMax *= 0.8;
    //make binary mask
    threshold(blueMat, threshMat, blueMax, 255, THRESH_BINARY);
    
    //finding all blue contours:
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;
    findContours(blueMat, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
    
    double maxSquare = 0;
    vector<Point> maxContour;
    //finding contours with biggest square:
    for (size_t i=0; i<contours.size(); i++)
    {
        double square = contourArea(contours[i]);
        if (square > maxSquare)
        {
            maxContour = contours[i];
            maxSquare = square;
        }
    }
    
    //output results:
    Point center = centerPolygon(maxContour);
    cout << "square = " << maxSquare << endl;
    cout << "position: x: " << center.x << ", y: " << center.y << endl;
    

    Here's centerPolygon function:

    Point centerPolygon(const vector<Point>& points)
    {
        int x=0, y=0;
    
        for (size_t i=0; i<points.size(); i++)
        {
            x += points[i].x;
            y += points[i].y;
        }
    
        return Point(x/points.size(), y/points.size());
    }
    

    The output of program is next:

    square = 263525
    position: x: 318, y: 208
    

    You can convert this code to JavaCV - see this tutorial.