OpenCV  3.4.1
Open Source Computer Vision
Histogram Calculation

Goal

In this tutorial you will learn how to:

Note
In the last tutorial (Histogram Equalization) we talked about a particular kind of histogram called Image histogram. Now we will considerate it in its more general concept. Read on!

What are histograms?

What OpenCV offers you

For simple purposes, OpenCV implements the function cv::calcHist , which calculates the histogram of a set of arrays (usually images or image planes). It can operate with up to 32 dimensions. We will see it in the code below!

Code

Explanation

  1. Create the necessary matrices:
    Mat src, dst;
  2. Load the source image
    src = imread( argv[1], 1 );
    if( !src.data )
    { return -1; }
  3. Separate the source image in its three R,G and B planes. For this we use the OpenCV function cv::split :
    vector<Mat> bgr_planes;
    split( src, bgr_planes );
    our input is the image to be divided (this case with three channels) and the output is a vector of Mat )
  4. Now we are ready to start configuring the histograms for each plane. Since we are working with the B, G and R planes, we know that our values will range in the interval \([0,255]\)
    1. Establish number of bins (5, 10...):
      int histSize = 256; //from 0 to 255
    2. Set the range of values (as we said, between 0 and 255 )
      float range[] = { 0, 256 } ; //the upper boundary is exclusive
      const float* histRange = { range };
    3. We want our bins to have the same size (uniform) and to clear the histograms in the beginning, so:
      bool uniform = true; bool accumulate = false;
    4. Finally, we create the Mat objects to save our histograms. Creating 3 (one for each plane):
      Mat b_hist, g_hist, r_hist;
    5. We proceed to calculate the histograms by using the OpenCV function cv::calcHist :
      calcHist( &bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate );
      calcHist( &bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate );
      calcHist( &bgr_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate );
      where the arguments are:
      • **&bgr_planes[0]:** The source array(s)
      • 1: The number of source arrays (in this case we are using 1. We can enter here also a list of arrays )
      • 0: The channel (dim) to be measured. In this case it is just the intensity (each array is single-channel) so we just write 0.
      • Mat(): A mask to be used on the source array ( zeros indicating pixels to be ignored ). If not defined it is not used
      • b_hist: The Mat object where the histogram will be stored
      • 1: The histogram dimensionality.
      • histSize: The number of bins per each used dimension
      • histRange: The range of values to be measured per each dimension
      • uniform and accumulate: The bin sizes are the same and the histogram is cleared at the beginning.
  5. Create an image to display the histograms:
    // Draw the histograms for R, G and B
    int hist_w = 512; int hist_h = 400;
    int bin_w = cvRound( (double) hist_w/histSize );
    Mat histImage( hist_h, hist_w, CV_8UC3, Scalar( 0,0,0) );
  6. Notice that before drawing, we first cv::normalize the histogram so its values fall in the range indicated by the parameters entered:
    normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
    normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
    normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
    this function receives these arguments:
    • b_hist: Input array
    • b_hist: Output normalized array (can be the same)
    • 0 and**histImage.rows: For this example, they are the lower and upper limits to normalize the values ofr_hist**
    • NORM_MINMAX: Argument that indicates the type of normalization (as described above, it adjusts the values between the two limits set before)
    • **-1:** Implies that the output normalized array will be the same type as the input
    • Mat(): Optional mask
  7. Finally, observe that to access the bin (in this case in this 1D-Histogram):
    for( int i = 1; i < histSize; i++ )
    {
    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 );
    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(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 );
    }
    we use the expression:
    b_hist.at<float>(i)
    where \(i\) indicates the dimension. If it were a 2D-histogram we would use something like:
    b_hist.at<float>( i, j )
  8. Finally we display our histograms and wait for the user to exit:
    namedWindow("calcHist Demo", WINDOW_AUTOSIZE );
    imshow("calcHist Demo", histImage );
    return 0;

Result

  1. Using as input argument an image like the shown below:

    Histogram_Calculation_Original_Image.jpg
  2. Produces the following histogram:

    Histogram_Calculation_Result.jpg