Merge pull request #26926 from MaximSmolskiy:fix-getPerspectiveTransform-for-singular-case

Fix getPerspectiveTransform for singular case #26926

### Pull Request Readiness Checklist

Fix #26916 

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:
Maxim Smolskiy 2025-03-02 12:44:39 +03:00 committed by GitHub
parent 87cc1643f4
commit dbd3ef9a6f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 71 additions and 4 deletions

View File

@ -3392,7 +3392,7 @@ cv::Matx23d cv::getRotationMatrix2D_(Point2f center, double angle, double scale)
* vi = ---------------------
* c20*xi + c21*yi + c22
*
* Coefficients are calculated by solving linear system:
* Coefficients are calculated by solving one of 2 linear systems:
* / x0 y0 1 0 0 0 -x0*u0 -y0*u0 \ /c00\ /u0\
* | x1 y1 1 0 0 0 -x1*u1 -y1*u1 | |c01| |u1|
* | x2 y2 1 0 0 0 -x2*u2 -y2*u2 | |c02| |u2|
@ -3404,12 +3404,28 @@ cv::Matx23d cv::getRotationMatrix2D_(Point2f center, double angle, double scale)
*
* where:
* cij - matrix coefficients, c22 = 1
*
* or
*
* / x0 y0 1 0 0 0 -x0*u0 -y0*u0 -u0 \ /c00\ /0\
* | x1 y1 1 0 0 0 -x1*u1 -y1*u1 -u1 | |c01| |0|
* | x2 y2 1 0 0 0 -x2*u2 -y2*u2 -u2 | |c02| |0|
* | x3 y3 1 0 0 0 -x3*u3 -y3*u3 -u3 |.|c10|=|0|,
* | 0 0 0 x0 y0 1 -x0*v0 -y0*v0 -v0 | |c11| |0|
* | 0 0 0 x1 y1 1 -x1*v1 -y1*v1 -v1 | |c12| |0|
* | 0 0 0 x2 y2 1 -x2*v2 -y2*v2 -v2 | |c20| |0|
* \ 0 0 0 x3 y3 1 -x3*v3 -y3*v3 -v3 / |c21| \0/
* \c22/
*
* where:
* cij - matrix coefficients, c00^2 + c01^2 + c02^2 + c10^2 + c11^2 + c12^2 + c20^2 + c21^2 + c22^2 = 1
*/
cv::Mat cv::getPerspectiveTransform(const Point2f src[], const Point2f dst[], int solveMethod)
{
CV_INSTRUMENT_REGION();
Mat M(3, 3, CV_64F), X(8, 1, CV_64F, M.ptr());
// try c22 = 1
Mat M(3, 3, CV_64F), X8(8, 1, CV_64F, M.ptr());
double a[8][8], b[8];
Mat A(8, 8, CV_64F, a), B(8, 1, CV_64F, b);
@ -3428,8 +3444,24 @@ cv::Mat cv::getPerspectiveTransform(const Point2f src[], const Point2f dst[], in
b[i+4] = dst[i].y;
}
solve(A, B, X, solveMethod);
M.ptr<double>()[8] = 1.;
if (solve(A, B, X8, solveMethod) && norm(A * X8, B) < 1e-8)
{
M.ptr<double>()[8] = 1.;
return M;
}
// c00^2 + c01^2 + c02^2 + c10^2 + c11^2 + c12^2 + c20^2 + c21^2 + c22^2 = 1
hconcat(A, -B, A);
Mat AtA;
mulTransposed(A, AtA, true);
Mat D, U;
SVDecomp(AtA, D, U, noArray());
Mat X9(9, 1, CV_64F, M.ptr());
U.col(8).copyTo(X9);
return M;
}

View File

@ -1765,5 +1765,40 @@ TEST(Imgproc_Remap, issue_23562)
}
}
TEST(Imgproc_getPerspectiveTransform, issue_26916)
{
double src_data[] = {320, 512, 960, 512, 0, 1024, 1280, 1024};
const Mat src_points(4, 2, CV_64FC1, src_data);
double dst_data[] = {0, 0, 1280, 0, 0, 1024, 1280, 1024};
const Mat dst_points(4, 2, CV_64FC1, dst_data);
Mat src_points_f;
src_points.convertTo(src_points_f, CV_32FC1);
Mat dst_points_f;
dst_points.convertTo(dst_points_f, CV_32FC1);
Mat perspective_transform = getPerspectiveTransform(src_points_f, dst_points_f);
EXPECT_NEAR(perspective_transform.at<double>(2, 2), 0, 1e-16);
EXPECT_NEAR(cv::norm(perspective_transform), 1, 1e-14);
const Mat ones = Mat::ones(4, 1, CV_64FC1);
Mat homogeneous_src_points;
hconcat(src_points, ones, homogeneous_src_points);
Mat obtained_homogeneous_dst_points = (perspective_transform * homogeneous_src_points.t()).t();
for (int row = 0; row < 4; ++row)
{
obtained_homogeneous_dst_points.row(row) /= obtained_homogeneous_dst_points.at<double>(row, 2);
}
Mat expected_homogeneous_dst_points;
hconcat(dst_points, ones, expected_homogeneous_dst_points);
EXPECT_MAT_NEAR(obtained_homogeneous_dst_points, expected_homogeneous_dst_points, 1e-10);
}
}} // namespace
/* End of file. */