Well, that's true, I had not read the thread for a long period of time.
Anyway, probably it's too late, but here some code I wrote for color detection (though, the pseodo code I had given was, in my opinion quite clear and easy to be transposed to matlab code).

Anyway, the following code is a matlab function that takes the following arguments:
1) RGB: a MxNx3 matrix of the RGB values
2) mode: this argument should take the values 1,2 or 3, depending on the color you want to detect (red, green or blue).
3) thres: a threshold that is used to detect the desired color in the following way: if the desired color is LARGER than thres * (each of the other two colors), then the current pixel is labelled as 1 (color found).

Code:

function I = findColor(RGB, mode, thres)
% function BIN = findColor(RGB, rgbVal, Thres)
% Function for detecting a specific rgb value, within acceptable tollerance
% RGB: the rgb image
% mode: the color to detect:
% 1: red
% 2: green
% 3: blue
% thres: the distance tollerance
[M,N,t] = size(RGB);
switch (mode)
case 1
I1 = zeros(M,N); I2 = zeros(M,N);
I1( find(RGB(:,:,1) > thres * RGB(:,:,2)) ) = 1;
I2( find(RGB(:,:,1) > thres * RGB(:,:,3)) ) = 1;
strTitle = 'Color RED detected (white areas)';
I = I1 .* I2;
case 2
I1 = zeros(M,N); I2 = zeros(M,N);
I1( find(RGB(:,:,2) > thres * RGB(:,:,1)) ) = 1;
I2( find(RGB(:,:,2) > thres * RGB(:,:,3)) ) = 1;
strTitle = 'Color GREEN detected (white areas)';
I = I1 .* I2;
case 3
I1 = zeros(M,N); I2 = zeros(M,N);
I1( find(RGB(:,:,3) > thres * RGB(:,:,1)) ) = 1;
I2( find(RGB(:,:,3) > thres * RGB(:,:,2)) ) = 1;
strTitle = 'Color BLUE detected (white areas)';
I = I1 .* I2;
otherwise
fprintf('WRONG ARGUMENTS'\n);
return;
end
subplot(2,1,1),imshow(RGB); title('Original Image');
subplot(2,1,2),imshow(I,[]); title(strTitle);

The plotted results are shown in the attached images.

PS1: Other threshold values can be used for more accurate color detection. PS2: The thresholding method presented here is trivial. More sophisticated methods could also be used, that, for example would take into consideration the texture.

Well, in a similar way. I suppose you will have to check the hue scale mainly. For example, if you want to detect "RED" color you will have to search about pixels with values of H close to 0. If you are interested in "pure" red color then force the S (saturation) to be close to 100%. Otherwise, do not use the S component. Finally, you can put restrictions (with a tollerance) in the Value component. Anyway, if you are not interested in the brightness, do not use the V component, and if you are not interested in the "clearness" of the color do not use the S component.

To be more analytic, assume you want to detect red color. You have to decide if you want a specific brightness of the color (that is V) and a specific pureness of your color. To speak in "rgb terms", for example you want to detect RGB={150,0,0} (that has 100% saturation, since it is 100% pure red color). The questions above become:
1. do you also want to detect e.g. RGB={250,0,0} (that is, do you want to have tollerance in the V coefficient of the HSV space?) and
2. do you also want to detect, e.g. RGB = {60,10,10} which corresponds to lower S values?

function colorDetectHSV(RGB, hsvVal, tol)
HSV = rgb2hsv(RGB);
% find the difference between required and real H value:
diffH = abs(HSV(:,:,1) - hsvVal(1));
[M,N,t] = size(RGB);
I1 = zeros(M,N); I2 = zeros(M,N); I3 = zeros(M,N);
T1 = tol(1);
I1( find(diffH < T1) ) = 1;
if (length(tol)>1)
% find the difference between required and real S value:
diffS = abs(HSV(:,:,2) - hsvVal(2));
T2 = tol(2);
I2( find(diffS < T2) ) = 1;
if (length(tol)>2)
% find the difference between required and real V value:
difV = HSV(:,:,3) - hsvVal(3);
T3 = tol(3);
I3( find(diffS < T3) ) = 1;
I = I1.*I2.*I3;
else
I = I1.*I2;
end
else
I = I1;
end
subplot(2,1,1),imshow(RGB); title('Original Image');
subplot(2,1,2),imshow(I,[]); title('Detected Areas');

