1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-21 19:33:03 +04:00

Merge pull request #28051 from dkurt:minAreaRect_angle

Correct minAreaRect angle to be in range [-90, 0) #28051

### Pull Request Readiness Checklist

Box angle range over all imgproc tests is in interval `[-90, -0.0581199]`

resolves https://github.com/opencv/opencv/issues/27667
resolves https://github.com/opencv/opencv/issues/19472
resolves https://github.com/opencv/opencv/issues/24436

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
Dmitry Kurtaev
2025-11-25 20:56:59 +08:00
committed by GitHub
parent 52a5b6c3df
commit 621ad482d6
5 changed files with 55 additions and 19 deletions
@@ -1344,8 +1344,8 @@ public class ImgprocTest extends OpenCVTestCase {
RotatedRect rrect = Imgproc.minAreaRect(points);
assertEquals(new Size(5, 2), rrect.size);
assertEquals(0., rrect.angle);
assertEquals(new Size(2, 5), rrect.size);
assertEquals(-90., rrect.angle);
assertEquals(new Point(3.5, 2), rrect.center);
}
+26 -8
View File
@@ -365,6 +365,7 @@ cv::RotatedRect cv::minAreaRect( InputArray _points )
Mat hull;
Point2f out[3];
RotatedRect box;
box.angle = -(float)CV_PI / 2; // default angle for box without rotation and single point
static const bool clockwise = false;
convexHull(_points, hull, clockwise, true);
@@ -384,19 +385,34 @@ cv::RotatedRect cv::minAreaRect( InputArray _points )
rotatingCalipers( hpoints, n, clockwise ? -1.f : 1.f, CALIPERS_MINAREARECT, (float*)out );
box.center.x = out[0].x + (out[1].x + out[2].x)*0.5f;
box.center.y = out[0].y + (out[1].y + out[2].y)*0.5f;
box.size.width = (float)std::sqrt((double)out[1].x*out[1].x + (double)out[1].y*out[1].y);
box.size.height = (float)std::sqrt((double)out[2].x*out[2].x + (double)out[2].y*out[2].y);
box.angle = (float)atan2( (double)out[1].y, (double)out[1].x );
box.size.width = (float)std::sqrt((double)out[2].x*out[2].x + (double)out[2].y*out[2].y);
box.size.height = (float)std::sqrt((double)out[1].x*out[1].x + (double)out[1].y*out[1].y);
if (out[1].x == 0.f && out[1].y > 0.f)
std::swap(box.size.width, box.size.height);
else
box.angle += (float)atan2( (double)out[1].y, (double)out[1].x );
}
else if( n == 2 )
{
box.center.x = (hpoints[0].x + hpoints[1].x)*0.5f;
box.center.y = (hpoints[0].y + hpoints[1].y)*0.5f;
double dx = hpoints[1].x - hpoints[0].x;
double dy = hpoints[1].y - hpoints[0].y;
box.size.width = (float)std::sqrt(dx*dx + dy*dy);
box.size.height = 0;
box.angle = (float)atan2( dy, dx );
double dx = hpoints[0].x - hpoints[1].x;
double dy = hpoints[0].y - hpoints[1].y;
box.size.width = 0;
box.size.height = (float)std::sqrt(dx*dx + dy*dy);
if (dx == 0)
{
std::swap(box.size.width, box.size.height);
}
else if (dy < 0)
{
box.angle = (float)atan2( dy, dx );
std::swap(box.size.width, box.size.height);
}
else if (dy > 0)
{
box.angle += (float)atan2( dy, dx );
}
}
else
{
@@ -405,6 +421,8 @@ cv::RotatedRect cv::minAreaRect( InputArray _points )
}
box.angle = (float)(box.angle*180/CV_PI);
CV_DbgCheckGE(box.angle, -90.0f, "");
CV_DbgCheckLT(box.angle, 0.0f, "");
return box;
}
+18
View File
@@ -1265,6 +1265,24 @@ TEST(Imgproc_minAreaRect, reproducer_21482_small_values)
EXPECT_EQ(rr.size.height, 1e-4f);
}
typedef testing::TestWithParam<tuple<Point2f, Point2f, Point2f, Size2f, float>> minAreaRect_of_line;
TEST_P(minAreaRect_of_line, accuracy)
{
Point2f p1 = get<0>(GetParam());
Point2f p2 = get<1>(GetParam());
RotatedRect out = minAreaRect(std::vector<Point2f>{p1, p2});
EXPECT_EQ(out.center, get<2>(GetParam()));
EXPECT_EQ(out.size, get<3>(GetParam()));
EXPECT_NEAR(out.angle, get<4>(GetParam()), 1e-6);
}
INSTANTIATE_TEST_CASE_P(Imgproc, minAreaRect_of_line,
testing::Values(
std::make_tuple(Point2f(10, 15), Point2f(10, 25), Point2f(10, 20), Size2f(10, 0), -90.f),
std::make_tuple(Point2f(450, 500), Point2f(508, 500), Point2f(479, 500), Size2f(0, 58), -90.f),
std::make_tuple(Point2f(10, 20), Point2f(13, 16), Point2f(11.5, 18), Size2f(5, 0), -53.1301002f),
std::make_tuple(Point2f(9, 19), Point2f(4, 7), Point2f(6.5, 13), Size2f(0, 13), -22.6198654f)
));
}} // namespace
/* End of file. */
+8 -8
View File
@@ -710,14 +710,14 @@ QUnit.test('test_rotatedRectangleIntersection', function(assert) {
assert.deepEqual(intersectionType, cv.INTERSECT_FULL);
intersectionPoints.convertTo(intersectionPoints, cv.CV_32S);
let intersectionPointsData = intersectionPoints.data32S;
assert.deepEqual(intersectionPointsData[0], 30);
assert.deepEqual(intersectionPointsData[1], 40);
assert.deepEqual(intersectionPointsData[2], 40);
assert.deepEqual(intersectionPointsData[3], 30);
assert.deepEqual(intersectionPointsData[4], 50);
assert.deepEqual(intersectionPointsData[5], 40);
assert.deepEqual(intersectionPointsData[6], 40);
assert.deepEqual(intersectionPointsData[7], 50);
assert.deepEqual(intersectionPointsData[0], 40);
assert.deepEqual(intersectionPointsData[1], 50);
assert.deepEqual(intersectionPointsData[2], 30);
assert.deepEqual(intersectionPointsData[3], 40);
assert.deepEqual(intersectionPointsData[4], 40);
assert.deepEqual(intersectionPointsData[5], 30);
assert.deepEqual(intersectionPointsData[6], 50);
assert.deepEqual(intersectionPointsData[7], 40);
intersectionType = cv.rotatedRectangleIntersection(rr1, rr3, intersectionPoints);
+1 -1
View File
@@ -81,7 +81,7 @@ class Hackathon244Tests(NewOpenCVTests):
mc, mr = cv.minEnclosingCircle(a)
be0 = ((150.2511749267578, 150.77322387695312), (158.024658203125, 197.57696533203125), 37.57804489135742)
br0 = ((161.2974090576172, 154.41793823242188), (207.7177734375, 199.2301483154297), 80.83544921875)
br0 = ((161.2974090576172, 154.41793823242188), (199.2301483154297, 207.7177734375), -9.164555549621582)
mc0, mr0 = (160.41790771484375, 144.55152893066406), 136.713500977
self.check_close_boxes(be, be0, 5, 15)