30

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?

Jeru Luke
  • 20,118
  • 13
  • 80
  • 87
Kristian Vitozev
  • 5,791
  • 6
  • 36
  • 56

2 Answers2

65

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

Community
  • 1
  • 1
Miki
  • 40,887
  • 13
  • 123
  • 202
  • 4
    Just a cool little trick from @fmw42 that I wanted to share... you can invert the image and look for cyans which don't wrap :-) http://stackoverflow.com/a/43926013/2836621 – Mark Setchell May 12 '17 at 08:12
  • 1
    @MarkSetchell that's clever! Added to the answer with C++ code for completeness. Thank you very much! – Miki May 12 '17 at 08:30
  • Your response was super helpful! Thanks @Miki! – LucCW Jul 14 '17 at 17:24
  • Can you help me please? my imshow("Mask", mask) gives an error. `error: (-215) size.width>0 && size.height>0 in function imshow`. As far as I understood, the error is because mask.data == null . What am I doing wrong? Thanks in advance – gbossa Jul 23 '20 at 19:20
3

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.
Jeru Luke
  • 20,118
  • 13
  • 80
  • 87