iKK
iKK

Reputation: 7012

HSV color detection with OpenCV

The "red" color-detection is not working yet. The following code is supposed to detect a red bar from an input-image and return a mask-image showing a white bar at the corresponding location.

The corresponding HSV-values of the "red" bar in the inputRGBimage are : H = 177, S = 252, V = 244

    cv::Mat findColor(cv::Mat inputRGBimage) {

    cv::Mat imageHSV(inputRGBimage.rows, inputRGBimage.cols, CV_8UC3);
    cv::Mat imgThreshold(inputRGBimage.rows, inputRGBimage.cols, CV_8UC1);

    // convert input-image to HSV-image
    cvtColor(inputRGBimage, imageHSV, cv::COLOR_BGR2HSV);

    // for red: (H < 14)
    // cv::inRange(imageHSV, cv::Scalar(0, 53, 185, 0), cv::Scalar(14, 255, 255, 0), imgThreshold);
    // or (H > 165) (...closing HSV-circle)
    cv::inRange(imageHSV, cv::Scalar(165, 53, 185, 0), cv::Scalar(180, 255, 255, 0), imgThreshold);

    return imgThreshold;
}

The two images below show the inputRGBimage (top) and the returned imgThreshold (bottom). As you can see, the mask is not showing the white bar at the expected color "red" but shows it for some unknown reason at the "blue" bar. Why ????

wrong mask 01

The following change of the cv::inRange line of code (i.e. H > 120) and its result again illustrates that the color detection is not actually acting as expected :

    // or (H > 120) (...closing HSV-circle)
    cv::inRange(imageHSV, cv::Scalar(120, 53, 185, 0), cv::Scalar(180, 255, 255, 0), imgThreshold);

wrong mask 02

As a third example: (H > 100):

    // or (H > 100) (...closing HSV-circle)
    cv::inRange(imageHSV, cv::Scalar(100, 53, 185, 0), cv::Scalar(180, 255, 255, 0), imgThreshold);

wrong mask 03

Why the unexpected order of colors in my 3 code-examples (decreasing the H-value from 165 to 100) showing mask orders of "blue->violet->red->orange" instead of the actually expected HSV-wheel rough order of "red->violet->blue->green->yellow->orange" ?????

HSV table

HSV in OpenCV has ranges: 0 <= H <= 180, 0 <= S <= 255, 0 <= V <= 255, (not quite like in the illustrating graphic above - but the order of colors should be the same for OpenCV HSV-colors - or not ???)

Upvotes: 2

Views: 10542

Answers (1)

nils
nils

Reputation: 2534

Make sure that the image uses the channel order B, G, R. Also, for the color red you need check two ranges of values, one around H=0 and the other around H=180. You could try this function:

cv::Mat findColor(const cv::Mat & inputBGRimage, int rng=15)
{
    // Make sure that your input image uses the channel order B, G, R (check not implemented).
    cv::Mat input = inputBGRimage.clone();
    cv::Mat imageHSV;//(input.rows, input.cols, CV_8UC3);
    cv::Mat imgThreshold, imgThreshold0, imgThreshold1;//(input.rows, input.cols, CV_8UC1);
    assert( ! input.empty() );

    // convert input-image to HSV-image
    cv::cvtColor( input, imageHSV, cv::COLOR_BGR2HSV );

    // In the HSV-color space the color 'red' is located around the H-value 0 and also around the
    // H-value 180. That is why you need to threshold your image twice and the combine the results.
    cv::inRange(imageHSV, cv::Scalar(      0, 53, 185, 0), cv::Scalar(rng, 255, 255, 0), imgThreshold0);

    if ( rng > 0 )
    {
        cv::inRange(imageHSV, cv::Scalar(180-rng, 53, 185, 0), cv::Scalar(180, 255, 255, 0), imgThreshold1);
        cv::bitwise_or( imgThreshold0, imgThreshold1, imgThreshold );
    }
    else
    {
        imgThreshold = imgThreshold0;
    }

    return imgThreshold;
}

Good luck! :)

Upvotes: 3

Related Questions