From 1d4d81103e9d52a4f599ac69ab53379c4a1af0c7 Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Tue, 2 Jun 2026 07:25:44 +0200 Subject: [PATCH] Merge pull request #29077 from vrabaud:throw Homogeneize some calib/3d behavior #29077 - use CV_Check to validate input sizes (thus throwing for invalid inputs) - return bool to validate a function result This fixes #22746 ### Pull Request Readiness Checklist 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 --- .../geometry/include/opencv2/geometry/3d.hpp | 16 +++++--- modules/geometry/src/fundam.cpp | 5 +-- modules/geometry/src/ptsetreg.cpp | 37 ++++++++++++------- .../geometry/test/test_affine2d_estimator.cpp | 9 ++++- .../test/test_affine_partial2d_estimator.cpp | 9 ++++- .../objdetect/include/opencv2/objdetect.hpp | 33 ++++++++++------- 6 files changed, 71 insertions(+), 38 deletions(-) diff --git a/modules/geometry/include/opencv2/geometry/3d.hpp b/modules/geometry/include/opencv2/geometry/3d.hpp index 8a2c70164e..ffe6db1c53 100644 --- a/modules/geometry/include/opencv2/geometry/3d.hpp +++ b/modules/geometry/include/opencv2/geometry/3d.hpp @@ -1885,12 +1885,14 @@ an inlier. between 0.95 and 0.99 is usually good enough. Values too close to 1 can slow down the estimation significantly. Values lower than 0.8-0.9 can result in an incorrectly estimated transformation. +@return Whether a solution was found. + The function estimates an optimal 3D affine transformation between two 3D point sets using the RANSAC algorithm. */ -CV_EXPORTS_W int estimateAffine3D(InputArray src, InputArray dst, - OutputArray out, OutputArray inliers, - double ransacThreshold = 3, double confidence = 0.99); +CV_EXPORTS_W bool estimateAffine3D(InputArray src, InputArray dst, + OutputArray out, OutputArray inliers, + double ransacThreshold = 3, double confidence = 0.99); /** @brief Computes an optimal affine transformation between two 3D point sets. @@ -1959,12 +1961,14 @@ CV_EXPORTS_W cv::Mat estimateAffine3D(InputArray src, InputArray dst, * between 0.95 and 0.99 is usually good enough. Values too close to 1 can slow down the estimation * significantly. Values lower than 0.8-0.9 can result in an incorrectly estimated transformation. * + * @return Whether a translation was found. + * * The function estimates an optimal 3D translation between two 3D point sets using the * RANSAC algorithm. * */ -CV_EXPORTS_W int estimateTranslation3D(InputArray src, InputArray dst, - OutputArray out, OutputArray inliers, - double ransacThreshold = 3, double confidence = 0.99); +CV_EXPORTS_W bool estimateTranslation3D(InputArray src, InputArray dst, + OutputArray out, OutputArray inliers, + double ransacThreshold = 3, double confidence = 0.99); /** @brief Computes an optimal affine transformation between two 2D point sets. diff --git a/modules/geometry/src/fundam.cpp b/modules/geometry/src/fundam.cpp index 786dda08b7..3aba71247a 100644 --- a/modules/geometry/src/fundam.cpp +++ b/modules/geometry/src/fundam.cpp @@ -320,13 +320,10 @@ Mat findHomography( InputArray _points1, InputArray _points2, npoints = p.checkVector(3, -1, false); if( npoints < 0 ) CV_Error(Error::StsBadArg, "The input arrays should be 2D or 3D point sets"); - if( npoints == 0 ) - return Mat(); convertPointsFromHomogeneous(p, p); } // Need at least 4 point correspondences to calculate Homography - if( npoints < 4 ) - CV_Error(Error::StsVecLengthErr , "The input arrays should have at least 4 corresponding point sets to calculate Homography"); + CV_CheckGE(npoints, 4, "The input arrays should have at least 4 corresponding point sets to calculate Homography"); p.reshape(2, npoints).convertTo(m, CV_32F); } diff --git a/modules/geometry/src/ptsetreg.cpp b/modules/geometry/src/ptsetreg.cpp index 2bf4c40e82..e8667f9524 100644 --- a/modules/geometry/src/ptsetreg.cpp +++ b/modules/geometry/src/ptsetreg.cpp @@ -829,16 +829,18 @@ public: } }; -int estimateAffine3D(InputArray _from, InputArray _to, - OutputArray _out, OutputArray _inliers, - double ransacThreshold, double confidence) +bool estimateAffine3D(InputArray _from, InputArray _to, + OutputArray _out, OutputArray _inliers, + double ransacThreshold, double confidence) { CV_INSTRUMENT_REGION(); Mat from = _from.getMat(), to = _to.getMat(); int count = from.checkVector(3); - CV_Assert( count >= 0 && to.checkVector(3) == count ); + CV_CheckGE( count, 0, "Points need to be 3d"); + CV_CheckGE( count, 3, "At least 3 points are needed for affine transformation estimation."); + CV_CheckEQ(to.checkVector(3), count, "Point matches need to have the same size"); Mat dFrom, dTo; from.convertTo(dFrom, CV_32F); @@ -857,11 +859,13 @@ Mat estimateAffine3D(InputArray _from, InputArray _to, CV_OUT double* _scale, bool force_rotation) { CV_INSTRUMENT_REGION(); + Mat from = _from.getMat(), to = _to.getMat(); int count = from.checkVector(3); - CV_CheckGE(count, 3, "Umeyama algorithm needs at least 3 points for affine transformation estimation."); - CV_CheckEQ(to.checkVector(3), count, "Point sets need to have the same size"); + CV_CheckGE(count, 3, "At least 3 points are needed for affine transformation estimation."); + CV_CheckEQ(to.checkVector(3), count, "Point matches need to have the same size"); + from = from.reshape(1, count); to = to.reshape(1, count); if(from.type() != CV_64F) @@ -933,16 +937,17 @@ Mat estimateAffine3D(InputArray _from, InputArray _to, return transform; } -int estimateTranslation3D(InputArray _from, InputArray _to, - OutputArray _out, OutputArray _inliers, - double ransacThreshold, double confidence) +bool estimateTranslation3D(InputArray _from, InputArray _to, + OutputArray _out, OutputArray _inliers, + double ransacThreshold, double confidence) { CV_INSTRUMENT_REGION(); Mat from = _from.getMat(), to = _to.getMat(); int count = from.checkVector(3); - CV_Assert( count >= 0 && to.checkVector(3) == count ); + CV_CheckGE(count, 0, "Points need to be 3d"); + CV_CheckEQ(to.checkVector(3), count, "Point matches need to have the same size"); Mat dFrom, dTo; from.convertTo(dFrom, CV_32F); @@ -972,7 +977,9 @@ Mat estimateAffine2D(InputArray _from, InputArray _to, OutputArray _inliers, bool result = false; Mat H; - CV_Assert( count >= 0 && to.checkVector(2) == count ); + CV_CheckGE(count, 0, "Points need to be 2d"); + CV_CheckGE(count, 2, "At least 2 points for partial affine transformation estimation."); + CV_CheckEQ(to.checkVector(2), count, "Point matches need to have the same size"); if (from.type() != CV_32FC2 || to.type() != CV_32FC2) { @@ -1108,7 +1115,9 @@ Mat estimateAffinePartial2D(InputArray _from, InputArray _to, OutputArray _inlie bool result = false; Mat H; - CV_Assert( count >= 0 && to.checkVector(2) == count ); + CV_CheckGE(count, 0, "Points need to be 2d"); + CV_CheckGE(count, 2, "At least 2 points for partial affine transformation estimation."); + CV_CheckEQ(to.checkVector(2), count, "Point matches need to have the same size"); if (from.type() != CV_32FC2 || to.type() != CV_32FC2) { @@ -1253,7 +1262,9 @@ Vec2d estimateTranslation2D(InputArray _from, InputArray _to, Mat from = _from.getMat(), to = _to.getMat(); int count = from.checkVector(2); bool result = false; - CV_Assert(count >= 0 && to.checkVector(2) == count); + + CV_CheckGE(count, 0, "Points need to be 2d"); + CV_CheckEQ(to.checkVector(2), count, "Point matches need to have the same size"); if (from.type() != CV_32FC2 || to.type() != CV_32FC2) { Mat tmp1, tmp2; diff --git a/modules/geometry/test/test_affine2d_estimator.cpp b/modules/geometry/test/test_affine2d_estimator.cpp index 2282dc3240..fac1636b1f 100644 --- a/modules/geometry/test/test_affine2d_estimator.cpp +++ b/modules/geometry/test/test_affine2d_estimator.cpp @@ -50,6 +50,13 @@ static float rngIn(float from, float to) { return from + (to-from) * (float)theR TEST_P(EstimateAffine2D, test3Points) { + const int method = GetParam(); + for (size_t i = 0; i < 2; ++i) + { + std::vector fpts(i), tpts(i); + vector inliers; + EXPECT_THROW(estimateAffine2D(fpts, tpts, inliers, method), cv::Exception); + } // try more transformations for (size_t i = 0; i < 500; ++i) { @@ -67,7 +74,7 @@ TEST_P(EstimateAffine2D, test3Points) transform(fpts, tpts, aff); vector inliers; - Mat aff_est = estimateAffine2D(fpts, tpts, inliers, GetParam() /* method */); + Mat aff_est = estimateAffine2D(fpts, tpts, inliers, method); EXPECT_NEAR(0., cvtest::norm(aff_est, aff, NORM_INF), 1e-3); diff --git a/modules/geometry/test/test_affine_partial2d_estimator.cpp b/modules/geometry/test/test_affine_partial2d_estimator.cpp index dbbb4da0d9..9b30ae5976 100644 --- a/modules/geometry/test/test_affine_partial2d_estimator.cpp +++ b/modules/geometry/test/test_affine_partial2d_estimator.cpp @@ -62,6 +62,13 @@ static Mat rngPartialAffMat() { TEST_P(EstimateAffinePartial2D, test2Points) { + const int method = GetParam(); + for (size_t i = 0; i < 2; ++i) + { + std::vector fpts(i), tpts(i); + vector inliers; + EXPECT_THROW(estimateAffinePartial2D(fpts, tpts, inliers, method), cv::Exception); + } // try more transformations for (size_t i = 0; i < 500; ++i) { @@ -77,7 +84,7 @@ TEST_P(EstimateAffinePartial2D, test2Points) transform(fpts, tpts, aff); vector inliers; - Mat aff_est = estimateAffinePartial2D(fpts, tpts, inliers, GetParam() /* method */); + Mat aff_est = estimateAffinePartial2D(fpts, tpts, inliers, method); EXPECT_NEAR(0., cvtest::norm(aff_est, aff, NORM_INF), 1e-3); diff --git a/modules/objdetect/include/opencv2/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect.hpp index b71af5cb66..51b00e36ea 100644 --- a/modules/objdetect/include/opencv2/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect.hpp @@ -354,14 +354,16 @@ No image processing is done to improve to find the checkerboard. This has the ef execution of the function but could lead to not recognizing the checkerboard if the image is not previously binarized in the appropriate manner. +@return True if all of the corners are found and placed in a certain order (row by row, +left to right in every row). Otherwise, if the function fails to find all the corners or reorder them, +it returns false. + The function attempts to determine whether the input image is a view of the chessboard pattern and -locate the internal chessboard corners. The function returns a non-zero value if all of the corners -are found and they are placed in a certain order (row by row, left to right in every row). -Otherwise, if the function fails to find all the corners or reorder them, it returns 0. For example, -a regular chessboard has 8 x 8 squares and 7 x 7 internal corners, that is, points where the black -squares touch each other. The detected coordinates are approximate, and to determine their positions -more accurately, the function calls #cornerSubPix. You also may use the function #cornerSubPix with -different parameters if returned coordinates are not accurate enough. +locate the internal chessboard corners. For example, a regular chessboard has 8 x 8 squares and +7 x 7 internal corners, that is, points where the black squares touch each other. The detected +coordinates are approximate, and to determine their positions more accurately, the function +calls #cornerSubPix. You also may use the function #cornerSubPix with different parameters if +returned coordinates are not accurate enough. Sample usage of detecting and drawing chessboard corners: : @code @@ -392,9 +394,12 @@ to create the desired checkerboard pattern. CV_EXPORTS_W bool findChessboardCorners( InputArray image, Size patternSize, OutputArray corners, int flags = CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE ); -/* - Checks whether the image contains chessboard of the specific size or not. - If yes, nonzero value is returned. +/** @brief Checks whether the image contains chessboard of the specific size or not. + +@param img Source chessboard view. +@param size Size of the chessboard. + +@return Whether a chessboard was found. */ CV_EXPORTS_W bool checkChessboard(InputArray img, Size size); @@ -552,10 +557,12 @@ perspective distortions but much more sensitive to background clutter. If `blobDetector` is NULL then `image` represents Point2f array of candidates. @param parameters struct for finding circles in a grid pattern. +return True if all of the centers have been found and they have been placed in a certain order +(row by row, left to right in every row). Otherwise, if the function fails to find all the corners +or reorder them, it returns false. + The function attempts to determine whether the input image contains a grid of circles. If it is, the -function locates centers of the circles. The function returns a non-zero value if all of the centers -have been found and they have been placed in a certain order (row by row, left to right in every -row). Otherwise, if the function fails to find all the corners or reorder them, it returns 0. +function locates centers of the circles. Sample usage of detecting and drawing the centers of circles: : @code