The above function:
1. Takes as argument an rgb image (MxNx3), the hsv value you want to detect (this can be a vector 1x1 OR 2x1 OR 3x1) and a tolerance array (save size as hsv value)
2. Transposes the image to HSV.
3. Depending on the length L of tollerance (tol):
3.1: L=1: Searches ONLY the H coeficient and finds pixels that are close enough to hsv.
3.2: L=2: Searches H and S coeficients and finds pixels that are close enough to hsv (the 2 values given in the argument).
3.3. L=3: Searches ALL HSV componets.

Here is an example of how to call the above function (again with the trees image):

The above code calls the function for finding the hsv value [0.66 1.0 0.5] with tollerance [0.1 0.5 0.5]. We have to note that H = 0.66 stands for the blue color (tolerance: 0.1), S = 1.0 stands for 100% pure color (tolerance 0.50).

I attach the original and the detected image in the attached image.

do you have any idea which one is better, RGB or HSV if i use it to detect lips or mouth?

The attachments below "image.zip" are images which i have tried with RGB. The moustache seems to be the noise.

Please help, i will appreciate every piece of comment.

Hi. First of all I believe HSV is better for colour detection (I've tried this in the past for "skin" detection and it worked better). Though, in order to test the above function I've written, you need some "training" data, i.e. some prior knowledge. In other words, you may need the average value of your object of interest (in that case lips).
The following function does the following:

Loads an image (which you provide as an RGB argument)

Waits for user clicks (right clicks) and for each click it selects an area (around the selected point) and it keeps the HSV (average) value of that area.

Finally, it calculates the average HSV value and it plots the progress of the average H, S, and V coefficients along mouse clicks.

Code:

function hsvMean = selectPixelsAndGetHSV(RGB, Area)
%
% function hsvMean = selectPixelsAndGetHSV(RGB, Area)
%
% Use this function in order to select multiple points from an image (use
% right click to stop process). The selected points are used to calculate
% the average HSV values.
% ARGUMENTS:
% RGB: the RGB image
% AREA: the area size used to calulate the HSV values of each point
%
%
% Theodoros Giannakopoulos - November 2007
%
imshow(RGB); hold on;
HSV = rgb2hsv(RGB);
numOfSelectedPixels = 0;
right_not_pressed = 1;
BUTTON = 1;
while (BUTTON~=3)
numOfSelectedPixels = numOfSelectedPixels + 1;
[X,Y,BUTTON] = GINPUT(1);
hsvTemp2 = HSV(Y-(Area-1)/2:Y+(Area-1)/2, X-(Area-1)/2:X+(Area-1)/2, :);
hsvTemp = zeros(3,1);
[K,L,M] = size(hsvTemp2);
for (i=1:K)
for (j=1:L)
hsvTemp(1) = hsvTemp(1) + hsvTemp2(i,j,1);
hsvTemp(2) = hsvTemp(2) + hsvTemp2(i,j,2);
hsvTemp(3) = hsvTemp(3) + hsvTemp2(i,j,3);
end
end
hsvTemp = hsvTemp / (K*L);
hsv(numOfSelectedPixels,:) = hsvTemp;
hsvMean = mean(hsv,1);
line([X-(Area-1)/2 X+(Area-1)/2] , [Y-(Area-1)/2 Y-(Area-1)/2]);
line([X+(Area-1)/2 X+(Area-1)/2] , [Y-(Area-1)/2 Y+(Area-1)/2]);
line([X+(Area-1)/2 X-(Area-1)/2] , [Y+(Area-1)/2 Y+(Area-1)/2]);
line([X-(Area-1)/2 X-(Area-1)/2] , [Y+(Area-1)/2 Y-(Area-1)/2]);
fprintf('Cur HSV Values: %.3f %.3f %.3f\n', hsvTemp(1), hsvTemp(2), hsvTemp(3));
fprintf('Mean HSV Values: %.3f %.3f %.3f\n', hsvMean(1), hsvMean(2), hsvMean(3));
end
[N, t] = size(hsv);
for (i=1:N) hsvM1(i) = mean(hsv(1:i,1)); end
for (i=1:N) hsvM2(i) = mean(hsv(1:i,2)); end
for (i=1:N) hsvM3(i) = mean(hsv(1:i,3)); end
hsvMean = mean(hsv);
figure;
subplot(3,1,1); plot(hsvM1); title(sprintf('H-->%.4f',hsvM1(end)));
subplot(3,1,2); plot(hsvM2); title(sprintf('S-->%.4f',hsvM2(end)));
subplot(3,1,3); plot(hsvM3); title(sprintf('V-->%.4f',hsvM3(end)));

I run the above m-file for one of your images (code: hsvMean = selectPixelsAndGetHSV(RGB1, 5). In the following pictures you can see:
a) the initial image and the selected 5x5 areas in blue.
b) how the average H, S and V values were changed along time.

