Kristian Vitozev
Kristian Vitozev

Reputation: 5971

OpenCV better detection of red color?

I have the following image:

enter image description here

I would like to detect the red rectangle using cv::inRange method and HSV color space.

int H_MIN = 0;
int H_MAX = 10;
int S_MIN = 70; 
int S_MAX = 255;
int V_MIN = 50;
int V_MAX = 255;

cv::cvtColor( input, imageHSV, cv::COLOR_BGR2HSV );

cv::inRange( imageHSV, cv::Scalar( H_MIN, S_MIN, V_MIN ), cv::Scalar( H_MAX, S_MAX, V_MAX ), imgThreshold0 );

I already created dynamic trackbars in order to change the values for HSV, but I can't get the desired result.

Any suggestion for best values (and maybe filters) to use?

Upvotes: 31

Views: 68735

Answers (2)

Jeru Luke
Jeru Luke

Reputation: 21203

While working with dominant colors such as red, blue, green and yellow; analyzing the two color channels of the LAB color space keeps things simple. All you need to do is apply a suitable threshold on either of the two color channels.

1. Detecting Red color

Background :

The LAB color space represents:

  • the brightness value in the image in the primary channel (L-channel)

while colors are expressed in the two remaining channels:

  • the color variations between red and green are expressed in the secondary channel (A-channel)
  • the color variations between yellow and blue are expressed in the third channel (B-channel)

Code :

import cv2
img = cv2.imread('red.png')

# convert to LAB color space
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)

# Perform Otsu threshold on the A-channel 
th = cv2.threshold(lab[:,:,1], 127, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

Result:

I have placed the LAB converted image and the threshold image besides each other.

enter image description here

2. Detecting Blue color

Now lets see how to detect blue color

Sample image:

enter image description here

Since I am working with blue color:

  • Analyze the B-channel (since it expresses blue color better)
  • Perform inverse threshold to make the blue region appear white

(Note: the code changes below compared to the one above)

Code :

import cv2
img = cv2.imread('blue.jpg')

# convert to LAB color space
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)

# Perform Otsu threshold on the A-channel 
th = cv2.threshold(lab[:,:,2], 127, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

Result:

Again, stacking the LAB and final image:

enter image description here

Conclusion :

  • Similar processing can be performed on green and yellow colors
  • Moreover segmenting a range of one of these dominant colors is also much simpler.

Upvotes: 3

Miki
Miki

Reputation: 41765

In HSV space, the red color wraps around 180. So you need the H values to be both in [0,10] and [170, 180].

Try this:

#include <opencv2\opencv.hpp>
using namespace cv;

int main()
{
    Mat3b bgr = imread("path_to_image");

    Mat3b hsv;
    cvtColor(bgr, hsv, COLOR_BGR2HSV);

    Mat1b mask1, mask2;
    inRange(hsv, Scalar(0, 70, 50), Scalar(10, 255, 255), mask1);
    inRange(hsv, Scalar(170, 70, 50), Scalar(180, 255, 255), mask2);

    Mat1b mask = mask1 | mask2;

    imshow("Mask", mask);
    waitKey();

    return 0;
}

Your previous result:

enter image description here

Result adding range [170, 180]:

enter image description here


Another interesting approach which needs to check a single range only is:

  • invert the BGR image
  • convert to HSV
  • look for cyan color

This idea has been proposed by fmw42 and kindly pointed out by Mark Setchell. Thank you very much for that.

#include <opencv2\opencv.hpp>
using namespace cv;

int main()
{
    Mat3b bgr = imread("path_to_image");

    Mat3b bgr_inv = ~bgr;
    Mat3b hsv_inv;
    cvtColor(bgr_inv, hsv_inv, COLOR_BGR2HSV);

    Mat1b mask; 
    inRange(hsv_inv, Scalar(90 - 10, 70, 50), Scalar(90 + 10, 255, 255), mask); // Cyan is 90

    imshow("Mask", mask);
    waitKey();

    return 0;
}

enter image description here

Upvotes: 66

Related Questions