6

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.

501 - not implemented
  • 2,638
  • 4
  • 39
  • 74

1 Answers1

6

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.

ArtemStorozhuk
  • 8,715
  • 4
  • 35
  • 53
  • jey, now it works great for me :) But why do you take YCbCr? All the time i thought LAB is the best to detect colors PS: i can hit +1 only then i have 15 reputations. – 501 - not implemented Jun 27 '12 at 15:22
  • 1
    @destiny because YCbCr is the best for blue/red color detection (Cb means channel blue). You can try HSV (or LAB, or some other) - I have already mentioned that in my answer. – ArtemStorozhuk Jun 27 '12 at 16:24