How to find the average real-time OpenCV loop intensity

I have an image ranging in size from 50 to 100 small outlines. I want to find the average intensity [1] of each of these circuits in real time [2]. Some of the ways I could think of were

  • Draw a path with the FILLED parameter for each path; use each image as a mask on top of the original image, so find the average value. But I believe that this method will not be real-time at first glance.

  • Examine the OpenCV implementation of the drawContour function with the FILLED option and in the same way access the pixels enclosed by the outline. But the code seems very complicated and incomprehensible.

  • Calculate the minimum rectangle of the area, find all the points inside the rectangle using a transform, and find the average of the points other than zero. Again, it seems like a complicated approach.

Is there an easier and more efficient way to do this?

[1] The average of all pixel intensities enclosed by each of the non-overlapping paths

[2] About 25, (960 x 480) pixels per second on a 2.66 GHz desktop PC

+7
c ++ opencv average contour real-time
source share
1 answer

I could not come up with any methods that are significantly different from the approaches you proposed. However, I was able to make some timings that can help you make a decision. All my timings were performed on a 1280 * 720 image on an iMac, limited to finding 100 loops. Of course, the timings will be different on your machine, but relative timings should be informative.

For each test case, the following are declared:

 std::vector<std::vector<cv::Point>> cont; // Filled by cv::findContours() cv::Mat labels = cv::Mat::zeros(image.size(), CV_8UC1); std::vector<float> cont_avgs(cont.size(), 0.f); // This contains the averages of each contour 

Method 1: 19.0 ms

Method 1 is conceptually the simplest, but it is also the slowest. Each path is indicated by assigning a unique color to each path. The values โ€‹โ€‹of each labeled component are summed by repeating each pixel in the image.

 for (size_t i = 0; i < cont.size(); ++i) { // Labels starts at 1 because 0 means no contour cv::drawContours(labels, cont, i, cv::Scalar(i+1), CV_FILLED); } std::vector<float> counts(cont.size(), 0.f); const int width = image.rows; for (size_t i = 0; i < image.rows; ++i) { for (size_t j = 0; j < image.cols; ++j) { uchar label = labels.data[i*width + j]; if (label == 0) { continue; // No contour } else { label -= 1; // Make labels zero-indexed } uchar value = image.data[i*width + j]; cont_avgs[label] += value; ++counts[label]; } } for (size_t i = 0; i < cont_avgs.size(); ++i) { cont_avgs[i] /= counts[i]; } 

Method 3: 15.7 ms

Unmodified method 3 has the simplest implementation and is also the fastest. All contours are filled in for use as a mask to find the average value. The bounding box of each path is calculated, and then the average value is calculated using the mask in the bounding box.

Warning: This method will give incorrect results if any other contours are within the bounding box of the contour of interest.

 cv::drawContours(labels, cont, -1, cv::Scalar(255), CV_FILLED); for (size_t i = 0; i < cont.size(); ++i) { cv::Rect roi = cv::boundingRect(cont[i]); cv::Scalar mean = cv::mean(image(roi), labels(roi)); cont_avgs[i] = mean[0]; } 

Modified Method 3: 17.8 ms

Making minor changes to Method 3 slightly increases the execution time, but it benefits from providing the correct results regardless of the position of the contour. Each path is individually labeled, and the average is calculated using only the mask for that path.

 for (size_t i = 0; i < cont.size(); ++i) { cv::drawContours(labels, cont, i, cv::Scalar(i), CV_FILLED); cv::Rect roi = cv::boundingRect(cont[i]); cv::Scalar mean = cv::mean(image(roi), labels(roi) == i); cont_avgs[i] = mean[0]; } 
+10
source share

All Articles