Skip to content

Commit 203dc3b

Browse files
parojalalek
authored andcommitted
Merge pull request opencv#10667 from paroj:stereo_calib_ex
calib3d: add stereoCalibrateExtended (opencv#10667) * cvCalibrateCamera2Internal: simplify per view error computation * calib3d: add stereoCalibrateExtended - allow CALIB_USE_EXTRINSIC_GUESS - returns per view errors * calib3d: add stereoCalibrateExtended test
1 parent 7b8ab4e commit 203dc3b

File tree

3 files changed

+152
-33
lines changed

3 files changed

+152
-33
lines changed

modules/calib3d/include/opencv2/calib3d.hpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ enum { CALIB_USE_INTRINSIC_GUESS = 0x00001,
276276
// for stereo rectification
277277
CALIB_ZERO_DISPARITY = 0x00400,
278278
CALIB_USE_LU = (1 << 17), //!< use LU instead of SVD decomposition for solving. much faster but potentially less precise
279+
CALIB_USE_EXTRINSIC_GUESS = (1 << 22), //!< for stereoCalibrate
279280
};
280281

281282
//! the algorithm for finding fundamental matrix
@@ -1125,11 +1126,14 @@ is similar to distCoeffs1 .
11251126
@param T Output translation vector between the coordinate systems of the cameras.
11261127
@param E Output essential matrix.
11271128
@param F Output fundamental matrix.
1129+
@param perViewErrors Output vector of the RMS re-projection error estimated for each pattern view.
11281130
@param flags Different flags that may be zero or a combination of the following values:
11291131
- **CALIB_FIX_INTRINSIC** Fix cameraMatrix? and distCoeffs? so that only R, T, E , and F
11301132
matrices are estimated.
11311133
- **CALIB_USE_INTRINSIC_GUESS** Optimize some or all of the intrinsic parameters
11321134
according to the specified flags. Initial values are provided by the user.
1135+
- **CALIB_USE_EXTRINSIC_GUESS** R, T contain valid initial values that are optimized further.
1136+
Otherwise R, T are initialized to the median value of the pattern views (each dimension separately).
11331137
- **CALIB_FIX_PRINCIPAL_POINT** Fix the principal points during the optimization.
11341138
- **CALIB_FIX_FOCAL_LENGTH** Fix \f$f^{(j)}_x\f$ and \f$f^{(j)}_y\f$ .
11351139
- **CALIB_FIX_ASPECT_RATIO** Optimize \f$f^{(j)}_y\f$ . Fix the ratio \f$f^{(j)}_x/f^{(j)}_y\f$
@@ -1194,6 +1198,15 @@ Similarly to calibrateCamera , the function minimizes the total re-projection er
11941198
points in all the available views from both cameras. The function returns the final value of the
11951199
re-projection error.
11961200
*/
1201+
CV_EXPORTS_AS(stereoCalibrateExtended) double stereoCalibrate( InputArrayOfArrays objectPoints,
1202+
InputArrayOfArrays imagePoints1, InputArrayOfArrays imagePoints2,
1203+
InputOutputArray cameraMatrix1, InputOutputArray distCoeffs1,
1204+
InputOutputArray cameraMatrix2, InputOutputArray distCoeffs2,
1205+
Size imageSize, InputOutputArray R,InputOutputArray T, OutputArray E, OutputArray F,
1206+
OutputArray perViewErrors, int flags = CALIB_FIX_INTRINSIC,
1207+
TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 1e-6) );
1208+
1209+
/// @overload
11971210
CV_EXPORTS_W double stereoCalibrate( InputArrayOfArrays objectPoints,
11981211
InputArrayOfArrays imagePoints1, InputArrayOfArrays imagePoints2,
11991212
InputOutputArray cameraMatrix1, InputOutputArray distCoeffs1,
@@ -1202,7 +1215,6 @@ CV_EXPORTS_W double stereoCalibrate( InputArrayOfArrays objectPoints,
12021215
int flags = CALIB_FIX_INTRINSIC,
12031216
TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 1e-6) );
12041217

