From 4009bca59a3655cdc50e6dfa4a65e5a73fa415da Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Mon, 23 Jan 2023 12:59:43 +0100 Subject: [PATCH] Merge pull request #23025 from savuor:backport3_stddev_calib_fix Backport of #22992 to 3.4 ### 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 --- modules/calib3d/src/calibration.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/modules/calib3d/src/calibration.cpp b/modules/calib3d/src/calibration.cpp index ee1a751530..7081ee8b4d 100644 --- a/modules/calib3d/src/calibration.cpp +++ b/modules/calib3d/src/calibration.cpp @@ -1549,6 +1549,12 @@ static double cvCalibrateCamera2Internal( const CvMat* objectPoints, } } + Mat mask = cvarrToMat(solver.mask); + int nparams_nz = countNonZero(mask); + if (nparams_nz >= 2 * total) + CV_Error_(CV_StsBadArg, + ("There should be less vars to optimize (having %d) than the number of residuals (%d = 2 per point)", nparams_nz, 2 * total)); + // 2. initialize extrinsic parameters for( i = 0, pos = 0; i < nimages; i++, pos += ni ) { @@ -1651,27 +1657,24 @@ static double cvCalibrateCamera2Internal( const CvMat* objectPoints, { if( stdDevs ) { - Mat mask = cvarrToMat(solver.mask); - int nparams_nz = countNonZero(mask); Mat JtJinv, JtJN; JtJN.create(nparams_nz, nparams_nz, CV_64F); subMatrix(cvarrToMat(_JtJ), JtJN, mask, mask); completeSymm(JtJN, false); cv::invert(JtJN, JtJinv, DECOMP_SVD); - //sigma2 is deviation of the noise - //see any papers about variance of the least squares estimator for - //detailed description of the variance estimation methods - double sigma2 = norm(allErrors, NORM_L2SQR) / (total - nparams_nz); + // an explanation of that denominator correction can be found here: + // R. Hartley, A. Zisserman, Multiple View Geometry in Computer Vision, 2004, section 5.1.3, page 134 + // see the discussion for more details: https://github.com/opencv/opencv/pull/22992 + int nErrors = 2 * total - nparams_nz; + double sigma2 = norm(allErrors, NORM_L2SQR) / nErrors; Mat stdDevsM = cvarrToMat(stdDevs); int j = 0; for ( int s = 0; s < nparams; s++ ) + { + stdDevsM.at(s) = mask.data[s] ? std::sqrt(JtJinv.at(j,j) * sigma2) : 0.0; if( mask.data[s] ) - { - stdDevsM.at(s) = std::sqrt(JtJinv.at(j,j) * sigma2); j++; - } - else - stdDevsM.at(s) = 0.; + } } break; }