As you can see, the final HSV values are:
HSV = [0.0782 0.5461 0.3454].

Or even better don't stick to particular representation and allow a class of transformations that covers RGB, HSV and more and learn parameters appropriate for you task from training data. You'd need to understand some theory though.

"Programs must be written for people to read, and only incidentally for machines to execute."

Thanks for the code.
Yiannakop: I have tried your codes. Let's say we get HSV = [0.0782 0.5461 0.3454] by running selectPixelsAndGetHSV(RGB, Area). Then only we use the mean to apply to your previous codes on HSV, colorDetectHSV(a, [0.0782 0.5461 0.3454], [0.3 0.5 0.5]). Is this what you mean?
I can get the mouth region in black pixels. Why? By right it should be detected in white pixels as shown by your example "tree".

This attachment "aa.pgm" is the result i get by using your RGB codes. It is the same image as you tried with HSV. I found that the result generated by RGB is better. Why? I have used the method as above.

Can you try to make the HSV automatically as the RGB code, in which we only need to fill in the threshold? This is because if i have a bigger database, does it mean that i need to mark every image to get the average for HSV? Can you help to make the HSV codes more automated in this case?

Anyway, thanks for sharing out the codes. At least i know HSV can do it but more complicated compared to RGB. Thank you very much.

Last edited by tommy_chai; November 23rd, 2007 at 04:14 AM.

Ok I found were the problem was. As you can see, in the last function i sent you I finally keep the mean() (AVERAGE) value of the HSV values. I realized that this is incorrect: suppose that you select a pixel which has much different HSV values. Then, the average value will significally change (and that is much obvious in HSV values, where for example the H value differs alot from color to color). Therefore, I have decided to replace the mean() calls with median(), which is more robust against noise pixels. Now, the code becomes:

Code:

