Detecting color range from "average" in OpenCV - c++

Currently I am using a HaarCascade to detect a face in a picture. Which is working, I am getting a rect of where the face is.
Now I want to get the "average?" (skin color) in that rect. And use that as a base for the color range to search for other skin in the photo. How should I go about that?
I have found the inRange function, which searches for a color in a range. But I am not quite sure how I could get the average color of my skin in there. It seems that the inRange function needs HSV values? However, I don't know quite what that format is. It doesn't seem to be the same as HSB in photoshop. (Which I tried for "testing purposes").
My question boils down to this, how can I get the "average" color in a rect, and find other colours in that range (e.g, lighter and darker than that color, but the same shade).
Thanks.

Get the 3D color histogram within the detected region of interest. That is, not three 1D histograms for each channel, but one 3D histogram for all 3 channels together. OpenCV's calcHist has options for that. Here's an example which does that. This example is using Python bindings for OpenCV, but it shows how to set the parameters.
Also, set the range of the histogram within a reasonable range for skin color. As MSalters suggested in the comments, HSV is a better color space for things like this. Perhaps you can disregard the S and V channels and only do the 1D histogram for V. Try is out.
The bin with the highest count is going to be your "average" skin color.

Related

Getting Hue value from the peaks in histogram opencv

I am trying to use color information of detection of rectangles. Some of my rectangles are overlapping and with multicolor. I found a solution to detect these rectangles using Hue values. I am checking inRange with Hue values of colors
Orange 0-22
Yellow 22- 38
Green 38-75
Blue 75-130
Violet 130-160
Red 160-179
, but I do not know what exact color is going to be. For example, in one image rectangles can be orange, red, blue and in another image, it can be other colors.
I tried to look histogram, but I would have a background which is not only white or black. So, the histogram is confusing.
If you give me some ideas about how to handle this problem, I will appreciate it.
You can try a brute force approach, where you try all the color ranges, then use findcontours (example) to see if you can find a contour that is possibly a rectangle. If the background is very noisy you can use a minimum size for the contour
(contourArea). You could also check the solidity by dividing the contour area by the area of the minAreaRect, the result for a rectangle (that has good detection) should approach 1.
Whether this could possibly work depends on several factors, and overlapping rectangles will quickly break it.
So if I understand correctly, you have a variety of images, each of which contain multiple rectangles which can be a variety of different colors, and the background of the image is non uniform, and you're trying to segment out the rectangles using a histogram?
Using histograms for image segmentation works best with grey scale images with a uniform background, so that upon seeing the peeks in your histogram you know the primary intensities of the objects you are trying to segment out. This method is not going to translate well to your application because the shapes you are attempting to segment are non uniform in shade, without seeing example images I would probably say this isn't going to work, however you might be able to get away with it if the shade variation of the rectangles is relatively similar... basically if you have rectangles that are 15-30 you might be alright, but if they vary from 20-100 you're going to be out of luck, same goes with variation of the background.
If the background and the rectangles have very clearly defined borders, and the background colors transition VERY smoothly, you may be able to get away with some sort of region growing on the background in order to get a list of all the background pixels and then just set those to black or something in order to allow better analysis of the rectangles in the foreground, but I can only speculate so much with the information you've given in your post

OpenCV Adaptive Thresholding a HSV image

We (my group and I) want to be able to track a hand (well the index fingertip mostly). The hand is basically the same colour as the face in the picture, but as you can see, so is a lot of the noise we get. It works very well with a black "screen" behind the hand.
Now the problem is that Adaptive thresholding is useful only on Grayscale images, and as such would not detect the hand very well.
I've tried googling HSV Adaptive Thresholding but no luck, so I figured stackoverflow had some great ideas.
EDIT: The current HSV -> Binary threshold:
inRange(hsvx, Scalar(0, 50, 0), Scalar(20, 150, 255), bina);
I suggest you use a color histogramming for your tracking. Camshift is doing it for example to good success.
There is camshift sample code in OpenCV.
See http://docs.opencv.org/master/db/df8/tutorial_py_meanshift.html (very brief explanation)
or https://github.com/Itseez/opencv/blob/master/samples/cpp/camshiftdemo.cpp (code sample)
If you want to go with your thresholding, you are already proper about not thresholding the V channel. I would still suggest to do separate adaptive thresholding on H and S.
I would suggest you using a histogram backprojection algorithm.
Back Projection is a way of recording how well the pixels of a given image fit the distribution of pixels in a histogram model. You can specify the histogram model by using manually selected set of hand-pixels.
This algorithm outputs an image where each pixel has the value of likelihood the color of this pixel is a color of the skin (is similar to the skin). You can then specify a likelihood threshold to adjust the performance.
It will let you find the skin-colured areas in the image.
For details see:
http://docs.opencv.org/2.4/doc/tutorials/imgproc/histograms/back_projection/back_projection.html
http://docs.opencv.org/master/dc/df6/tutorial_py_histogram_backprojection.html#gsc.tab=0

