Search code examples
c++opencvimage-processinghistogramnon-deterministic

Why do these histogram functions differ, and why is one nondeterministic?


NOTE: This is a homework problem and the professor explicitly forbids soliciting answers from StackOverflow, so please limit your response to the specific question I have asked and do not attempt to provide a working solution.

I am asked to implement a function that computes the histogram of a single-channel 8-bit image represented as an OpenCV Mat with type CV_U8.

In this case, the histogram uses 256 uniformly-distributed buckets. This is the reference we are intended to replicate (using OpenCV 3.4):

Mat reference;

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

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

bool uniform = true;
bool accumulate = false;

cv::calcHist(&bgr_planes[0], 1, 0, Mat(), reference, 1, &histSize, &histRange,
             uniform, accumulate);

// reference now contains the canonical histogram of the input image's
// blue channel

I wrote the following function to calculate the histogram, which produces the correct results 45-69% of the time (p<0.05, n=66). Once when it failed, I examined the results and found no discernable pattern. All trials were conducted on the same test image.

Mat myCalcHist(const Mat& input) {
  assert(input.isContinuous());

  Mat res(256, 1, CV_32F);
  for (const uint8_t* it = input.datastart; it != input.dataend; ++it) {
    ++res.at<float>(*it);
  }

  return res;
}

The following function, on the other hand, more closely matches OpenCV's internal implementation in that it uses the idiomatic accessors and converts the float result from an int work matrix, but in n=66 trials it did not produce the correct result a single time. Again, I found no discernable pattern in the data.

Mat myCalcHist(const Mat& input) {

  Mat ires(256, 1, CV_32S);
  for (int i = 0; i < input.total(); ++i) {
    ++ires.at<int>(input.at<uint8_t>(i));
  }

  Mat res(256, 1, CV_32F);
  ires.convertTo(res, CV_32F);
  return res;
}

Why are the results for my first implementation different than those from my second implementation, and where is nondeterminism introduced to the first implementation?


Solution

  • initializing the histogram matrix should work:

    Mat myCalcHist(const Mat& input) 
    {
        Mat ires = cv::Mat::zeros(256, 1, CV_32S);
        for (int i = 0; i < input.total(); ++i) 
        {
             ++ires.at<int>(input.at<uint8_t>(i));
        }
    
       Mat res(256, 1, CV_32F);
       ires.convertTo(res, CV_32F);
       return res;
    }