1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 15:53:03 +04:00

Merge pull request #21677 from chacha21:rectangle_intersection

* better accuracy of _rotatedRectangleIntersection

instead of just migrating to double-precision (which would work), some computations are scaled by a factor that depends on the length of the smallest vectors.
There is a better accuracy even with floats, so this is certainly better for very sensitive cases

* Update intersection.cpp

use L2SQR norm to tune the numeric scale

* Update intersection.cpp

adapt samePointEps with L2 norm

* Update intersection.cpp

move comment

* Update intersection.cpp

fix wrong numericalScalingFactor usage

* added tests

* fixed warnings returned by buildbot

* modifications suggested by reviewer

renaming numericalScaleFctor to normalizationScale
refactor some computations
more "const"

* modifications as suggested by reviewer
This commit is contained in:
Pierre Chatelier
2022-03-16 15:46:11 +01:00
committed by GitHub
parent e0ffd3e8a5
commit ef6f421f89
2 changed files with 130 additions and 26 deletions
+52 -26
View File
@@ -51,15 +51,15 @@ static int _rotatedRectangleIntersection( const RotatedRect& rect1, const Rotate
{
CV_INSTRUMENT_REGION();
// L2 metric
const float samePointEps = std::max(1e-16f, 1e-6f * (float)std::max(rect1.size.area(), rect2.size.area()));
Point2f vec1[4], vec2[4];
Point2f pts1[4], pts2[4];
rect1.points(pts1);
rect2.points(pts2);
// L2 metric
float samePointEps = 1e-6f * (float)std::max(rect1.size.area(), rect2.size.area());
int ret = INTERSECT_FULL;
// Specical case of rect1 == rect2
@@ -99,14 +99,22 @@ static int _rotatedRectangleIntersection( const RotatedRect& rect1, const Rotate
vec2[i].y = pts2[(i+1)%4].y - pts2[i].y;
}
//we adapt the epsilon to the smallest dimension of the rects
for( int i = 0; i < 4; i++ )
{
samePointEps = std::min(samePointEps, std::sqrt(vec1[i].x*vec1[i].x+vec1[i].y*vec1[i].y));
samePointEps = std::min(samePointEps, std::sqrt(vec2[i].x*vec2[i].x+vec2[i].y*vec2[i].y));
}
samePointEps = std::max(1e-16f, samePointEps);
// Line test - test all line combos for intersection
for( int i = 0; i < 4; i++ )
{
for( int j = 0; j < 4; j++ )
{
// Solve for 2x2 Ax=b
float x21 = pts2[j].x - pts1[i].x;
float y21 = pts2[j].y - pts1[i].y;
const float x21 = pts2[j].x - pts1[i].x;
const float y21 = pts2[j].y - pts1[i].y;
float vx1 = vec1[i].x;
float vy1 = vec1[i].y;
@@ -114,10 +122,22 @@ static int _rotatedRectangleIntersection( const RotatedRect& rect1, const Rotate
float vx2 = vec2[j].x;
float vy2 = vec2[j].y;
float det = vx2*vy1 - vx1*vy2;
float normalizationScale = std::min(vx1*vx1+vy1*vy1, vx2*vx2+vy2*vy2);//sum of squares : this is >= 0
//normalizationScale is a square, and we usually limit accuracy around 1e-6, so normalizationScale should be rather limited by ((1e-6)^2)=1e-12
normalizationScale = (normalizationScale < 1e-12f) ? 1.f : 1.f/normalizationScale;
float t1 = (vx2*y21 - vy2*x21) / det;
float t2 = (vx1*y21 - vy1*x21) / det;
vx1 *= normalizationScale;
vy1 *= normalizationScale;
vx2 *= normalizationScale;
vy2 *= normalizationScale;
const float det = vx2*vy1 - vx1*vy2;
if (std::abs(det) < 1e-12)//like normalizationScale, we consider accuracy around 1e-6, i.e. 1e-12 when squared
continue;
const float detInvScaled = normalizationScale/det;
const float t1 = (vx2*y21 - vy2*x21)*detInvScaled;
const float t2 = (vx1*y21 - vy1*x21)*detInvScaled;
// This takes care of parallel lines
if( cvIsInf(t1) || cvIsInf(t2) || cvIsNaN(t1) || cvIsNaN(t2) )
@@ -127,8 +147,8 @@ static int _rotatedRectangleIntersection( const RotatedRect& rect1, const Rotate
if( t1 >= 0.0f && t1 <= 1.0f && t2 >= 0.0f && t2 <= 1.0f )
{
float xi = pts1[i].x + vec1[i].x*t1;
float yi = pts1[i].y + vec1[i].y*t1;
const float xi = pts1[i].x + vec1[i].x*t1;
const float yi = pts1[i].y + vec1[i].y*t1;
intersection.push_back(Point2f(xi,yi));
}
@@ -149,18 +169,20 @@ static int _rotatedRectangleIntersection( const RotatedRect& rect1, const Rotate
int posSign = 0;
int negSign = 0;
float x = pts1[i].x;
float y = pts1[i].y;
const float x = pts1[i].x;
const float y = pts1[i].y;
for( int j = 0; j < 4; j++ )
{
float normalizationScale = vec2[j].x*vec2[j].x+vec2[j].y*vec2[j].y;
normalizationScale = (normalizationScale < 1e-12f) ? 1.f : 1.f/normalizationScale;
// line equation: Ax + By + C = 0
// see which side of the line this point is at
float A = -vec2[j].y;
float B = vec2[j].x;
float C = -(A*pts2[j].x + B*pts2[j].y);
const float A = -vec2[j].y*normalizationScale ;
const float B = vec2[j].x*normalizationScale ;
const float C = -(A*pts2[j].x + B*pts2[j].y);
float s = A*x+ B*y+ C;
const float s = A*x + B*y + C;
if( s >= 0 )
{
@@ -187,18 +209,22 @@ static int _rotatedRectangleIntersection( const RotatedRect& rect1, const Rotate
int posSign = 0;
int negSign = 0;
float x = pts2[i].x;
float y = pts2[i].y;
const float x = pts2[i].x;
const float y = pts2[i].y;
for( int j = 0; j < 4; j++ )
{
// line equation: Ax + By + C = 0
// see which side of the line this point is at
float A = -vec1[j].y;
float B = vec1[j].x;
float C = -(A*pts1[j].x + B*pts1[j].y);
float normalizationScale = vec2[j].x*vec2[j].x+vec2[j].y*vec2[j].y;
normalizationScale = (normalizationScale < 1e-12f) ? 1.f : 1.f/normalizationScale;
if (std::isinf(normalizationScale ))
normalizationScale = 1.f;
const float A = -vec1[j].y*normalizationScale ;
const float B = vec1[j].x*normalizationScale ;
const float C = -(A*pts1[j].x + B*pts1[j].y);
float s = A*x + B*y + C;
const float s = A*x + B*y + C;
if( s >= 0 )
{
@@ -223,7 +249,7 @@ static int _rotatedRectangleIntersection( const RotatedRect& rect1, const Rotate
}
// Get rid of duplicated points
int Nstride = N;
const int Nstride = N;
cv::AutoBuffer<float, 100> distPt(N * N);
cv::AutoBuffer<int> ptDistRemap(N);
for (int i = 0; i < N; ++i)
@@ -233,7 +259,7 @@ static int _rotatedRectangleIntersection( const RotatedRect& rect1, const Rotate
for (int j = i + 1; j < N; )
{
const Point2f pt1 = intersection[j];
float d2 = normL2Sqr<float>(pt1 - pt0);
const float d2 = normL2Sqr<float>(pt1 - pt0);
if(d2 <= samePointEps)
{
if (j < N - 1)
@@ -252,10 +278,10 @@ static int _rotatedRectangleIntersection( const RotatedRect& rect1, const Rotate
float minD = distPt[1];
for (int i = 0; i < N - 1; ++i)
{
float* pDist = distPt.data() + Nstride * ptDistRemap[i];
const float* pDist = distPt.data() + Nstride * ptDistRemap[i];
for (int j = i + 1; j < N; ++j)
{
float d = pDist[ptDistRemap[j]];
const float d = pDist[ptDistRemap[j]];
if (d < minD)
{
minD = d;