Friday, December 23, 2011

Computing Entropy of an image (CORRECTED)

entropy is a measure of the uncertainty associated with a random variable.
basically i want to get a single value representing the entropy of an image.

1. Assign 255 bins for the range of values between 0-255
2. separate the image into its 3 channels
3. compute histogram for each channel
4. normalize all 3 channels unifirmely
5. for each channel get the bin value (Hc) and use its absolute value (negative log is infinity)
6. compute Hc*log10(Hc)
7. add to entropy and continue with 5 until a single value converges
5. get the frequency of each channel - add all the values of the bin
6. for each bin get a probability -
if bin 1 = 20
bin 2 = 30
then frequency is 50 and probability is 20/50 and 30/50
then compute using shannon formula

 REFERENCE: http://people.revoledu.com/kardi/tutorial/DecisionTree/how-to-measure-impurity.htm




class atsHistogram
{
public:
    cv::Mat DrawHistogram(Mat src)
    {
        /// Separate the image in 3 places ( R, G and B )
         vector<Mat> rgb_planes;
         split( src, rgb_planes );


         /// Establish the number of bins
         int histSize = 255;


         /// Set the ranges ( for R,G,B) )
         float range[] = { 0, 255 } ;
         const float* histRange = { range };


         bool uniform = true; bool accumulate = false;


         Mat r_hist, g_hist, b_hist;


         /// Compute the histograms:
         calcHist( &rgb_planes[0], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate );
         calcHist( &rgb_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate );
         calcHist( &rgb_planes[2], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate );


         // Draw the histograms for R, G and B
         int hist_w = 400; int hist_h = 400;
         int bin_w = cvRound( (double) hist_w/histSize );


         Mat histImage( hist_w, hist_h, CV_8UC3, Scalar( 0,0,0) );


         /// Normalize the result to [ 0, histImage.rows ]
         normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
         normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
         normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );


         /// Draw for each channel
         /*for( int i = 1; i < histSize; i++ )
           {
             line( histImage, Point( bin_w*(i-1), hist_h - cvRound(r_hist.at<float>(i-1)) ) ,
                              Point( bin_w*(i), hist_h - cvRound(r_hist.at<float>(i)) ),
                              Scalar( 0, 0, 255), 2, 8, 0  );
             line( histImage, Point( bin_w*(i-1), hist_h - cvRound(g_hist.at<float>(i-1)) ) ,
                              Point( bin_w*(i), hist_h - cvRound(g_hist.at<float>(i)) ),
                              Scalar( 0, 255, 0), 2, 8, 0  );
             line( histImage, Point( bin_w*(i-1), hist_h - cvRound(b_hist.at<float>(i-1)) ) ,
                              Point( bin_w*(i), hist_h - cvRound(b_hist.at<float>(i)) ),
                              Scalar( 255, 0, 0), 2, 8, 0  );
            }*/
double entropy = (double)computeShannon(r_hist,g_hist,b_hist);
         //printf("Entropy: %f" , entropy );
         cout << entropy << endl;
         return histImage;
    }
    float getHistogramBinValue(Mat hist, int binNum)
    {
        //return cvRound(hist.at<float>(binNum));
        return hist.at<float>(binNum);
    }
    float computeShannon(Mat r, Mat g, Mat b)
    {
        int histSize = 255; //number of bins
        float entropy = 0.0;
        for( int i = 1; i < histSize; i++ )
        {
            float Hc = abs(getHistogramBinValue(r,i));
            //cout << "Red " << Hc << endl;
            entropy += Hc * log10(Hc);
            //cout << "-" << Hc << " -n- " << entropy << endl;
        }
        for( int i = 1; i < histSize; i++ )
        {
            float Hc = abs(getHistogramBinValue(g,i));
            //cout << "Green " << Hc << endl;
            entropy += Hc * log10(Hc);
        }
        for( int i = 1; i < histSize; i++ )
        {
            float Hc = abs(getHistogramBinValue(b,i));
            //cout << "Blue " << Hc << endl;
            entropy += Hc * log10(Hc);
        }
        entropy = entropy * -1;
        //cout << entropy <<endl;
        return entropy;
    }
private:
};

     float getHistogramBinValue(Mat hist, int binNum)
    {
        return hist.at<float>(binNum);
    }
    float getFrequencyOfBin(Mat channel)
    {
        float frequency = 0.0;
        for( int i = 1; i < histSize; i++ )
        {
            float Hc = abs(getHistogramBinValue(channel,i));
            frequency += Hc;
        }
        return frequency;
    }
    float computeShannonEntropy(Mat r, Mat g, Mat b)
    {
        float entropy = 0.0;
        float frequency = getFrequencyOfBin(r);
        for( int i = 1; i < histSize; i++ )
        {
            float Hc = abs(getHistogramBinValue(r,i));
            entropy += -(Hc/frequency) * log10((Hc/frequency));
        }
        frequency = getFrequencyOfBin(g);
        for( int i = 1; i < histSize; i++ )
        {
            float Hc = abs(getHistogramBinValue(g,i));
            entropy += -(Hc/frequency) * log10((Hc/frequency));
        }
        frequency = getFrequencyOfBin(b);
        for( int i = 1; i < histSize; i++ )
        {
            float Hc = abs(getHistogramBinValue(b,i));
            entropy += -(Hc/frequency) * log10((Hc/frequency));
        }
        entropy = entropy;
        //cout << entropy <<endl;
        return entropy;
    }

6 comments:

  1. Can we use this as a way to measure contrast?

    ReplyDelete
  2. when i try run your example code the program show me this error:

    D:\Adrian\Mis Programas\Image_work\Image_Work\image_work.cpp:181: error: no matching function for call to 'calcHist(cv::Mat*&, int, int, cv::Mat, cv::Mat&, int, int*, const float*&, bool&, bool&)'

    can you help me?

    ReplyDelete
    Replies
    1. calcHist() function expects the first argument to be 'Mat*' instead of 'Mat' .

      Try this function. It worked fine for me.

      /****************************************************

      Method to calculate entropy of an image

      ****************************************************/

      double entropy(Mat *img)
      {
      int numBins = 256, nPixels;
      float range[] = {0, 255};
      double imgEntropy = 0 , prob;
      const float* histRange = { range };
      Mat histValues;

      //calculating the histogram
      calcHist(img, 1, 0, Mat(), histValues, 1, &numBins, &histRange, true, true );

      nPixels = sum(histValues)[0];


      for(int i = 1; i < numBins; i++)
      {
      prob = histValues.at(i)/nPixels;
      if(prob < FLT_EPSILON)
      continue;
      imgEntropy += prob*(log(prob)/log(2));

      }

      return (0-imgEntropy);
      }

      /**************************************************************************************/

      Before calling the function, you should split the image into channels using 'split()' function and pass the address of the channel of your choice.

      Hope this helps you.

      Delete
    2. Hello, I tried the code above but somehow I get negative entropy. Am I wrongly implemented the code?

      Delete
  3. sorry, i work with QtCreator. This it's a recommendation that Qt give me.

    note: void cv::calcHist(const cv::Mat*, int, const int*, const cv::_InputArray&, cv::SparseMat&, int, const int*, const float**, bool, bool)

    how i can change the parameters of this function to my program run correctly?

    ReplyDelete
  4. how i can give input image above function...and i gives error to me

    ReplyDelete