Question

I'm trying to do something similar to this post: Getting dominant colour value from HSV Histogram

I have an image and want to extract the dominant color (Hue) from it. I've gotten to the point where I have calculated the histogram, and gotten the maxValue from minMaxLoc. But the numbers I'm retrieving from Core.MinMaxLocResult make absolutely no sense. I'm getting things like 806924 and 1067036; shouldn't the expected values of Hue be between 0 and 180?

Is the histogram supposed to be normalized? How so? I've seen methods like "equalizeHist" and "normalize", but don't really know how to use these and how they'll help me.

Also, once I've gotten a sensible "most occuring" Hue number, how do I translate that to an actual color shade (like "Green is the most occuring color in this image")? Is there a standard Hue range? Like 0-10 is red, 10-20 is purple, etc?

UPDATE: HERE IS MY CODE:

    private void processImage(String imgFilename) {
            channels = new MatOfInt[] { new MatOfInt(0), new MatOfInt(1),
            new MatOfInt(2) };
    histSize = new MatOfInt(histSizeNum);
    hRanges = new MatOfFloat(0f, 180f); // hue varies from 0 to 179
    // use openCV stuff to convert from RGB to HLS space
    Mat src = Highgui.imread(imgFilename);
    Mat hls = new Mat();// destination color space
    Imgproc.cvtColor(src, hls, Imgproc.COLOR_RGB2HLS);
    Core.split(hls, hlsChannels);
    Mat hue = hlsChannels.get(0);
    Mat lum = hlsChannels.get(1);
    Mat sat = hlsChannels.get(2);
    // we compute the histogram from the 0-th channel for hue
    Imgproc.calcHist(Arrays.asList(hls), channels[0], new Mat(), hist,
            histSize, hRanges);

     Core.normalize(hist, hist, 0,2, Core.NORM_MINMAX, -1, new Mat());

    Core.MinMaxLocResult result = Core.minMaxLoc(hist);
    // max value should contain the most-recurring Hue value.
    double mostOccurringHue = result.maxVal;
    double leastOccurringHue = result.minVal;
    //double mostOccurringHue = result.maxLoc.x;
    //double leastOccurringHue = result.minLoc.x;

    // print out for sanity checking
    System.out.println("MAX HUE = " + Double.toString(mostOccurringHue) +"\n");
    System.out.println("MIN HUE = " + Double.toString(leastOccurringHue) +"\n");
Was it helpful?

Solution

Instead of using the openCV's ibuilt method of calculating the histogram and its normalization, i have written my own code for it as we are creating histogram only for hue channel. Have a look at my code.

int main()
{

    Mat input = imread("jan31/class4Jan31.jpg",1);
    Mat hsv_input;
    int h_bins = 5;
    Mat hist_input = Mat::zeros( 1, h_bins, CV_32FC1);
    int h_range = 179;
    int totalNumberPixels = 0;

    cvtColor(input, hsv_input, CV_RGB2HSV);
    Mat hsv_channels[3];

    split( hsv_input, hsv_channels );

    for (int i=0; i<hsv_channels[0].rows; i++)
    {
        for (int j=0; j<hsv_channels[0].cols; j++)
        {               
            if( (int)hsv_channels[1].at<uchar>(i,j)>10 &&    (int)hsv_channels[1].at<uchar>(i,j)>100)
            {               
                totalNumberPixels++;
                int pixel_value = (int)hsv_channels[0].at<uchar>(i,j);
                int corresponding_bin =  ( pixel_value * h_bins ) / h_range;                
                hist_input.at<float>( 0, corresponding_bin ) = ( hist_input.at<float>( 0, corresponding_bin ) + 1 );                
            }                                                                                       
        }
    }

    cout<<"\n total pixels: "<<totalNumberPixels;

    for(int i=0; i<hist_input.rows; i++)
    {
        for (int j=0; j<hist_input.cols; j++)
        {
            float pixel = hist_input.at<float>(i,j);
            hist_input.at<float>(i,j) = pixel / totalNumberPixels;

            pixel = hist_input.at<float>(i,j);
            cout<<"\n Pixel: "<<pixel;
        }
    }

    cv::waitKey(0);
    return 0;
}

OTHER TIPS

I would suggest you check the answer again which was posted on the link you have mentioned. You should think about following point:

-You are looking for dominant color value so you should work with only Hue channel.

-Your hranges should be 0-180.

-dims should be 1 (not 2), because you only need the value histogram.

==> After that maxValLoc will tell you the bin which has maximum value in "Point" format where point will consist of x and y coordinates.

For example:

double minVal; double maxVal; Point minLoc; Point maxLoc;
minMaxLoc( hist_image, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );

Now your "maxVal" will contain the maximum value and "maxLoc.y" & "maxLoc.x" will tell you the row and col value of the point which contain that maximum value.

It would be good if you post some part of you code. You can normalize the histogram by:

normalize( hist_input, hist_input, 0, 2, NORM_MINMAX, -1, Mat() );  

To predict get the range of Green, it would be good if you check yourself the pixel values in H-component of a green image. For me, it is was lying 53-65.

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