1205-
12061218
/** @brief Computes rectification transforms for each head of a calibrated stereo camera.
12071219
12081220
@param cameraMatrix1 First camera matrix.

modules/calib3d/src/calibration.cpp

Lines changed: 97 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1629,7 +1629,12 @@ static double cvCalibrateCamera2Internal( const CvMat* objectPoints,
16291629
JtErr.rowRange(NINTRINSIC + i * 6, NINTRINSIC + (i + 1) * 6) = _Je.t() * _err;
16301630
}
16311631

1632-
reprojErr += norm(_err, NORM_L2SQR);
1632+
double viewErr = norm(_err, NORM_L2SQR);
1633+
1634+
if( perViewErrors )
1635+
perViewErrors->data.db[i] = std::sqrt(viewErr / ni);
1636+
1637+
reprojErr += viewErr;
16331638
}
16341639
if( _errNorm )
16351640
*_errNorm = reprojErr;
@@ -1671,13 +1676,6 @@ static double cvCalibrateCamera2Internal( const CvMat* objectPoints,
16711676
for( i = 0, pos = 0; i < nimages; i++ )
16721677
{
16731678
CvMat src, dst;
1674-
if( perViewErrors )
1675-
{
1676-
ni = npoints->data.i[i*npstep];
1677-
perViewErrors->data.db[i] = std::sqrt(cv::norm(allErrors.colRange(pos, pos + ni),
1678-
NORM_L2SQR) / ni);
1679-
pos+=ni;
1680-
}
16811679

16821680
if( rvecs )
16831681
{
@@ -1757,14 +1755,13 @@ static int dbCmp( const void* _a, const void* _b )
17571755
return (a > b) - (a < b);
17581756
}
17591757

1760-
1761-
double cvStereoCalibrate( const CvMat* _objectPoints, const CvMat* _imagePoints1,
1758+
static double cvStereoCalibrateImpl( const CvMat* _objectPoints, const CvMat* _imagePoints1,
17621759
const CvMat* _imagePoints2, const CvMat* _npoints,
17631760
CvMat* _cameraMatrix1, CvMat* _distCoeffs1,
17641761
CvMat* _cameraMatrix2, CvMat* _distCoeffs2,
17651762
CvSize imageSize, CvMat* matR, CvMat* matT,
17661763
CvMat* matE, CvMat* matF,
1767-
int flags,
1764+
CvMat* perViewErr, int flags,
17681765
CvTermCriteria termCrit )
17691766
{
17701767
const int NINTRINSIC = 18;
@@ -1866,9 +1863,6 @@ double cvStereoCalibrate( const CvMat* _objectPoints, const CvMat* _imagePoints1
18661863
// for intrinisic parameters of each camera ((fx,fy,cx,cy,k1,k2,p1,p2) ~ 8 parameters).
18671864
nparams = 6*(nimages+1) + (recomputeIntrinsics ? NINTRINSIC*2 : 0);
18681865

1869-
// storage for initial [om(R){i}|t{i}] (in order to compute the median for each component)
1870-
RT0.reset(cvCreateMat( 6, nimages, CV_64F ));
1871-
18721866
CvLevMarq solver( nparams, 0, termCrit );
18731867

18741868
if(flags & CALIB_USE_LU) {
@@ -1918,6 +1912,8 @@ double cvStereoCalibrate( const CvMat* _objectPoints, const CvMat* _imagePoints1
19181912
}
19191913
}
19201914

1915+
// storage for initial [om(R){i}|t{i}] (in order to compute the median for each component)
1916+
RT0.reset(cvCreateMat( 6, nimages, CV_64F ));
19211917
/*
19221918
Compute initial estimate of pose
19231919
For each image, compute:
@@ -1968,12 +1964,32 @@ double cvStereoCalibrate( const CvMat* _objectPoints, const CvMat* _imagePoints1
19681964
RT0->data.db[i + nimages*5] = t[1][2];
19691965
}
19701966

1971-
// find the medians and save the first 6 parameters
1972-
for( i = 0; i < 6; i++ )
1967+
if(flags & CALIB_USE_EXTRINSIC_GUESS)
19731968
{
1974-
qsort( RT0->data.db + i*nimages, nimages, CV_ELEM_SIZE(RT0->type), dbCmp );
1975-
solver.param->data.db[i] = nimages % 2 != 0 ? RT0->data.db[i*nimages + nimages/2] :
1976-
(RT0->data.db[i*nimages + nimages/2 - 1] + RT0->data.db[i*nimages + nimages/2])*0.5;
1969+
Vec3d R, T;
1970+
cvarrToMat(matT).convertTo(T, CV_64F);
1971+
1972+
if( matR->rows == 3 && matR->cols == 3 )
1973+
Rodrigues(cvarrToMat(matR), R);
1974+
else
1975+
cvarrToMat(matR).convertTo(R, CV_64F);
1976+
1977+
solver.param->data.db[0] = R[0];
1978+
solver.param->data.db[1] = R[1];
1979+
solver.param->data.db[2] = R[2];
1980+
solver.param->data.db[3] = T[0];
1981+
solver.param->data.db[4] = T[1];
1982+
solver.param->data.db[5] = T[2];
1983+
}
1984+
else
1985+
{
1986+
// find the medians and save the first 6 parameters
1987+
for( i = 0; i < 6; i++ )
1988+
{
1989+
qsort( RT0->data.db + i*nimages, nimages, CV_ELEM_SIZE(RT0->type), dbCmp );
1990+
solver.param->data.db[i] = nimages % 2 != 0 ? RT0->data.db[i*nimages + nimages/2] :
1991+
(RT0->data.db[i*nimages + nimages/2 - 1] + RT0->data.db[i*nimages + nimages/2])*0.5;
1992+
}
19771993
}
19781994

19791995
if( recomputeIntrinsics )
@@ -2151,7 +2167,12 @@ double cvStereoCalibrate( const CvMat* _objectPoints, const CvMat* _imagePoints1
21512167
}
21522168
}
21532169

2154-
reprojErr += norm(err, NORM_L2SQR);
2170+
double viewErr = norm(err, NORM_L2SQR);
2171+
2172+
if(perViewErr)
2173+
perViewErr->data.db[i*2 + k] = std::sqrt(viewErr/ni);
2174+
2175+
reprojErr += viewErr;
21552176
}
21562177
}
21572178
if(_errNorm)
@@ -2209,7 +2230,19 @@ double cvStereoCalibrate( const CvMat* _objectPoints, const CvMat* _imagePoints1
22092230

22102231
return std::sqrt(reprojErr/(pointsTotal*2));
22112232
}
2212-
2233+
double cvStereoCalibrate( const CvMat* _objectPoints, const CvMat* _imagePoints1,
2234+
const CvMat* _imagePoints2, const CvMat* _npoints,
2235+
CvMat* _cameraMatrix1, CvMat* _distCoeffs1,
2236+
CvMat* _cameraMatrix2, CvMat* _distCoeffs2,
2237+
CvSize imageSize, CvMat* matR, CvMat* matT,
2238+
CvMat* matE, CvMat* matF,
2239+
int flags,
2240+
CvTermCriteria termCrit )
2241+
{
2242+
return cvStereoCalibrateImpl(_objectPoints, _imagePoints1, _imagePoints2, _npoints, _cameraMatrix1,
2243+
_distCoeffs1, _cameraMatrix2, _distCoeffs2, imageSize, matR, matT, matE,
2244+
matF, NULL, flags, termCrit);
2245+
}
22132246

22142247
static void
22152248
icvGetRectangles( const CvMat* cameraMatrix, const CvMat* distCoeffs,
@@ -3474,7 +3507,26 @@ double cv::stereoCalibrate( InputArrayOfArrays _objectPoints,
34743507
InputOutputArray _cameraMatrix1, InputOutputArray _distCoeffs1,
34753508
InputOutputArray _cameraMatrix2, InputOutputArray _distCoeffs2,
34763509
Size imageSize, OutputArray _Rmat, OutputArray _Tmat,
3477-
OutputArray _Emat, OutputArray _Fmat, int flags ,
3510+
OutputArray _Emat, OutputArray _Fmat, int flags,
3511+
TermCriteria criteria)
3512+
{
3513+
Mat Rmat, Tmat;
3514+
double ret = stereoCalibrate(_objectPoints, _imagePoints1, _imagePoints2, _cameraMatrix1, _distCoeffs1,
3515+
_cameraMatrix2, _distCoeffs2, imageSize, Rmat, Tmat, _Emat, _Fmat,
3516+
noArray(), flags, criteria);
3517+
Rmat.copyTo(_Rmat);
3518+
Tmat.copyTo(_Tmat);
3519+
return ret;
3520+
}
3521+
3522+
double cv::stereoCalibrate( InputArrayOfArrays _objectPoints,
3523+
InputArrayOfArrays _imagePoints1,
3524+
InputArrayOfArrays _imagePoints2,
3525+
InputOutputArray _cameraMatrix1, InputOutputArray _distCoeffs1,
3526+
InputOutputArray _cameraMatrix2, InputOutputArray _distCoeffs2,
3527+
Size imageSize, InputOutputArray _Rmat, InputOutputArray _Tmat,
3528+
OutputArray _Emat, OutputArray _Fmat,
3529+
OutputArray _perViewErrors, int flags ,
34783530
TermCriteria criteria)
34793531
{
34803532
int rtype = CV_64F;
@@ -3495,8 +3547,11 @@ double cv::stereoCalibrate( InputArrayOfArrays _objectPoints,
34953547
distCoeffs2 = distCoeffs2.rows == 1 ? distCoeffs2.colRange(0, 5) : distCoeffs2.rowRange(0, 5);
34963548
}
34973549

3498-
_Rmat.create(3, 3, rtype);
3499-
_Tmat.create(3, 1, rtype);
3550+
if((flags & CALIB_USE_EXTRINSIC_GUESS) == 0)
3551+
{
3552+
_Rmat.create(3, 3, rtype);
3553+
_Tmat.create(3, 1, rtype);
3554+
}
35003555

35013556
Mat objPt, imgPt, imgPt2, npoints;
35023557

@@ -3505,22 +3560,32 @@ double cv::stereoCalibrate( InputArrayOfArrays _objectPoints,
35053560
CvMat c_objPt = objPt, c_imgPt = imgPt, c_imgPt2 = imgPt2, c_npoints = npoints;
35063561
CvMat c_cameraMatrix1 = cameraMatrix1, c_distCoeffs1 = distCoeffs1;
35073562
CvMat c_cameraMatrix2 = cameraMatrix2, c_distCoeffs2 = distCoeffs2;
3508-
CvMat c_matR = _Rmat.getMat(), c_matT = _Tmat.getMat(), c_matE, c_matF, *p_matE = 0, *p_matF = 0;
3563+
CvMat c_matR = _Rmat.getMat(), c_matT = _Tmat.getMat(), c_matE, c_matF, c_matErr;
3564+
3565+
bool E_needed = _Emat.needed(), F_needed = _Fmat.needed(), errors_needed = _perViewErrors.needed();
35093566

3510-
if( _Emat.needed() )
3567+
if( E_needed )
35113568
{
35123569
_Emat.create(3, 3, rtype);
3513-
p_matE = &(c_matE = _Emat.getMat());
3570+
c_matE = _Emat.getMat();
35143571
}
3515-
if( _Fmat.needed() )
3572+
if( F_needed )
35163573
{
35173574
_Fmat.create(3, 3, rtype);
3518-
p_matF = &(c_matF = _Fmat.getMat());
3575+
c_matF = _Fmat.getMat();
3576+
}
3577+
3578+
if( errors_needed )
3579+
{
3580+
int nimages = int(_objectPoints.total());
3581+
_perViewErrors.create(nimages, 2, CV_64F);
3582+
c_matErr = _perViewErrors.getMat();
35193583
}
35203584

3521-
double err = cvStereoCalibrate(&c_objPt, &c_imgPt, &c_imgPt2, &c_npoints, &c_cameraMatrix1,
3522-
&c_distCoeffs1, &c_cameraMatrix2, &c_distCoeffs2, imageSize,
3523-
&c_matR, &c_matT, p_matE, p_matF, flags, criteria );
3585+
double err = cvStereoCalibrateImpl(&c_objPt, &c_imgPt, &c_imgPt2, &c_npoints, &c_cameraMatrix1,
3586+
&c_distCoeffs1, &c_cameraMatrix2, &c_distCoeffs2, imageSize, &c_matR,
3587+
&c_matT, E_needed ? &c_matE : NULL, F_needed ? &c_matF : NULL,
3588+
errors_needed ? &c_matErr : NULL, flags, criteria);
35243589

35253590
cameraMatrix1.copyTo(_cameraMatrix1);
35263591
cameraMatrix2.copyTo(_cameraMatrix2);

modules/calib3d/test/test_cameracalibration.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2076,6 +2076,48 @@ TEST(Calib3d_StereoCalibrate_C, regression) { CV_StereoCalibrationTest_C test; t
20762076
TEST(Calib3d_StereoCalibrate_CPP, regression) { CV_StereoCalibrationTest_CPP test; test.safe_run(); }
20772077
TEST(Calib3d_StereoCalibrateCorner, regression) { CV_StereoCalibrationCornerTest test; test.safe_run(); }
20782078

2079+
TEST(Calib3d_StereoCalibrate_CPP, extended)
2080+
{
2081+
cvtest::TS* ts = cvtest::TS::ptr();
2082+
String filepath = cv::format("%scv/stereo/case%d/", ts->get_data_path().c_str(), 1 );
2083+
2084+
Mat left = imread(filepath+"left01.png");
2085+
Mat right = imread(filepath+"right01.png");
2086+
if(left.empty() || right.empty())
2087+
{
2088+
ts->set_failed_test_info( cvtest::TS::FAIL_MISSING_TEST_DATA );
2089+
return;
2090+
}
2091+
2092+
vector<vector<Point2f> > imgpt1(1), imgpt2(1);
2093+
vector<vector<Point3f> > objpt(1);
2094+
Size patternSize(9, 6), imageSize(640, 480);
2095+
bool found1 = findChessboardCorners(left, patternSize, imgpt1[0]);
2096+
bool found2 = findChessboardCorners(right, patternSize, imgpt2[0]);
2097+
2098+
if(!found1 || !found2)
2099+
{
2100+
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
2101+
return;
2102+
}
2103+
2104+
for( int j = 0; j < patternSize.width*patternSize.height; j++ )
2105+
objpt[0].push_back(Point3f((float)(j%patternSize.width), (float)(j/patternSize.width), 0.f));
2106+
2107+
Mat K1, K2, c1, c2, R, T, E, F, err;
2108+
int flags = 0;
2109+
double res0 = stereoCalibrate( objpt, imgpt1, imgpt2,
2110+
K1, c1, K2, c2,
2111+
imageSize, R, T, E, F, err, flags);
2112+
2113+
flags = CALIB_USE_EXTRINSIC_GUESS;
2114+
double res1 = stereoCalibrate( objpt, imgpt1, imgpt2,
2115+
K1, c1, K2, c2,
2116+
imageSize, R, T, E, F, err, flags);
2117+
EXPECT_LE(res1, res0);
2118+
EXPECT_TRUE(err.total() == 2);
2119+
}
2120+
20792121
TEST(Calib3d_Triangulate, accuracy)
20802122
{
20812123
// the testcase from http://code.opencv.org/issues/4334

0 commit comments

Comments
 (0)