Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

efficiently threshold red using HSV in OpenCV

Tags:

I'm trying to threshold red pixels in a video stream using OpenCV. I have other colors working quite nicely, but red poses a problem because it wraps around the hue axis (ie. HSV(0, 255, 255) and HSV(179, 255, 255) are both red). The technique I'm using now is less than ideal. Basically:

cvInRangeS(src, cvScalar(0, 135, 135), cvScalar(20, 255, 255), dstA); cvInRangeS(src, cvScalar(159, 135, 135), cvScalar(179, 255, 255), dstB); cvOr(dstA, dstB, dst); 

This is suboptimal because it requires a branch in the code for red (potential bugs), the allocation of two extra images, and two extra operations when compared to the easy case of blue:

cvInRangeS(src, cvScalar(100, 135, 135), cvScalar(140, 255, 255), dst); 

The nicer alternative that occurred to me was to "rotate" the image's colors, so that the target hue is at 90 degrees. Eg.

int rotation = 90 - 179; // 179 = red cvAddS(src, cvScalar(rotation, 0, 0), dst1); cvInRangeS(dst1, cvScalar(70, 135, 135), cvScalar(110, 255, 255), dst); 

This allows me to treat all colors similarly.

However, the cvAddS operation doesn't wrap the hue values back to 180 when they go below 0, so you lose data. I looked at converting the image to CvMat so that I could subtract from it and then use modulus to wrap the negative values back to the top of the range, but CvMat doesn't seem to support modulus. Of course, I could iterate over every pixel, but I'm concerned that that's going to be very slow.


I've read many tutorials and code samples, but they all seem to conveniently only look at ranges that don't wrap around the hue spectrum, or use solutions that are even uglier (eg. re-implementing cvInRangeS by iterating over every pixel and doing manual comparisons against a color table).

So, what's the usual way to solve this? What's the best way? What are the tradeoffs of each? Is iterating over pixels much slower than using built-in CV functions?

like image 596
Ian Avatar asked Aug 30 '12 19:08

Ian


1 Answers

This is kind of late, but this is what I'd try.

Make the conversion: cvCvtColor(imageBgr, imageHsv, CV_RGB2HSV);

Note, RGB vs Bgr are purposefully being crossed.

This way, red color will be treated in a blue channel and will be centered around 170. There would also be a flip in direction, but that is OK as long as you know to expect it.

like image 85
GaryK Avatar answered Oct 06 '22 02:10

GaryK