Extracting the Percentage of color (Red,blue,green,yellow,orange) in an image in Opencv?

StackOverflow https://stackoverflow.com/questions/21462502

  •  05-10-2022
  •  | 
  •  

Question

I have to differentiate between 5 types of images which could have mostly either red,green, blue, orange or yellow color with some white or black color. I have to find which color is prominent in the image.

The source of images is Webcam, so the actual color also depends upon the illumination and distance of the image from webcam. An example of my image can be seen here:

http://ian-albert.com/hazmat_placards/placard-2-flammable-gas.png

I am trying to calculate the percentage based upon "Hue" values. I am specifying some range for each color. My ranges are:

Red: 0-10
Green: 50-65

Yellow: 18-21

Blue: 100-115

PROBLEM: Even though the image displayed is not Red still i am getting high % for red color.

My code is following:

int findRect::checkByHSV(int svmResult, Mat detectedSquare)
{

    Mat hsv_img;
    cvtColor(detectedSquare,hsv_img,CV_BGR2HSV);

    Vec3b pixel;
    float totalPixel=0; // to count the total number of pixels in an image---to get the Percentage later
    float totalClass[6];// because we want to test for 5 classes+ a garbage class.{{ Class-0 -> Garbage, Class-1->Orange, Class-2->Green, Class-3->Red, 
                        //  Class-4->Blue, Class-5->Yellow }} 

    for(int i=0; i<hsv_img.rows; i++)
    {
        for (int j=0; j<hsv_img.cols; j++)
        {

            totalPixel++;
            pixel= hsv_img.at<Vec3b>(i,j);

            if(  pixel[0]>0 &&  pixel[0]<1  )           totalClass[1]++;    // Class-1->Orange
            else if (  pixel[0]>50 &&  pixel[0]<65  )   totalClass[2]++;    // To check Green class-2 //svmResult==2 && 
            else if (  pixel[0]>0 &&  pixel[0]<10  )        totalClass[3]++;    // Class-3->Red
            else if (  pixel[0]>100 &&  pixel[0]<115  )     totalClass[4]++;    // Class-4->Blue
            else if (  pixel[0]>18 &&  pixel[0]<21  )       totalClass[5]++;    // Class-5->Yellow
        }

    }

    float percentage[5];
    totalClass[0]=0; //Putting zero to the Garbage class

    for (int i=0; i<=5; i++)
    {
        percentage[i] = (totalClass[i] / totalPixel )*100;
    }

    cout<<"\n Organge: "<<percentage[1]<<"  Green: "<<percentage[2]<<"  Red: "<<percentage[3]<<"  Blue: "<<percentage[4]<<"  Yellow: "<<percentage[5]<<"\n \n";

    return svmResult;
}
Was it helpful?

Solution

You mentioned that you can have some White and/or some Black.

HSV allows you to have any value for the H component and:

  1. A High V, Low S gives White
  2. A Low V gives black

In other words: White and Black can give you Red OR Green OR Orange OR ... etc if you only judge by the H component.

Personally I'd say stick to RGB (or BGR whatever), or compensate for the fact the S and V can affect the colour.

OTHER TIPS

You can use the function countNonZero and divide by the total number of pixels of image. A example of use:

vector<Mat> channels;
split(hsv_img,channels);

Mat red, blue, green;
inRange(channels[0], Scalar(0), Scalar(10), red); // red
// ... do the same for blue, green, etc only changing the Scalar values

double image_size = hsv_img.cols*hsv_img.rows;
double red_percent = ((double) cv::countNonZero(red))/image_size;

But it could be not optimal depending of the application (for example, if you need to scan lots of image). Anyway, you can use it to compare the values.

The problem is that black or white have Hue=0 (which is red). But in this case the saturation is very low. What you can do is multiply each pixel by its saturation somewhere.

For example:

  • Using thresholds and AND operations, create a binary image with the "red" pixels in white and all non-red pixels in black.
  • Multiply this image by the saturation. Now you will have a grayscale image that says how red each pixel is.
  • You can threshold this second image if you need a binary image

Pure black is counted as red (HUE=0), but you will have the same problem with bluish black (a very dark blue). So the previous procedure should be repeated for all colors you want to detect.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top