function hsvMean = selectPixelsAndGetHSV(RGB, Area)
%
% function hsvMean = selectPixelsAndGetHSV(RGB, Area)
%
% Use this function in order to select multiple points from an image (use
% right click to stop process). The selected points are used to calculate
% the average HSV values.
% ARGUMENTS:
% RGB: the RGB image
% AREA: the area size used to calulate the HSV values of each point
%
%
% Theodoros Giannakopoulos - November 2007
%
imshow(RGB); hold on;
HSV = rgb2hsv(RGB);
HSV2 = HSV;
numOfSelectedPixels = 0;
right_not_pressed = 1;
BUTTON = 1;
while (BUTTON~=3)
numOfSelectedPixels = numOfSelectedPixels + 1;
[X,Y,BUTTON] = GINPUT(1);
hsvTemp2 = HSV(Y-(Area-1)/2:Y+(Area-1)/2, X-(Area-1)/2:X+(Area-1)/2, :);
HSV2(Y-(Area-1)/2:Y+(Area-1)/2, X-(Area-1)/2:X+(Area-1)/2, :) = 0;
hsvTemp = zeros(3,1);
[K,L,M] = size(hsvTemp2);
for (i=1:K)
for (j=1:L)
hsvTemp(1) = hsvTemp(1) + hsvTemp2(i,j,1);
hsvTemp(2) = hsvTemp(2) + hsvTemp2(i,j,2);
hsvTemp(3) = hsvTemp(3) + hsvTemp2(i,j,3);
end
end
hsvTemp = hsvTemp / (K*L);
hsv(numOfSelectedPixels,:) = hsvTemp;
hsvMean = median(hsv,1);
line([X-(Area-1)/2 X+(Area-1)/2] , [Y-(Area-1)/2 Y-(Area-1)/2]);
line([X+(Area-1)/2 X+(Area-1)/2] , [Y-(Area-1)/2 Y+(Area-1)/2]);
line([X+(Area-1)/2 X-(Area-1)/2] , [Y+(Area-1)/2 Y+(Area-1)/2]);
line([X-(Area-1)/2 X-(Area-1)/2] , [Y+(Area-1)/2 Y-(Area-1)/2]);
%rgbTemp = hsv2rgb(hsvTemp);
%fprintf('Cur RGV Values: %.3f %.3f %.3f\n', rgbTemp(1), rgbTemp(2), rgbTemp(3));
fprintf('Cur HSV Values: %.3f %.3f %.3f\n', hsvTemp(1), hsvTemp(2), hsvTemp(3));
fprintf('Mean HSV Values: %.3f %.3f %.3f\n', hsvMean(1), hsvMean(2), hsvMean(3));
end
[N, t] = size(hsv);
for (i=1:N) hsvM1(i) = median(hsv(1:i,1)); end
for (i=1:N) hsvM2(i) = median(hsv(1:i,2)); end
for (i=1:N) hsvM3(i) = median(hsv(1:i,3)); end
hsvMean = median(hsv);
figure;
subplot(3,1,1); plot(hsvM1); title(sprintf('H-->%.4f',hsvM1(end)));
subplot(3,1,2); plot(hsvM2); title(sprintf('S-->%.4f',hsvM2(end)));
subplot(3,1,3); plot(hsvM3); title(sprintf('V-->%.4f',hsvM3(end)));
figure;
RGB2 = hsv2rgb(HSV2);
imshow(RGB2);

And if I run the above function for one of your images again, I get the following hsv vales:

HSV = [0.0189 0.5501 0.3329];

I also attach the respective figure of the HSV values.

Can you try to make the HSV automatically as the RGB code, in which we only need to fill in the threshold? This is because if i have a bigger database, does it mean that i need to mark every image to get the average for HSV? Can you help to make the HSV codes more automated in this case?

Well I suppose it wouldn't be hard to use the hsv detect function i've written in a matlab script that reads multiple images from a folder and keep the median hsv value from all images. I know this is hard work, but this is generally a serious step in all pattern recognition problems: gathering and labelling training data... so you will have to get through this......

Yiannakop: Thanks for your guidance, really appreciated it. From my opinion,
colorDetectHSV(RGB1, [0.019 0.55 0.33], [0.01 0.02 0.02]);
we still have to fill in manually for HSV value which we can obtain by executing hsvMean = selectPixelsAndGetHSV(RGB, Area) right?
In order to make it automated, i need to make a link to connect the HSV value which is generated to automatically update in colorDetectHSV() right?

Good job my friend. Thanks again.

Best regards,
Tommy
(PS:Honestly, i haven't try your new program to use median() due to my workload but i iwill try it as soon as possible)

Yiannakop: Thanks for your guidance, really appreciated it.

Any time

Originally Posted by tommy_chai

From my opinion,
colorDetectHSV(RGB1, [0.019 0.55 0.33], [0.01 0.02 0.02]);
we still have to fill in manually for HSV value which we can obtain by executing hsvMean = selectPixelsAndGetHSV(RGB, Area) right?

Yes you need to have a "manual" stage involving in your training process and that happens in general. So, yes you will have to execute selectPixelsAndGetHSV(), and to be 100% correct you will have to execute this on MANY images (which, in a way will form the training data set).

Originally Posted by tommy_chai

In order to make it automated, i need to make a link to connect the HSV value which is generated to automatically update in colorDetectHSV() right?