From 5cc154147f749c0d9ac7a32e4b12aa7469b817c3 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 19 Apr 2022 21:07:34 +0300 Subject: [PATCH] Merge pull request #21841 from victor1234:calib3d-undistortPoints-tests Add distort/undistort test for fisheye::undistortPoints() * Add distort/undistort test for fisheye::undistortPoints() Lack of test has allowed error described in 19138 to be unnoticed. In addition to random points, four corners and principal center added to point set * Add random distortion coefficients set * Move undistortPoints test to google test, refactor * Add fisheye::undistortPoints() perf test * Add negative distortion coefficients to undistortPoints test, increase value * Move to theRNG() * Change test check from cvtest::norm(L2) to EXPECT_MAT_NEAR() * Layout fix * Add points number parameters, comments --- modules/calib3d/perf/perf_undistort.cpp | 36 +++++++++ modules/calib3d/test/test_fisheye.cpp | 49 ++++++++++++ .../calib3d/test/test_undistort_points.cpp | 80 +++++++++---------- 3 files changed, 124 insertions(+), 41 deletions(-) diff --git a/modules/calib3d/perf/perf_undistort.cpp b/modules/calib3d/perf/perf_undistort.cpp index e15d2aefe3..86f622a92f 100644 --- a/modules/calib3d/perf/perf_undistort.cpp +++ b/modules/calib3d/perf/perf_undistort.cpp @@ -27,4 +27,40 @@ PERF_TEST(Undistort, DISABLED_InitInverseRectificationMap) SANITY_CHECK_NOTHING(); } +using PerfIntType = perf::TestBaseWithParam>; +PERF_TEST_P(PerfIntType, fisheye_undistortPoints, + (testing::Values(1e2, 1e3, 1e4))) +{ + const cv::Size imageSize(1280, 800); + + /* Set camera matrix */ + const cv::Matx33d K(558.478087865323, 0, 620.458515360843, + 0, 560.506767351568, 381.939424848348, + 0, 0, 1); + + /* Set distortion coefficients */ + Mat D(1, 4, CV_64F); + theRNG().fill(D, RNG::UNIFORM, -1.e-5, 1.e-5); + + int pointsNumber = std::get<0>(GetParam()); + + /* Create two-channel points matrix */ + cv::Mat xy[2] = {}; + xy[0].create(pointsNumber, 1, CV_64F); + theRNG().fill(xy[0], cv::RNG::UNIFORM, 0, imageSize.width); // x + xy[1].create(pointsNumber, 1, CV_64F); + theRNG().fill(xy[1], cv::RNG::UNIFORM, 0, imageSize.height); // y + + cv::Mat points; + merge(xy, 2, points); + + /* Set fixed iteration number to check only c++ code, not algo convergence */ + TermCriteria termCriteria(TermCriteria::MAX_ITER, 10, 0); + + Mat undistortedPoints; + TEST_CYCLE() fisheye::undistortPoints(points, undistortedPoints, K, D, noArray(), noArray(), termCriteria); + + SANITY_CHECK_NOTHING(); +} + } // namespace diff --git a/modules/calib3d/test/test_fisheye.cpp b/modules/calib3d/test/test_fisheye.cpp index 310804d233..ad3b066a6a 100644 --- a/modules/calib3d/test/test_fisheye.cpp +++ b/modules/calib3d/test/test_fisheye.cpp @@ -101,6 +101,55 @@ TEST_F(fisheyeTest, projectPoints) EXPECT_MAT_NEAR(distorted0, distorted2, 1e-10); } +TEST_F(fisheyeTest, distortUndistortPoints) +{ + int width = imageSize.width; + int height = imageSize.height; + + /* Create test points */ + std::vector points0Vector; + cv::Mat principalPoints = (cv::Mat_(5, 2) << K(0, 2), K(1, 2), // (cx, cy) + /* Image corners */ + 0, 0, + 0, height, + width, 0, + width, height + ); + + /* Random points inside image */ + cv::Mat xy[2] = {}; + xy[0].create(100, 1, CV_64F); + theRNG().fill(xy[0], cv::RNG::UNIFORM, 0, width); // x + xy[1].create(100, 1, CV_64F); + theRNG().fill(xy[1], cv::RNG::UNIFORM, 0, height); // y + + cv::Mat randomPoints; + merge(xy, 2, randomPoints); + + cv::Mat points0; + cv::vconcat(principalPoints.reshape(2), randomPoints, points0); + + /* Test with random D set */ + for (size_t i = 0; i < 10; ++i) { + cv::Mat D(1, 4, CV_64F); + theRNG().fill(D, cv::RNG::UNIFORM, -0.00001, 0.00001); + + /* Distort -> Undistort */ + cv::Mat distortedPoints; + cv::fisheye::distortPoints(points0, distortedPoints, K, D); + cv::Mat undistortedPoints; + cv::fisheye::undistortPoints(distortedPoints, undistortedPoints, K, D); + + EXPECT_MAT_NEAR(points0, undistortedPoints, 1e-8); + + /* Undistort -> Distort */ + cv::fisheye::undistortPoints(points0, undistortedPoints, K, D); + cv::fisheye::distortPoints(undistortedPoints, distortedPoints, K, D); + + EXPECT_MAT_NEAR(points0, distortedPoints, 1e-8); + } +} + TEST_F(fisheyeTest, undistortImage) { cv::Matx33d theK = this->K; diff --git a/modules/calib3d/test/test_undistort_points.cpp b/modules/calib3d/test/test_undistort_points.cpp index 8765e2c5eb..1ac18dedba 100644 --- a/modules/calib3d/test/test_undistort_points.cpp +++ b/modules/calib3d/test/test_undistort_points.cpp @@ -1,34 +1,24 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. + +#include // EXPECT_MAT_NEAR #include "test_precomp.hpp" namespace opencv_test { namespace { -class CV_UndistortTest : public cvtest::BaseTest +class UndistortPointsTest : public ::testing::Test { -public: - CV_UndistortTest(); - ~CV_UndistortTest(); protected: - void run(int); -private: void generate3DPointCloud(vector& points, Point3f pmin = Point3f(-1, -1, 5), Point3f pmax = Point3f(1, 1, 10)); void generateCameraMatrix(Mat& cameraMatrix); void generateDistCoeffs(Mat& distCoeffs, int count); - double thresh; - RNG rng; + double thresh = 1.0e-2; }; -CV_UndistortTest::CV_UndistortTest() -{ - thresh = 1.0e-2; -} -CV_UndistortTest::~CV_UndistortTest() {} - -void CV_UndistortTest::generate3DPointCloud(vector& points, Point3f pmin, Point3f pmax) +void UndistortPointsTest::generate3DPointCloud(vector& points, Point3f pmin, Point3f pmax) { RNG rng_Point = cv::theRNG(); // fix the seed to use "fixed" input 3D points for (size_t i = 0; i < points.size(); i++) @@ -39,31 +29,35 @@ void CV_UndistortTest::generate3DPointCloud(vector& points, Point3f pmi points[i] = Point3f(_x, _y, _z); } } -void CV_UndistortTest::generateCameraMatrix(Mat& cameraMatrix) + +void UndistortPointsTest::generateCameraMatrix(Mat& cameraMatrix) { const double fcMinVal = 1e-3; const double fcMaxVal = 100; cameraMatrix.create(3, 3, CV_64FC1); cameraMatrix.setTo(Scalar(0)); - cameraMatrix.at(0,0) = rng.uniform(fcMinVal, fcMaxVal); - cameraMatrix.at(1,1) = rng.uniform(fcMinVal, fcMaxVal); - cameraMatrix.at(0,2) = rng.uniform(fcMinVal, fcMaxVal); - cameraMatrix.at(1,2) = rng.uniform(fcMinVal, fcMaxVal); + cameraMatrix.at(0,0) = theRNG().uniform(fcMinVal, fcMaxVal); + cameraMatrix.at(1,1) = theRNG().uniform(fcMinVal, fcMaxVal); + cameraMatrix.at(0,2) = theRNG().uniform(fcMinVal, fcMaxVal); + cameraMatrix.at(1,2) = theRNG().uniform(fcMinVal, fcMaxVal); cameraMatrix.at(2,2) = 1; } -void CV_UndistortTest::generateDistCoeffs(Mat& distCoeffs, int count) + +void UndistortPointsTest::generateDistCoeffs(Mat& distCoeffs, int count) { distCoeffs = Mat::zeros(count, 1, CV_64FC1); for (int i = 0; i < count; i++) - distCoeffs.at(i,0) = rng.uniform(0.0, 1.0e-3); + distCoeffs.at(i,0) = theRNG().uniform(-0.1, 0.1); } -void CV_UndistortTest::run(int /* start_from */) +TEST_F(UndistortPointsTest, accuracy) { Mat intrinsics, distCoeffs; generateCameraMatrix(intrinsics); + vector points(500); generate3DPointCloud(points); + vector projectedPoints; projectedPoints.resize(points.size()); @@ -71,10 +65,15 @@ void CV_UndistortTest::run(int /* start_from */) for (int idx = 0; idx < 3; idx++) { generateDistCoeffs(distCoeffs, modelMembersCount[idx]); - projectPoints(Mat(points), Mat::zeros(3,1,CV_64FC1), Mat::zeros(3,1,CV_64FC1), intrinsics, distCoeffs, projectedPoints); + + projectPoints(Mat(points), Mat::zeros(3,1,CV_64FC1), + Mat::zeros(3,1,CV_64FC1), intrinsics, + distCoeffs, projectedPoints); vector realUndistortedPoints; - projectPoints(Mat(points), Mat::zeros(3,1,CV_64FC1), Mat::zeros(3,1,CV_64FC1), intrinsics, Mat::zeros(4,1,CV_64FC1), realUndistortedPoints); + projectPoints(Mat(points), Mat::zeros(3,1,CV_64FC1), + Mat::zeros(3,1,CV_64FC1), intrinsics, + Mat::zeros(4,1,CV_64FC1), realUndistortedPoints); Mat undistortedPoints; undistortPoints(Mat(projectedPoints), undistortedPoints, intrinsics, distCoeffs); @@ -82,44 +81,43 @@ void CV_UndistortTest::run(int /* start_from */) Mat p; perspectiveTransform(undistortedPoints, p, intrinsics); undistortedPoints = p; - double diff = cvtest::norm(Mat(realUndistortedPoints), undistortedPoints, NORM_L2); - if (diff > thresh) - { - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - return; - } - ts->set_failed_test_info(cvtest::TS::OK); + + EXPECT_MAT_NEAR(realUndistortedPoints, undistortedPoints.t(), thresh); } } -TEST(Calib3d_Undistort, accuracy) { CV_UndistortTest test; test.safe_run(); } - -TEST(Calib3d_Undistort, stop_criteria) +TEST_F(UndistortPointsTest, stop_criteria) { Mat cameraMatrix = (Mat_(3,3,CV_64F) << 857.48296979, 0, 968.06224829, 0, 876.71824265, 556.37145899, 0, 0, 1); Mat distCoeffs = (Mat_(5,1,CV_64F) << -2.57614020e-01, 8.77086999e-02, -2.56970803e-04, -5.93390389e-04, -1.52194091e-02); - RNG rng(2); - Point2d pt_distorted(rng.uniform(0.0, 1920.0), rng.uniform(0.0, 1080.0)); + + Point2d pt_distorted(theRNG().uniform(0.0, 1920.0), theRNG().uniform(0.0, 1080.0)); + std::vector pt_distorted_vec; pt_distorted_vec.push_back(pt_distorted); + const double maxError = 1e-6; TermCriteria criteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 100, maxError); + std::vector pt_undist_vec; undistortPoints(pt_distorted_vec, pt_undist_vec, cameraMatrix, distCoeffs, noArray(), noArray(), criteria); - std::vector pt_redistorted_vec; std::vector pt_undist_vec_homogeneous; - pt_undist_vec_homogeneous.push_back( Point3d(pt_undist_vec[0].x, pt_undist_vec[0].y, 1.0) ); - projectPoints(pt_undist_vec_homogeneous, Mat::zeros(3,1,CV_64F), Mat::zeros(3,1,CV_64F), cameraMatrix, distCoeffs, pt_redistorted_vec); + pt_undist_vec_homogeneous.emplace_back(pt_undist_vec[0].x, pt_undist_vec[0].y, 1.0 ); + + std::vector pt_redistorted_vec; + projectPoints(pt_undist_vec_homogeneous, Mat::zeros(3,1,CV_64F), + Mat::zeros(3,1,CV_64F), cameraMatrix, distCoeffs, pt_redistorted_vec); + const double obtainedError = sqrt( pow(pt_distorted.x - pt_redistorted_vec[0].x, 2) + pow(pt_distorted.y - pt_redistorted_vec[0].y, 2) ); ASSERT_LE(obtainedError, maxError); } -TEST(undistortPoints, regression_14583) +TEST_F(UndistortPointsTest, regression_14583) { const int col = 720; // const int row = 540;