Home > database >  What's the difference in results of cv::boundingRect() and cv::minAreaRect()?
What's the difference in results of cv::boundingRect() and cv::minAreaRect()?

Time:11-12

I tried reading the docs but couldn't understand the difference. I was expecting the same results when the target is a 3x3 square, but I get 3x3 with cv::boundingRect() and 2x2 with cv::minAreaRect(). I'm using OpenCV 4.4.

Here is a sample code.

char data[25] = {
    0, 0, 0, 0, 0,
    0, 255, 255, 255, 0,
    0, 255, 255, 255, 0,
    0, 255, 255, 255, 0,
    0, 0, 0, 0, 0
};
cv::Mat image = cv::Mat(5, 5, CV_8U, data);

std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(image, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

cv::Rect boundingRect = cv::boundingRect(contours[0]);
std::cout << "[cv::boundingRect]" << std::endl;
std::cout << "w, h: " << boundingRect.size().width << " x " << boundingRect.size().height << std::endl;

cv::RotatedRect minAreaRect = cv::minAreaRect(contours[0]);
std::cout << "[cv::minAreaRect]" << std::endl;
std::cout << "w, h: " << minAreaRect.size.width << " x " << minAreaRect.size.height << std::endl;

And the output is:

[cv::boundingRect]
w, h: 3 x 3
[cv::minAreaRect]
w, h: 2 x 2

Thanks in advance.

CodePudding user response:

Quite simply, the boundingRect() function returns a rectangle that is upright, or whose bottom edge is parallel to the x-axis. The minAreaRect() function also considers rotations about the contour of the object you pass to it, which allows it to find the smallest possible rectangle relative to total area.

E.g.

boundingRect() is green, minAreaRect() is red

enter image description here

CodePudding user response:

OpenCV has inconsistencies and sloppy/no "definitions", especially in those old parts.

Some of that stems from the reluctance to return fractional values to express that a line (contour) should be between pixels. Some more stems from not being clear on whether the "bottom right" of a rectangle is the last pixel in it, or the first pixel outside of it... or if we're considering the points that are on the extreme corners of those pixels.

In OpenCV, the center of a pixel is an integer coordinate. That much is sure.

In your example, which is clearly a box with sides of length 3, you would expect results to reflect that size.

findContours gives you strictly points that are in the shape... it gives the centers of those border pixels. Those would be (1,1), (1,3), (3,3), (3,1). And that's the first problem.

  • If you assume they're pixels (little squares), you could figure out that the true size is 3 by 3.
  • If they're considered points, the size would have to be 2 by 2.

boundingRect seems to assume the values to be pixels. It tells you the correct size. The top left corner is on the top left pixel, instead of its top left corner... and the bottom right corner of the rectangle isn't on the bottom right corner of the last inside pixel, but on the center of the pixel just outside of it.

minAreaRect seems to assume the values to be points. That explains its result of size 2 by 2.

Depending on what you need the results for, you'll have to correct. Sometimes that means adding/subtracting one from the size. Sometimes that means shifting the result by one half to get points on the corners of pixels.

Additionally, OpenCV's drawing functions are equally sloppily defined. Try drawing a Line of specific thickness, say 1 or 2, with LINE_AA style... it's a mess. A rectangle will probably be drawn such that the line sits on the top left inside pixel, and on the bottom right pixel just outside.

  • Related