OpenCV Histogram to Image Conversion

I've tryout some tutorial of converting Grayscale image to Histogram and thus perform comparision between the histogram. So, I've obtained the value returned from compare function in double datatype. Like this.
My problem here now is, how can I visualize the "non-match/ error" detected between images? Can I like obtained back the coordinates of those non-match pixels and draw a rectangle or circle at that particular coordinate?
Or any suggestion on algorithm I can take?
You can't do that from histogram comparison directly. As stated in the documentation,
Use the function compareHist to get a numerical parameter that express how well two histograms match with each other.
This measure is just a distance value which tells you how similar are the two histograms (or how similar are the two images in terms of color distribution).
However, you can use histogram backprojection to visualize how well each pixel in image A
fits the color distribution (histogram) of an image B. Take a look to that OpenCV example.

How to filter an image with dominant color of neighborhood in OpenCV

I want to have a brush effect filter on a image with OpenCV. In order to accomplish that, program will go through each pixel and give it dominant color of neighborhood within a given radius. By dominant color, I mean not average or median but most frequent color. Is there any OpenCV API to do this?
There's no magic function to do that as far as I know.

How to detect points which are drastically different than their neighbours

I'm doing some image processing, and am trying to keep track of points similar to those circled below, a very dark spot of a couple of pixels diameter, with all neighbouring pixels being bright. I'm sure there are algorithms and methods which are designed for this, but I just don't know what they are. I don't think edge detection would work, as I only want the small spots. I've read a little about morphological operators, could these be a suitable approach?
Thanks
Loop over your each pixel in your image. When you are done considering a pixel, mark it as "used" (change it to some sentinel value, or keep this data in a separate array parallel to the image).
When you come across a dark pixel, perform a flood-fill on it, marking all those pixels as "used", and keep track of how many pixels were filled in. During the flood-fill, make sure that if the pixel you're considering isn't dark, that it's sufficiently bright.
After the flood-fill, you'll know the size of the dark area you filled in, and whether the border of the fill was exclusively bright pixels. Now, continue the original loop, skipping "used" pixels.
How about some kind of median filtering? Sample values from 3*3 grid (or some other suitable size) around the pixel and set the value of pixel to median of those 9 pixels.
Then if most of the neighbours are bright the pixel becomes bright etc.
Edit: After some thinking, I realized that this will not detect the outliers, it will remove them. So this is not the solution original poster was asking.
Are you sure that you don't want to do an edge detection-like approach? It seems like a comparing the current pixel to the average value of the neighborhood pixels would do the trick. (I would evaluate various neighborhood sizes to be sure.)
Personally I like this corner detection algorithms manual.
Also you can workout naive corner detection algorithm by exploiting idea that isolated pixel is such pixel through which intensity changes drastically in every direction. It is just a starting idea to begin from and move on further to better algorithms.
I can think of these methods that might work with some tweaking of parameters:
Adaptive thresholds
Morphological operations
Corner detection
I'm actually going to suggest simple template matching for this, if all your features are of roughly the same size.
Just copy paste the pixels of one (or a few features) to create few templates, and then use Normalized Cross Correlation or any other score that OpenCV provides in its template matching routines to find similar regions. In the result, detect all the maximal peaks of the response (OpenCV has a function for this too), and those are your feature coordinates.
Blur (3x3) a copy of your image then diff your original image. The pixels with the highest values are the ones that are most different from their neighbors. This could be used as an edge detection algorithm but points are like super-edges so set your threshold higher.
what a single off pixel looks like:
(assume surrounding pixels are all 1)
original blurred diff
1,1,1 8/9,8/9,8/9 1/9,1/9,1/9
1,0,1 8/9,8/9,8/9 1/9,8/9,1/9
1,1,1 8/9,8/9,8/9 1/9,1/9,1/9
what an edge looks like:
(assume surrounding pixels are the same as their closest neighbor)
original blurred diff
1,0,0 6/9,3/9,0/9 3/9,3/9,0/9
1,0,0 6/9,3/9,0/9 3/9,3/9,0/9
1,0,0 6/9,3/9,0/9 3/9,3/9,0/9
Its been a few years since i did any image processing. But I would probably start by converting to a binary representation. It doesn't seem like you're overly interested in the grey middle values, just the very dark/very light regions, so get rid of all the grey. At that point, various morphological operations can accentuate the points you're interested in. Opening and Closing are pretty easy to implement, and can yield pretty nice results, leaving you with a field of black everywhere except the points you're interested in.
Have you tried extracting connected components using cvContours? First thresholding the image (using Otsu's method say) and then extracting each contour. Since the spots you wish to track are (from what I see in your image) somewhat isolated from neighborhood they will some up as separate contours. Now if we compute the area of the Bounding Rectangle of each contour and filter out the larger ones we'd be left with only small dots separate from dark neighbors.
As suggested earlier a bit of Morphological tinkering before the contour separation should yield good results.