mirror of
https://github.com/opencv/opencv.git
synced 2026-07-25 21:33:04 +04:00
59218f9edd
Geometry module #29175 OpenCV Contrib: https://github.com/opencv/opencv_contrib/pull/4129 CI changes: https://github.com/opencv/ci-gha-workflow/pull/313 Continues - https://github.com/opencv/opencv/pull/28804 - https://github.com/opencv/opencv/pull/29101 - https://github.com/opencv/opencv/pull/29108 - https://github.com/opencv/opencv/pull/28810 Todo for followup PRs: - [x] Rename doxygen groups - [x] Fix JS modules layout and whitelists - [ ] Sort tutorials code/snippets ### 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 - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake
1423 lines
56 KiB
C++
1423 lines
56 KiB
C++
// 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 "test_precomp.hpp"
|
|
#include "opencv2/geometry.hpp"
|
|
#include "opencv2/ts/cuda_test.hpp" // EXPECT_MAT_NEAR
|
|
|
|
namespace opencv_test { namespace {
|
|
|
|
#if 0
|
|
class CV_ProjectPointsTest : public cvtest::ArrayTest
|
|
{
|
|
public:
|
|
CV_ProjectPointsTest();
|
|
|
|
protected:
|
|
int read_params( const cv::FileStorage& fs );
|
|
void fill_array( int test_case_idx, int i, int j, Mat& arr );
|
|
int prepare_test_case( int test_case_idx );
|
|
void get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types );
|
|
double get_success_error_level( int test_case_idx, int i, int j );
|
|
void run_func();
|
|
void prepare_to_validation( int );
|
|
|
|
bool calc_jacobians;
|
|
};
|
|
|
|
|
|
CV_ProjectPointsTest::CV_ProjectPointsTest()
|
|
: cvtest::ArrayTest( "3d-ProjectPoints", "cvProjectPoints2", "" )
|
|
{
|
|
test_array[INPUT].push_back(NULL); // rotation vector
|
|
test_array[OUTPUT].push_back(NULL); // rotation matrix
|
|
test_array[OUTPUT].push_back(NULL); // jacobian (J)
|
|
test_array[OUTPUT].push_back(NULL); // rotation vector (backward transform result)
|
|
test_array[OUTPUT].push_back(NULL); // inverse transform jacobian (J1)
|
|
test_array[OUTPUT].push_back(NULL); // J*J1 (or J1*J) == I(3x3)
|
|
test_array[REF_OUTPUT].push_back(NULL);
|
|
test_array[REF_OUTPUT].push_back(NULL);
|
|
test_array[REF_OUTPUT].push_back(NULL);
|
|
test_array[REF_OUTPUT].push_back(NULL);
|
|
test_array[REF_OUTPUT].push_back(NULL);
|
|
|
|
element_wise_relative_error = false;
|
|
calc_jacobians = false;
|
|
}
|
|
|
|
|
|
int CV_ProjectPointsTest::read_params( const cv::FileStorage& fs )
|
|
{
|
|
int code = cvtest::ArrayTest::read_params( fs );
|
|
return code;
|
|
}
|
|
|
|
|
|
void CV_ProjectPointsTest::get_test_array_types_and_sizes(
|
|
int /*test_case_idx*/, vector<vector<Size> >& sizes, vector<vector<int> >& types )
|
|
{
|
|
RNG& rng = ts->get_rng();
|
|
int depth = cvtest::randInt(rng) % 2 == 0 ? CV_32F : CV_64F;
|
|
int i, code;
|
|
|
|
code = cvtest::randInt(rng) % 3;
|
|
types[INPUT][0] = CV_MAKETYPE(depth, 1);
|
|
|
|
if( code == 0 )
|
|
{
|
|
sizes[INPUT][0] = cvSize(1,1);
|
|
types[INPUT][0] = CV_MAKETYPE(depth, 3);
|
|
}
|
|
else if( code == 1 )
|
|
sizes[INPUT][0] = cvSize(3,1);
|
|
else
|
|
sizes[INPUT][0] = cvSize(1,3);
|
|
|
|
sizes[OUTPUT][0] = cvSize(3, 3);
|
|
types[OUTPUT][0] = CV_MAKETYPE(depth, 1);
|
|
|
|
types[OUTPUT][1] = CV_MAKETYPE(depth, 1);
|
|
|
|
if( cvtest::randInt(rng) % 2 )
|
|
sizes[OUTPUT][1] = cvSize(3,9);
|
|
else
|
|
sizes[OUTPUT][1] = cvSize(9,3);
|
|
|
|
types[OUTPUT][2] = types[INPUT][0];
|
|
sizes[OUTPUT][2] = sizes[INPUT][0];
|
|
|
|
types[OUTPUT][3] = types[OUTPUT][1];
|
|
sizes[OUTPUT][3] = cvSize(sizes[OUTPUT][1].height, sizes[OUTPUT][1].width);
|
|
|
|
types[OUTPUT][4] = types[OUTPUT][1];
|
|
sizes[OUTPUT][4] = cvSize(3,3);
|
|
|
|
calc_jacobians = 1;//cvtest::randInt(rng) % 3 != 0;
|
|
if( !calc_jacobians )
|
|
sizes[OUTPUT][1] = sizes[OUTPUT][3] = sizes[OUTPUT][4] = cvSize(0,0);
|
|
|
|
for( i = 0; i < 5; i++ )
|
|
{
|
|
types[REF_OUTPUT][i] = types[OUTPUT][i];
|
|
sizes[REF_OUTPUT][i] = sizes[OUTPUT][i];
|
|
}
|
|
}
|
|
|
|
|
|
double CV_ProjectPointsTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int j )
|
|
{
|
|
return j == 4 ? 1e-2 : 1e-2;
|
|
}
|
|
|
|
|
|
void CV_ProjectPointsTest::fill_array( int /*test_case_idx*/, int /*i*/, int /*j*/, CvMat* arr )
|
|
{
|
|
double r[3], theta0, theta1, f;
|
|
CvMat _r = cvMat( arr->rows, arr->cols, CV_MAKETYPE(CV_64F,CV_MAT_CN(arr->type)), r );
|
|
RNG& rng = ts->get_rng();
|
|
|
|
r[0] = cvtest::randReal(rng)*CV_PI*2;
|
|
r[1] = cvtest::randReal(rng)*CV_PI*2;
|
|
r[2] = cvtest::randReal(rng)*CV_PI*2;
|
|
|
|
theta0 = sqrt(r[0]*r[0] + r[1]*r[1] + r[2]*r[2]);
|
|
theta1 = fmod(theta0, CV_PI*2);
|
|
|
|
if( theta1 > CV_PI )
|
|
theta1 = -(CV_PI*2 - theta1);
|
|
|
|
f = theta1/(theta0 ? theta0 : 1);
|
|
r[0] *= f;
|
|
r[1] *= f;
|
|
r[2] *= f;
|
|
|
|
cvTsConvert( &_r, arr );
|
|
}
|
|
|
|
|
|
int CV_ProjectPointsTest::prepare_test_case( int test_case_idx )
|
|
{
|
|
int code = cvtest::ArrayTest::prepare_test_case( test_case_idx );
|
|
return code;
|
|
}
|
|
|
|
|
|
void CV_ProjectPointsTest::run_func()
|
|
{
|
|
CvMat *v2m_jac = 0, *m2v_jac = 0;
|
|
if( calc_jacobians )
|
|
{
|
|
v2m_jac = &test_mat[OUTPUT][1];
|
|
m2v_jac = &test_mat[OUTPUT][3];
|
|
}
|
|
|
|
cvProjectPoints2( &test_mat[INPUT][0], &test_mat[OUTPUT][0], v2m_jac );
|
|
cvProjectPoints2( &test_mat[OUTPUT][0], &test_mat[OUTPUT][2], m2v_jac );
|
|
}
|
|
|
|
|
|
void CV_ProjectPointsTest::prepare_to_validation( int /*test_case_idx*/ )
|
|
{
|
|
const CvMat* vec = &test_mat[INPUT][0];
|
|
CvMat* m = &test_mat[REF_OUTPUT][0];
|
|
CvMat* vec2 = &test_mat[REF_OUTPUT][2];
|
|
CvMat* v2m_jac = 0, *m2v_jac = 0;
|
|
double theta0, theta1;
|
|
|
|
if( calc_jacobians )
|
|
{
|
|
v2m_jac = &test_mat[REF_OUTPUT][1];
|
|
m2v_jac = &test_mat[REF_OUTPUT][3];
|
|
}
|
|
|
|
|
|
cvTsProjectPoints( vec, m, v2m_jac );
|
|
cvTsProjectPoints( m, vec2, m2v_jac );
|
|
cvTsCopy( vec, vec2 );
|
|
|
|
theta0 = cvtest::norm( cvarrtomat(vec2), 0, NORM_L2 );
|
|
theta1 = fmod( theta0, CV_PI*2 );
|
|
|
|
if( theta1 > CV_PI )
|
|
theta1 = -(CV_PI*2 - theta1);
|
|
cvScale( vec2, vec2, theta1/(theta0 ? theta0 : 1) );
|
|
|
|
if( calc_jacobians )
|
|
{
|
|
//cvInvert( v2m_jac, m2v_jac, CV_SVD );
|
|
if( cvtest::norm(cvarrtomat(&test_mat[OUTPUT][3]), 0, NORM_INF) < 1000 )
|
|
{
|
|
cvTsGEMM( &test_mat[OUTPUT][1], &test_mat[OUTPUT][3],
|
|
1, 0, 0, &test_mat[OUTPUT][4],
|
|
v2m_jac->rows == 3 ? 0 : CV_GEMM_A_T + CV_GEMM_B_T );
|
|
}
|
|
else
|
|
{
|
|
cvTsSetIdentity( &test_mat[OUTPUT][4], cvScalarAll(1.) );
|
|
cvTsCopy( &test_mat[REF_OUTPUT][2], &test_mat[OUTPUT][2] );
|
|
}
|
|
cvTsSetIdentity( &test_mat[REF_OUTPUT][4], cvScalarAll(1.) );
|
|
}
|
|
}
|
|
|
|
CV_ProjectPointsTest ProjectPoints_test;
|
|
|
|
#endif
|
|
|
|
//----------------------------------------- CV_ProjectPointsTest --------------------------------
|
|
void calcdfdx( const vector<vector<Point2f> >& leftF, const vector<vector<Point2f> >& rightF, double eps, Mat& dfdx )
|
|
{
|
|
const int fdim = 2;
|
|
CV_Assert( !leftF.empty() && !rightF.empty() && !leftF[0].empty() && !rightF[0].empty() );
|
|
CV_Assert( leftF[0].size() == rightF[0].size() );
|
|
CV_Assert( fabs(eps) > std::numeric_limits<double>::epsilon() );
|
|
int fcount = (int)leftF[0].size(), xdim = (int)leftF.size();
|
|
|
|
dfdx.create( fcount*fdim, xdim, CV_64FC1 );
|
|
|
|
vector<vector<Point2f> >::const_iterator arrLeftIt = leftF.begin();
|
|
vector<vector<Point2f> >::const_iterator arrRightIt = rightF.begin();
|
|
for( int xi = 0; xi < xdim; xi++, ++arrLeftIt, ++arrRightIt )
|
|
{
|
|
CV_Assert( (int)arrLeftIt->size() == fcount );
|
|
CV_Assert( (int)arrRightIt->size() == fcount );
|
|
vector<Point2f>::const_iterator lIt = arrLeftIt->begin();
|
|
vector<Point2f>::const_iterator rIt = arrRightIt->begin();
|
|
for( int fi = 0; fi < dfdx.rows; fi+=fdim, ++lIt, ++rIt )
|
|
{
|
|
dfdx.at<double>(fi, xi ) = 0.5 * ((double)(rIt->x - lIt->x)) / eps;
|
|
dfdx.at<double>(fi+1, xi ) = 0.5 * ((double)(rIt->y - lIt->y)) / eps;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class CV_ProjectPointsTest : public cvtest::BaseTest
|
|
{
|
|
public:
|
|
CV_ProjectPointsTest() {}
|
|
protected:
|
|
void run(int);
|
|
virtual void project( const Mat& objectPoints,
|
|
const Mat& rvec, const Mat& tvec,
|
|
const Mat& cameraMatrix,
|
|
const Mat& distCoeffs,
|
|
vector<Point2f>& imagePoints,
|
|
Mat& dpdrot, Mat& dpdt, Mat& dpdf,
|
|
Mat& dpdc, Mat& dpddist,
|
|
double aspectRatio=0 ) = 0;
|
|
};
|
|
|
|
void CV_ProjectPointsTest::run(int)
|
|
{
|
|
//typedef float matType;
|
|
|
|
int code = cvtest::TS::OK;
|
|
const int pointCount = 100;
|
|
|
|
const float zMinVal = 10.0f, zMaxVal = 100.0f,
|
|
rMinVal = -0.3f, rMaxVal = 0.3f,
|
|
tMinVal = -2.0f, tMaxVal = 2.0f;
|
|
|
|
const float imgPointErr = 1e-3f,
|
|
dEps = 1e-3f;
|
|
|
|
double err;
|
|
|
|
Size imgSize( 600, 800 );
|
|
Mat_<float> objPoints( pointCount, 3), rvec( 1, 3), rmat, tvec( 1, 3 ), cameraMatrix( 3, 3 ), distCoeffs( 1, 4 ),
|
|
leftRvec, rightRvec, leftTvec, rightTvec, leftCameraMatrix, rightCameraMatrix, leftDistCoeffs, rightDistCoeffs;
|
|
|
|
RNG rng = ts->get_rng();
|
|
|
|
// generate data
|
|
cameraMatrix << 300.f, 0.f, imgSize.width/2.f,
|
|
0.f, 300.f, imgSize.height/2.f,
|
|
0.f, 0.f, 1.f;
|
|
distCoeffs << 0.1, 0.01, 0.001, 0.001;
|
|
|
|
rvec(0,0) = rng.uniform( rMinVal, rMaxVal );
|
|
rvec(0,1) = rng.uniform( rMinVal, rMaxVal );
|
|
rvec(0,2) = rng.uniform( rMinVal, rMaxVal );
|
|
rmat = cv::Mat_<float>::zeros(3, 3);
|
|
Rodrigues( rvec, rmat );
|
|
|
|
tvec(0,0) = rng.uniform( tMinVal, tMaxVal );
|
|
tvec(0,1) = rng.uniform( tMinVal, tMaxVal );
|
|
tvec(0,2) = rng.uniform( tMinVal, tMaxVal );
|
|
|
|
for( int y = 0; y < objPoints.rows; y++ )
|
|
{
|
|
Mat point(1, 3, CV_32FC1, objPoints.ptr(y) );
|
|
float z = rng.uniform( zMinVal, zMaxVal );
|
|
point.at<float>(0,2) = z;
|
|
point.at<float>(0,0) = (rng.uniform(2.f,(float)(imgSize.width-2)) - cameraMatrix(0,2)) / cameraMatrix(0,0) * z;
|
|
point.at<float>(0,1) = (rng.uniform(2.f,(float)(imgSize.height-2)) - cameraMatrix(1,2)) / cameraMatrix(1,1) * z;
|
|
point = (point - tvec) * rmat;
|
|
}
|
|
|
|
vector<Point2f> imgPoints;
|
|
vector<vector<Point2f> > leftImgPoints;
|
|
vector<vector<Point2f> > rightImgPoints;
|
|
Mat dpdrot, dpdt, dpdf, dpdc, dpddist,
|
|
valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist;
|
|
|
|
project( objPoints, rvec, tvec, cameraMatrix, distCoeffs,
|
|
imgPoints, dpdrot, dpdt, dpdf, dpdc, dpddist, 0 );
|
|
|
|
// calculate and check image points
|
|
CV_Assert( (int)imgPoints.size() == pointCount );
|
|
vector<Point2f>::const_iterator it = imgPoints.begin();
|
|
for( int i = 0; i < pointCount; i++, ++it )
|
|
{
|
|
Point3d p( objPoints(i,0), objPoints(i,1), objPoints(i,2) );
|
|
double z = p.x*rmat(2,0) + p.y*rmat(2,1) + p.z*rmat(2,2) + tvec(0,2),
|
|
x = (p.x*rmat(0,0) + p.y*rmat(0,1) + p.z*rmat(0,2) + tvec(0,0)) / z,
|
|
y = (p.x*rmat(1,0) + p.y*rmat(1,1) + p.z*rmat(1,2) + tvec(0,1)) / z,
|
|
r2 = x*x + y*y,
|
|
r4 = r2*r2;
|
|
Point2f validImgPoint;
|
|
double a1 = 2*x*y,
|
|
a2 = r2 + 2*x*x,
|
|
a3 = r2 + 2*y*y,
|
|
cdist = 1+distCoeffs(0,0)*r2+distCoeffs(0,1)*r4;
|
|
validImgPoint.x = static_cast<float>((double)cameraMatrix(0,0)*(x*cdist + (double)distCoeffs(0,2)*a1 + (double)distCoeffs(0,3)*a2)
|
|
+ (double)cameraMatrix(0,2));
|
|
validImgPoint.y = static_cast<float>((double)cameraMatrix(1,1)*(y*cdist + (double)distCoeffs(0,2)*a3 + distCoeffs(0,3)*a1)
|
|
+ (double)cameraMatrix(1,2));
|
|
|
|
if( fabs(it->x - validImgPoint.x) > imgPointErr ||
|
|
fabs(it->y - validImgPoint.y) > imgPointErr )
|
|
{
|
|
ts->printf( cvtest::TS::LOG, "bad image point\n" );
|
|
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
|
goto _exit_;
|
|
}
|
|
}
|
|
|
|
// check derivatives
|
|
// 1. rotation
|
|
leftImgPoints.resize(3);
|
|
rightImgPoints.resize(3);
|
|
for( int i = 0; i < 3; i++ )
|
|
{
|
|
rvec.copyTo( leftRvec ); leftRvec(0,i) -= dEps;
|
|
project( objPoints, leftRvec, tvec, cameraMatrix, distCoeffs,
|
|
leftImgPoints[i], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 );
|
|
rvec.copyTo( rightRvec ); rightRvec(0,i) += dEps;
|
|
project( objPoints, rightRvec, tvec, cameraMatrix, distCoeffs,
|
|
rightImgPoints[i], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 );
|
|
}
|
|
calcdfdx( leftImgPoints, rightImgPoints, dEps, valDpdrot );
|
|
err = cvtest::norm( dpdrot, valDpdrot, NORM_INF );
|
|
if( err > 3 )
|
|
{
|
|
ts->printf( cvtest::TS::LOG, "bad dpdrot: too big difference = %g\n", err );
|
|
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
|
}
|
|
|
|
// 2. translation
|
|
for( int i = 0; i < 3; i++ )
|
|
{
|
|
tvec.copyTo( leftTvec ); leftTvec(0,i) -= dEps;
|
|
project( objPoints, rvec, leftTvec, cameraMatrix, distCoeffs,
|
|
leftImgPoints[i], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 );
|
|
tvec.copyTo( rightTvec ); rightTvec(0,i) += dEps;
|
|
project( objPoints, rvec, rightTvec, cameraMatrix, distCoeffs,
|
|
rightImgPoints[i], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 );
|
|
}
|
|
calcdfdx( leftImgPoints, rightImgPoints, dEps, valDpdt );
|
|
if( cvtest::norm( dpdt, valDpdt, NORM_INF ) > 0.2 )
|
|
{
|
|
ts->printf( cvtest::TS::LOG, "bad dpdtvec\n" );
|
|
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
|
}
|
|
|
|
// 3. camera matrix
|
|
// 3.1. focus
|
|
leftImgPoints.resize(2);
|
|
rightImgPoints.resize(2);
|
|
cameraMatrix.copyTo( leftCameraMatrix ); leftCameraMatrix(0,0) -= dEps;
|
|
project( objPoints, rvec, tvec, leftCameraMatrix, distCoeffs,
|
|
leftImgPoints[0], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 );
|
|
cameraMatrix.copyTo( leftCameraMatrix ); leftCameraMatrix(1,1) -= dEps;
|
|
project( objPoints, rvec, tvec, leftCameraMatrix, distCoeffs,
|
|
leftImgPoints[1], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 );
|
|
cameraMatrix.copyTo( rightCameraMatrix ); rightCameraMatrix(0,0) += dEps;
|
|
project( objPoints, rvec, tvec, rightCameraMatrix, distCoeffs,
|
|
rightImgPoints[0], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 );
|
|
cameraMatrix.copyTo( rightCameraMatrix ); rightCameraMatrix(1,1) += dEps;
|
|
project( objPoints, rvec, tvec, rightCameraMatrix, distCoeffs,
|
|
rightImgPoints[1], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 );
|
|
calcdfdx( leftImgPoints, rightImgPoints, dEps, valDpdf );
|
|
if ( cvtest::norm( dpdf, valDpdf, NORM_L2 ) > 0.2 )
|
|
{
|
|
ts->printf( cvtest::TS::LOG, "bad dpdf\n" );
|
|
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
|
}
|
|
// 3.2. principal point
|
|
leftImgPoints.resize(2);
|
|
rightImgPoints.resize(2);
|
|
cameraMatrix.copyTo( leftCameraMatrix ); leftCameraMatrix(0,2) -= dEps;
|
|
project( objPoints, rvec, tvec, leftCameraMatrix, distCoeffs,
|
|
leftImgPoints[0], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 );
|
|
cameraMatrix.copyTo( leftCameraMatrix ); leftCameraMatrix(1,2) -= dEps;
|
|
project( objPoints, rvec, tvec, leftCameraMatrix, distCoeffs,
|
|
leftImgPoints[1], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 );
|
|
cameraMatrix.copyTo( rightCameraMatrix ); rightCameraMatrix(0,2) += dEps;
|
|
project( objPoints, rvec, tvec, rightCameraMatrix, distCoeffs,
|
|
rightImgPoints[0], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 );
|
|
cameraMatrix.copyTo( rightCameraMatrix ); rightCameraMatrix(1,2) += dEps;
|
|
project( objPoints, rvec, tvec, rightCameraMatrix, distCoeffs,
|
|
rightImgPoints[1], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 );
|
|
calcdfdx( leftImgPoints, rightImgPoints, dEps, valDpdc );
|
|
if ( cvtest::norm( dpdc, valDpdc, NORM_L2 ) > 0.2 )
|
|
{
|
|
ts->printf( cvtest::TS::LOG, "bad dpdc\n" );
|
|
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
|
}
|
|
|
|
// 4. distortion
|
|
leftImgPoints.resize(distCoeffs.cols);
|
|
rightImgPoints.resize(distCoeffs.cols);
|
|
for( int i = 0; i < distCoeffs.cols; i++ )
|
|
{
|
|
distCoeffs.copyTo( leftDistCoeffs ); leftDistCoeffs(0,i) -= dEps;
|
|
project( objPoints, rvec, tvec, cameraMatrix, leftDistCoeffs,
|
|
leftImgPoints[i], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 );
|
|
distCoeffs.copyTo( rightDistCoeffs ); rightDistCoeffs(0,i) += dEps;
|
|
project( objPoints, rvec, tvec, cameraMatrix, rightDistCoeffs,
|
|
rightImgPoints[i], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 );
|
|
}
|
|
calcdfdx( leftImgPoints, rightImgPoints, dEps, valDpddist );
|
|
if( cvtest::norm( dpddist, valDpddist, NORM_L2 ) > 0.3 )
|
|
{
|
|
ts->printf( cvtest::TS::LOG, "bad dpddist\n" );
|
|
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
|
}
|
|
|
|
_exit_:
|
|
RNG& _rng = ts->get_rng();
|
|
_rng = rng;
|
|
ts->set_failed_test_info( code );
|
|
}
|
|
|
|
//----------------------------------------- CV_ProjectPointsTest_CPP --------------------------------
|
|
class CV_ProjectPointsTest_CPP : public CV_ProjectPointsTest
|
|
{
|
|
public:
|
|
CV_ProjectPointsTest_CPP() {}
|
|
protected:
|
|
virtual void project( const Mat& objectPoints,
|
|
const Mat& rvec, const Mat& tvec,
|
|
const Mat& cameraMatrix,
|
|
const Mat& distCoeffs,
|
|
vector<Point2f>& imagePoints,
|
|
Mat& dpdrot, Mat& dpdt, Mat& dpdf,
|
|
Mat& dpdc, Mat& dpddist,
|
|
double aspectRatio=0 );
|
|
};
|
|
|
|
void CV_ProjectPointsTest_CPP::project( const Mat& objectPoints, const Mat& rvec, const Mat& tvec,
|
|
const Mat& cameraMatrix, const Mat& distCoeffs, vector<Point2f>& imagePoints,
|
|
Mat& dpdrot, Mat& dpdt, Mat& dpdf, Mat& dpdc, Mat& dpddist, double aspectRatio)
|
|
{
|
|
Mat J;
|
|
projectPoints( objectPoints, rvec, tvec, cameraMatrix, distCoeffs, imagePoints, J, aspectRatio);
|
|
J.colRange(0, 3).copyTo(dpdrot);
|
|
J.colRange(3, 6).copyTo(dpdt);
|
|
J.colRange(6, 8).copyTo(dpdf);
|
|
J.colRange(8, 10).copyTo(dpdc);
|
|
J.colRange(10, J.cols).copyTo(dpddist);
|
|
}
|
|
|
|
TEST(Calib3d_ProjectPoints_CPP, regression) { CV_ProjectPointsTest_CPP test; test.safe_run(); }
|
|
|
|
TEST(Calib3d_ProjectPoints_CPP, inputShape)
|
|
{
|
|
Matx31d rvec = Matx31d::zeros();
|
|
Matx31d tvec(0, 0, 1);
|
|
Matx33d cameraMatrix = Matx33d::eye();
|
|
const float L = 0.1f;
|
|
{
|
|
//3xN 1-channel
|
|
Mat objectPoints = (Mat_<float>(3, 2) << -L, L,
|
|
L, L,
|
|
0, 0);
|
|
vector<Point2f> imagePoints;
|
|
projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints);
|
|
EXPECT_EQ(objectPoints.cols, static_cast<int>(imagePoints.size()));
|
|
EXPECT_NEAR(imagePoints[0].x, -L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints[0].y, L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints[1].x, L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints[1].y, L, std::numeric_limits<float>::epsilon());
|
|
}
|
|
{
|
|
//Nx2 1-channel
|
|
Mat objectPoints = (Mat_<float>(2, 3) << -L, L, 0,
|
|
L, L, 0);
|
|
vector<Point2f> imagePoints;
|
|
projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints);
|
|
EXPECT_EQ(objectPoints.rows, static_cast<int>(imagePoints.size()));
|
|
EXPECT_NEAR(imagePoints[0].x, -L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints[0].y, L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints[1].x, L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints[1].y, L, std::numeric_limits<float>::epsilon());
|
|
}
|
|
{
|
|
//1xN 3-channel
|
|
Mat objectPoints(1, 2, CV_32FC3);
|
|
objectPoints.at<Vec3f>(0,0) = Vec3f(-L, L, 0);
|
|
objectPoints.at<Vec3f>(0,1) = Vec3f(L, L, 0);
|
|
|
|
vector<Point2f> imagePoints;
|
|
projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints);
|
|
EXPECT_EQ(objectPoints.cols, static_cast<int>(imagePoints.size()));
|
|
EXPECT_NEAR(imagePoints[0].x, -L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints[0].y, L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints[1].x, L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints[1].y, L, std::numeric_limits<float>::epsilon());
|
|
}
|
|
{
|
|
//Nx1 3-channel
|
|
Mat objectPoints(2, 1, CV_32FC3);
|
|
objectPoints.at<Vec3f>(0,0) = Vec3f(-L, L, 0);
|
|
objectPoints.at<Vec3f>(1,0) = Vec3f(L, L, 0);
|
|
|
|
vector<Point2f> imagePoints;
|
|
projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints);
|
|
EXPECT_EQ(objectPoints.rows, static_cast<int>(imagePoints.size()));
|
|
EXPECT_NEAR(imagePoints[0].x, -L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints[0].y, L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints[1].x, L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints[1].y, L, std::numeric_limits<float>::epsilon());
|
|
}
|
|
{
|
|
//vector<Point3f>
|
|
vector<Point3f> objectPoints;
|
|
objectPoints.push_back(Point3f(-L, L, 0));
|
|
objectPoints.push_back(Point3f(L, L, 0));
|
|
|
|
vector<Point2f> imagePoints;
|
|
projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints);
|
|
EXPECT_EQ(objectPoints.size(), imagePoints.size());
|
|
EXPECT_NEAR(imagePoints[0].x, -L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints[0].y, L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints[1].x, L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints[1].y, L, std::numeric_limits<float>::epsilon());
|
|
}
|
|
{
|
|
//vector<Point3d>
|
|
vector<Point3d> objectPoints;
|
|
objectPoints.push_back(Point3d(-L, L, 0));
|
|
objectPoints.push_back(Point3d(L, L, 0));
|
|
|
|
vector<Point2d> imagePoints;
|
|
projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints);
|
|
EXPECT_EQ(objectPoints.size(), imagePoints.size());
|
|
EXPECT_NEAR(imagePoints[0].x, -L, std::numeric_limits<double>::epsilon());
|
|
EXPECT_NEAR(imagePoints[0].y, L, std::numeric_limits<double>::epsilon());
|
|
EXPECT_NEAR(imagePoints[1].x, L, std::numeric_limits<double>::epsilon());
|
|
EXPECT_NEAR(imagePoints[1].y, L, std::numeric_limits<double>::epsilon());
|
|
}
|
|
}
|
|
|
|
TEST(Calib3d_ProjectPoints_CPP, outputShape)
|
|
{
|
|
Matx31d rvec = Matx31d::zeros();
|
|
Matx31d tvec(0, 0, 1);
|
|
Matx33d cameraMatrix = Matx33d::eye();
|
|
const float L = 0.1f;
|
|
{
|
|
vector<Point3f> objectPoints;
|
|
objectPoints.push_back(Point3f(-L, L, 0));
|
|
objectPoints.push_back(Point3f( L, L, 0));
|
|
objectPoints.push_back(Point3f( L, -L, 0));
|
|
|
|
//Mat --> will be Nx1 2-channel
|
|
Mat imagePoints;
|
|
projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints);
|
|
EXPECT_EQ(static_cast<int>(objectPoints.size()), imagePoints.rows);
|
|
EXPECT_NEAR(imagePoints.at<Vec2f>(0,0)(0), -L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints.at<Vec2f>(0,0)(1), L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints.at<Vec2f>(1,0)(0), L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints.at<Vec2f>(1,0)(1), L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints.at<Vec2f>(2,0)(0), L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints.at<Vec2f>(2,0)(1), -L, std::numeric_limits<float>::epsilon());
|
|
}
|
|
{
|
|
vector<Point3f> objectPoints;
|
|
objectPoints.push_back(Point3f(-L, L, 0));
|
|
objectPoints.push_back(Point3f( L, L, 0));
|
|
objectPoints.push_back(Point3f( L, -L, 0));
|
|
|
|
//Nx1 2-channel
|
|
Mat imagePoints(3,1,CV_32FC2);
|
|
projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints);
|
|
EXPECT_EQ(static_cast<int>(objectPoints.size()), imagePoints.rows);
|
|
EXPECT_NEAR(imagePoints.at<Vec2f>(0,0)(0), -L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints.at<Vec2f>(0,0)(1), L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints.at<Vec2f>(1,0)(0), L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints.at<Vec2f>(1,0)(1), L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints.at<Vec2f>(2,0)(0), L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints.at<Vec2f>(2,0)(1), -L, std::numeric_limits<float>::epsilon());
|
|
}
|
|
{
|
|
vector<Point3f> objectPoints;
|
|
objectPoints.push_back(Point3f(-L, L, 0));
|
|
objectPoints.push_back(Point3f( L, L, 0));
|
|
objectPoints.push_back(Point3f( L, -L, 0));
|
|
|
|
//1xN 2-channel
|
|
Mat imagePoints(1,3,CV_32FC2);
|
|
projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints);
|
|
EXPECT_EQ(static_cast<int>(objectPoints.size()), imagePoints.cols);
|
|
EXPECT_NEAR(imagePoints.at<Vec2f>(0,0)(0), -L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints.at<Vec2f>(0,0)(1), L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints.at<Vec2f>(0,1)(0), L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints.at<Vec2f>(0,1)(1), L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints.at<Vec2f>(0,2)(0), L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints.at<Vec2f>(0,2)(1), -L, std::numeric_limits<float>::epsilon());
|
|
}
|
|
{
|
|
vector<Point3f> objectPoints;
|
|
objectPoints.push_back(Point3f(-L, L, 0));
|
|
objectPoints.push_back(Point3f(L, L, 0));
|
|
|
|
//vector<Point2f>
|
|
vector<Point2f> imagePoints;
|
|
projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints);
|
|
EXPECT_EQ(objectPoints.size(), imagePoints.size());
|
|
EXPECT_NEAR(imagePoints[0].x, -L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints[0].y, L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints[1].x, L, std::numeric_limits<float>::epsilon());
|
|
EXPECT_NEAR(imagePoints[1].y, L, std::numeric_limits<float>::epsilon());
|
|
}
|
|
{
|
|
vector<Point3d> objectPoints;
|
|
objectPoints.push_back(Point3d(-L, L, 0));
|
|
objectPoints.push_back(Point3d(L, L, 0));
|
|
|
|
//vector<Point2d>
|
|
vector<Point2d> imagePoints;
|
|
projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints);
|
|
EXPECT_EQ(objectPoints.size(), imagePoints.size());
|
|
EXPECT_NEAR(imagePoints[0].x, -L, std::numeric_limits<double>::epsilon());
|
|
EXPECT_NEAR(imagePoints[0].y, L, std::numeric_limits<double>::epsilon());
|
|
EXPECT_NEAR(imagePoints[1].x, L, std::numeric_limits<double>::epsilon());
|
|
EXPECT_NEAR(imagePoints[1].y, L, std::numeric_limits<double>::epsilon());
|
|
}
|
|
}
|
|
|
|
#define NUM_DIST_COEFF_TILT 14
|
|
|
|
class cameraCalibrationTiltTest : public ::testing::Test {
|
|
|
|
protected:
|
|
|
|
static const cv::Size m_imageSize;
|
|
static const double m_pixelSize;
|
|
static const double m_circleConfusionPixel;
|
|
static const double m_lensFocalLength;
|
|
static const double m_lensFNumber;
|
|
static const double m_objectDistance;
|
|
static const double m_planeTiltDegree;
|
|
static const double m_pointTargetDist;
|
|
static const int m_pointTargetNum;
|
|
|
|
/** image distance corresponding to working distance */
|
|
double m_imageDistance;
|
|
/** image tilt angle corresponding to the tilt of the object plane */
|
|
double m_imageTiltDegree;
|
|
/** center of the field of view, near and far plane */
|
|
std::vector<cv::Vec3d> m_fovCenter;
|
|
/** normal of the field of view, near and far plane */
|
|
std::vector<cv::Vec3d> m_fovNormal;
|
|
/** points on a plane calibration target */
|
|
std::vector<cv::Point3d> m_pointTarget;
|
|
/** rotations for the calibration target */
|
|
std::vector<cv::Vec3d> m_pointTargetRvec;
|
|
/** translations for the calibration target */
|
|
std::vector<cv::Vec3d> m_pointTargetTvec;
|
|
/** camera matrix */
|
|
cv::Matx33d m_cameraMatrix;
|
|
/** distortion coefficients */
|
|
cv::Vec<double, NUM_DIST_COEFF_TILT> m_distortionCoeff;
|
|
|
|
/** random generator */
|
|
cv::RNG m_rng;
|
|
/** degree to radian conversion factor */
|
|
const double m_toRadian;
|
|
/** radian to degree conversion factor */
|
|
const double m_toDegree;
|
|
|
|
cameraCalibrationTiltTest()
|
|
: m_toRadian(acos(-1.0)/180.0)
|
|
, m_toDegree(180.0/acos(-1.0))
|
|
{}
|
|
|
|
/**
|
|
C *hanges given distortion coefficients randomly by adding
|
|
a uniformly distributed random variable in [-max max]
|
|
\param coeff input
|
|
\param max limits for the random variables
|
|
*/
|
|
void randomDistortionCoeff(
|
|
cv::Vec<double, NUM_DIST_COEFF_TILT>& coeff,
|
|
const cv::Vec<double, NUM_DIST_COEFF_TILT>& max)
|
|
{
|
|
for (int i = 0; i < coeff.rows; ++i)
|
|
coeff(i) += m_rng.uniform(-max(i), max(i));
|
|
}
|
|
|
|
void removeInvalidPoints(
|
|
std::vector<cv::Point2d>& imagePoints,
|
|
std::vector<cv::Point3d>& objectPoints)
|
|
{
|
|
// remove object and imgage points out of range
|
|
std::vector<cv::Point2d>::iterator itImg = imagePoints.begin();
|
|
std::vector<cv::Point3d>::iterator itObj = objectPoints.begin();
|
|
while (itImg != imagePoints.end())
|
|
{
|
|
bool ok =
|
|
itImg->x >= 0 &&
|
|
itImg->x <= m_imageSize.width - 1.0 &&
|
|
itImg->y >= 0 &&
|
|
itImg->y <= m_imageSize.height - 1.0;
|
|
if (ok)
|
|
{
|
|
++itImg;
|
|
++itObj;
|
|
}
|
|
else
|
|
{
|
|
itImg = imagePoints.erase(itImg);
|
|
itObj = objectPoints.erase(itObj);
|
|
}
|
|
}
|
|
}
|
|
|
|
void numericalDerivative(
|
|
cv::Mat& jac,
|
|
double eps,
|
|
const std::vector<cv::Point3d>& obj,
|
|
const cv::Vec3d& rvec,
|
|
const cv::Vec3d& tvec,
|
|
const cv::Matx33d& camera,
|
|
const cv::Vec<double, NUM_DIST_COEFF_TILT>& distor)
|
|
{
|
|
cv::Vec3d r(rvec);
|
|
cv::Vec3d t(tvec);
|
|
cv::Matx33d cm(camera);
|
|
cv::Vec<double, NUM_DIST_COEFF_TILT> dc(distor);
|
|
double* param[10+NUM_DIST_COEFF_TILT] = {
|
|
&r(0), &r(1), &r(2),
|
|
&t(0), &t(1), &t(2),
|
|
&cm(0,0), &cm(1,1), &cm(0,2), &cm(1,2),
|
|
&dc(0), &dc(1), &dc(2), &dc(3), &dc(4), &dc(5), &dc(6),
|
|
&dc(7), &dc(8), &dc(9), &dc(10), &dc(11), &dc(12), &dc(13)};
|
|
std::vector<cv::Point2d> pix0, pix1;
|
|
double invEps = .5/eps;
|
|
|
|
for (int col = 0; col < 10+NUM_DIST_COEFF_TILT; ++col)
|
|
{
|
|
double save = *(param[col]);
|
|
*(param[col]) = save + eps;
|
|
cv::projectPoints(obj, r, t, cm, dc, pix0);
|
|
*(param[col]) = save - eps;
|
|
cv::projectPoints(obj, r, t, cm, dc, pix1);
|
|
*(param[col]) = save;
|
|
|
|
std::vector<cv::Point2d>::const_iterator it0 = pix0.begin();
|
|
std::vector<cv::Point2d>::const_iterator it1 = pix1.begin();
|
|
int row = 0;
|
|
for (;it0 != pix0.end(); ++it0, ++it1)
|
|
{
|
|
cv::Point2d d = invEps*(*it0 - *it1);
|
|
jac.at<double>(row, col) = d.x;
|
|
++row;
|
|
jac.at<double>(row, col) = d.y;
|
|
++row;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
c *omputes for a given distance of an image or object point
|
|
the distance of the corresponding object or image point
|
|
*/
|
|
double opticalMap(double dist) {
|
|
return m_lensFocalLength*dist/(dist - m_lensFocalLength);
|
|
}
|
|
|
|
/** magnification of the optical map */
|
|
double magnification(double dist) {
|
|
return m_lensFocalLength/(dist - m_lensFocalLength);
|
|
}
|
|
|
|
void SetUp()
|
|
{
|
|
m_imageDistance = opticalMap(m_objectDistance);
|
|
m_imageTiltDegree = m_toDegree * atan2(
|
|
m_imageDistance * tan(m_toRadian * m_planeTiltDegree),
|
|
m_objectDistance);
|
|
// half sensor height
|
|
double tmp = .5 * (m_imageSize.height - 1) * m_pixelSize
|
|
* cos(m_toRadian * m_imageTiltDegree);
|
|
// y-Value of tilted sensor
|
|
double yImage[2] = {tmp, -tmp};
|
|
// change in z because of the tilt
|
|
tmp *= sin(m_toRadian * m_imageTiltDegree);
|
|
// z-values of the sensor lower and upper corner
|
|
double zImage[2] = {
|
|
m_imageDistance + tmp,
|
|
m_imageDistance - tmp};
|
|
// circle of confusion
|
|
double circleConfusion = m_circleConfusionPixel*m_pixelSize;
|
|
// aperture of the lense
|
|
double aperture = m_lensFocalLength/m_lensFNumber;
|
|
// near and far factor on the image side
|
|
double nearFarFactorImage[2] = {
|
|
aperture/(aperture - circleConfusion),
|
|
aperture/(aperture + circleConfusion)};
|
|
// on the object side - points that determine the field of
|
|
// view
|
|
std::vector<cv::Vec3d> fovBottomTop(6);
|
|
std::vector<cv::Vec3d>::iterator itFov = fovBottomTop.begin();
|
|
for (size_t iBottomTop = 0; iBottomTop < 2; ++iBottomTop)
|
|
{
|
|
// mapping sensor to field of view
|
|
*itFov = cv::Vec3d(0,yImage[iBottomTop],zImage[iBottomTop]);
|
|
*itFov *= magnification((*itFov)(2));
|
|
++itFov;
|
|
for (size_t iNearFar = 0; iNearFar < 2; ++iNearFar, ++itFov)
|
|
{
|
|
// scaling to the near and far distance on the
|
|
// image side
|
|
*itFov = cv::Vec3d(0,yImage[iBottomTop],zImage[iBottomTop]) *
|
|
nearFarFactorImage[iNearFar];
|
|
// scaling to the object side
|
|
*itFov *= magnification((*itFov)(2));
|
|
}
|
|
}
|
|
m_fovCenter.resize(3);
|
|
m_fovNormal.resize(3);
|
|
for (size_t i = 0; i < 3; ++i)
|
|
{
|
|
m_fovCenter[i] = .5*(fovBottomTop[i] + fovBottomTop[i+3]);
|
|
m_fovNormal[i] = fovBottomTop[i+3] - fovBottomTop[i];
|
|
m_fovNormal[i] = cv::normalize(m_fovNormal[i]);
|
|
m_fovNormal[i] = cv::Vec3d(
|
|
m_fovNormal[i](0),
|
|
-m_fovNormal[i](2),
|
|
m_fovNormal[i](1));
|
|
// one target position in each plane
|
|
m_pointTargetTvec.push_back(m_fovCenter[i]);
|
|
cv::Vec3d rvec = cv::Vec3d(0,0,1).cross(m_fovNormal[i]);
|
|
rvec = cv::normalize(rvec);
|
|
rvec *= acos(m_fovNormal[i](2));
|
|
m_pointTargetRvec.push_back(rvec);
|
|
}
|
|
// calibration target
|
|
size_t num = 2*m_pointTargetNum + 1;
|
|
m_pointTarget.resize(num*num);
|
|
std::vector<cv::Point3d>::iterator itTarget = m_pointTarget.begin();
|
|
for (int iY = -m_pointTargetNum; iY <= m_pointTargetNum; ++iY)
|
|
{
|
|
for (int iX = -m_pointTargetNum; iX <= m_pointTargetNum; ++iX, ++itTarget)
|
|
{
|
|
*itTarget = cv::Point3d(iX, iY, 0) * m_pointTargetDist;
|
|
}
|
|
}
|
|
// oblique target positions
|
|
// approximate distance to the near and far plane
|
|
double dist = std::max(
|
|
std::abs(m_fovNormal[0].dot(m_fovCenter[0] - m_fovCenter[1])),
|
|
std::abs(m_fovNormal[0].dot(m_fovCenter[0] - m_fovCenter[2])));
|
|
// maximal angle such that target border "reaches" near and far plane
|
|
double maxAngle = atan2(dist, m_pointTargetNum*m_pointTargetDist);
|
|
std::vector<double> angle;
|
|
angle.push_back(-maxAngle);
|
|
angle.push_back(maxAngle);
|
|
cv::Matx33d baseMatrix;
|
|
cv::Rodrigues(m_pointTargetRvec.front(), baseMatrix);
|
|
for (std::vector<double>::const_iterator itAngle = angle.begin(); itAngle != angle.end(); ++itAngle)
|
|
{
|
|
cv::Matx33d rmat;
|
|
for (int i = 0; i < 2; ++i)
|
|
{
|
|
cv::Vec3d rvec(0,0,0);
|
|
rvec(i) = *itAngle;
|
|
cv::Rodrigues(rvec, rmat);
|
|
rmat = baseMatrix*rmat;
|
|
cv::Rodrigues(rmat, rvec);
|
|
m_pointTargetTvec.push_back(m_fovCenter.front());
|
|
m_pointTargetRvec.push_back(rvec);
|
|
}
|
|
}
|
|
// camera matrix
|
|
double cx = .5 * (m_imageSize.width - 1);
|
|
double cy = .5 * (m_imageSize.height - 1);
|
|
double f = m_imageDistance/m_pixelSize;
|
|
m_cameraMatrix = cv::Matx33d(
|
|
f,0,cx,
|
|
0,f,cy,
|
|
0,0,1);
|
|
// distortion coefficients
|
|
m_distortionCoeff = cv::Vec<double, NUM_DIST_COEFF_TILT>::all(0);
|
|
// tauX
|
|
m_distortionCoeff(12) = -m_toRadian*m_imageTiltDegree;
|
|
}
|
|
};
|
|
|
|
/** Number of Pixel of the sensor */
|
|
const cv::Size cameraCalibrationTiltTest::m_imageSize(1600, 1200);
|
|
/** Size of a pixel in mm */
|
|
const double cameraCalibrationTiltTest::m_pixelSize(.005);
|
|
/** Diameter of the circle of confusion */
|
|
const double cameraCalibrationTiltTest::m_circleConfusionPixel(3);
|
|
/** Focal length of the lens */
|
|
const double cameraCalibrationTiltTest::m_lensFocalLength(16.4);
|
|
/** F-Number */
|
|
const double cameraCalibrationTiltTest::m_lensFNumber(8);
|
|
/** Working distance */
|
|
const double cameraCalibrationTiltTest::m_objectDistance(200);
|
|
/** Angle between optical axis and object plane normal */
|
|
const double cameraCalibrationTiltTest::m_planeTiltDegree(55);
|
|
/** the calibration target are points on a square grid with this side length */
|
|
const double cameraCalibrationTiltTest::m_pointTargetDist(5);
|
|
/** the calibration target has (2*n + 1) x (2*n + 1) points */
|
|
const int cameraCalibrationTiltTest::m_pointTargetNum(15);
|
|
|
|
TEST_F(cameraCalibrationTiltTest, projectPoints)
|
|
{
|
|
std::vector<cv::Point2d> imagePoints;
|
|
std::vector<cv::Point3d> objectPoints = m_pointTarget;
|
|
cv::Vec3d rvec = m_pointTargetRvec.front();
|
|
cv::Vec3d tvec = m_pointTargetTvec.front();
|
|
|
|
cv::Vec<double, NUM_DIST_COEFF_TILT> coeffNoiseHalfWidth(
|
|
.1, .1, // k1 k2
|
|
.01, .01, // p1 p2
|
|
.001, .001, .001, .001, // k3 k4 k5 k6
|
|
.001, .001, .001, .001, // s1 s2 s3 s4
|
|
.01, .01); // tauX tauY
|
|
for (size_t numTest = 0; numTest < 10; ++numTest)
|
|
{
|
|
// create random distortion coefficients
|
|
cv::Vec<double, NUM_DIST_COEFF_TILT> distortionCoeff = m_distortionCoeff;
|
|
randomDistortionCoeff(distortionCoeff, coeffNoiseHalfWidth);
|
|
|
|
// projection
|
|
cv::projectPoints(
|
|
objectPoints,
|
|
rvec,
|
|
tvec,
|
|
m_cameraMatrix,
|
|
distortionCoeff,
|
|
imagePoints);
|
|
|
|
// remove object and imgage points out of range
|
|
removeInvalidPoints(imagePoints, objectPoints);
|
|
|
|
int numPoints = (int)imagePoints.size();
|
|
int numParams = 10 + distortionCoeff.rows;
|
|
cv::Mat jacobian(2*numPoints, numParams, CV_64FC1);
|
|
|
|
// projection and jacobian
|
|
cv::projectPoints(
|
|
objectPoints,
|
|
rvec,
|
|
tvec,
|
|
m_cameraMatrix,
|
|
distortionCoeff,
|
|
imagePoints,
|
|
jacobian);
|
|
|
|
// numerical derivatives
|
|
cv::Mat numericJacobian(2*numPoints, numParams, CV_64FC1);
|
|
double eps = 1e-7;
|
|
numericalDerivative(
|
|
numericJacobian,
|
|
eps,
|
|
objectPoints,
|
|
rvec,
|
|
tvec,
|
|
m_cameraMatrix,
|
|
distortionCoeff);
|
|
|
|
#if 0
|
|
for (size_t row = 0; row < 2; ++row)
|
|
{
|
|
std::cout << "------ Row = " << row << " ------\n";
|
|
for (size_t i = 0; i < 10+NUM_DIST_COEFF_TILT; ++i)
|
|
{
|
|
std::cout << i
|
|
<< " jac = " << jacobian.at<double>(row,i)
|
|
<< " num = " << numericJacobian.at<double>(row,i)
|
|
<< " rel. diff = " << abs(numericJacobian.at<double>(row,i) - jacobian.at<double>(row,i))/abs(numericJacobian.at<double>(row,i))
|
|
<< "\n";
|
|
}
|
|
}
|
|
#endif
|
|
// relative difference for large values (rvec and tvec)
|
|
cv::Mat check = abs(jacobian(cv::Range::all(), cv::Range(0,6)) - numericJacobian(cv::Range::all(), cv::Range(0,6)))/
|
|
(1 + abs(jacobian(cv::Range::all(), cv::Range(0,6))));
|
|
double minVal, maxVal;
|
|
cv::minMaxIdx(check, &minVal, &maxVal);
|
|
EXPECT_LE(maxVal, .01);
|
|
// absolute difference for distortion and camera matrix
|
|
EXPECT_MAT_NEAR(jacobian(cv::Range::all(), cv::Range(6,numParams)), numericJacobian(cv::Range::all(), cv::Range(6,numParams)), 1e-5);
|
|
}
|
|
}
|
|
|
|
TEST_F(cameraCalibrationTiltTest, undistortPoints)
|
|
{
|
|
cv::Vec<double, NUM_DIST_COEFF_TILT> coeffNoiseHalfWidth(
|
|
.2, .1, // k1 k2
|
|
.01, .01, // p1 p2
|
|
.01, .01, .01, .01, // k3 k4 k5 k6
|
|
.001, .001, .001, .001, // s1 s2 s3 s4
|
|
.001, .001); // tauX tauY
|
|
double step = 99;
|
|
double toleranceBackProjection = 1e-5;
|
|
|
|
for (size_t numTest = 0; numTest < 10; ++numTest)
|
|
{
|
|
cv::Vec<double, NUM_DIST_COEFF_TILT> distortionCoeff = m_distortionCoeff;
|
|
randomDistortionCoeff(distortionCoeff, coeffNoiseHalfWidth);
|
|
|
|
// distorted points
|
|
std::vector<cv::Point2d> distorted;
|
|
for (double x = 0; x <= m_imageSize.width-1; x += step)
|
|
for (double y = 0; y <= m_imageSize.height-1; y += step)
|
|
distorted.push_back(cv::Point2d(x,y));
|
|
std::vector<cv::Point2d> normalizedUndistorted;
|
|
|
|
// undistort
|
|
cv::undistortPoints(distorted,
|
|
normalizedUndistorted,
|
|
m_cameraMatrix,
|
|
distortionCoeff);
|
|
|
|
// copy normalized points to 3D
|
|
std::vector<cv::Point3d> objectPoints;
|
|
for (std::vector<cv::Point2d>::const_iterator itPnt = normalizedUndistorted.begin();
|
|
itPnt != normalizedUndistorted.end(); ++itPnt)
|
|
objectPoints.push_back(cv::Point3d(itPnt->x, itPnt->y, 1));
|
|
|
|
// project
|
|
std::vector<cv::Point2d> imagePoints(objectPoints.size());
|
|
cv::projectPoints(objectPoints,
|
|
cv::Vec3d(0,0,0),
|
|
cv::Vec3d(0,0,0),
|
|
m_cameraMatrix,
|
|
distortionCoeff,
|
|
imagePoints);
|
|
|
|
EXPECT_MAT_NEAR(distorted, imagePoints, toleranceBackProjection);
|
|
}
|
|
}
|
|
|
|
TEST(Calib3d_Triangulate, accuracy)
|
|
{
|
|
// the testcase from http://code.opencv.org/issues/4334
|
|
{
|
|
double P1data[] = { 250, 0, 200, 0, 0, 250, 150, 0, 0, 0, 1, 0 };
|
|
double P2data[] = { 250, 0, 200, -250, 0, 250, 150, 0, 0, 0, 1, 0 };
|
|
Mat P1(3, 4, CV_64F, P1data), P2(3, 4, CV_64F, P2data);
|
|
|
|
float x1data[] = { 200.f, 0.f };
|
|
float x2data[] = { 170.f, 1.f };
|
|
float Xdata[] = { 0.f, -5.f, 25/3.f };
|
|
constexpr int num_points = 5;
|
|
Mat1f res0(num_points, 3);
|
|
for(int i = 0; i < num_points; ++i) {
|
|
for(int j = 0; j < 3; ++j) {
|
|
res0(i, j) = Xdata[j];
|
|
}
|
|
}
|
|
|
|
Mat res_, res;
|
|
|
|
for(int test : {0, 1}) {
|
|
if (test == 0) {
|
|
// 2xN and 1xN 2-channel.
|
|
Mat1f x1(2, num_points);
|
|
Mat2f x2(1, num_points);
|
|
for(int i = 0; i < 2; ++i) {
|
|
for(int j = 0; j < num_points; ++j) {
|
|
x1(i, j) = x1data[i];
|
|
x2(0, j)[i] = x2data[i];
|
|
}
|
|
}
|
|
triangulatePoints(P1, P2, x1, x2, res_);
|
|
} else {
|
|
// Array and vector.
|
|
std::array<cv::Point2f, num_points> x1;
|
|
x1.fill({x1data[0], x1data[1]});
|
|
std::vector<cv::Point2f> x2(num_points, {x2data[0], x2data[1]});
|
|
triangulatePoints(P1, P2, x1, x2, res_);
|
|
}
|
|
cv::transpose(res_, res_); // TODO cvtest (transpose doesn't support inplace)
|
|
convertPointsFromHomogeneous(res_, res);
|
|
res = res.reshape(1, num_points);
|
|
|
|
cout << "[1]:" << endl;
|
|
cout << "\tres0: " << res0 << endl;
|
|
cout << "\tres: " << res << endl;
|
|
|
|
ASSERT_LE(cvtest::norm(res, res0, NORM_INF), 1e-1);
|
|
}
|
|
}
|
|
|
|
// another testcase http://code.opencv.org/issues/3461
|
|
{
|
|
Matx33d K1(6137.147949, 0.000000, 644.974609,
|
|
0.000000, 6137.147949, 573.442749,
|
|
0.000000, 0.000000, 1.000000);
|
|
Matx33d K2(6137.147949, 0.000000, 644.674438,
|
|
0.000000, 6137.147949, 573.079834,
|
|
0.000000, 0.000000, 1.000000);
|
|
|
|
Matx34d RT1(1, 0, 0, 0,
|
|
0, 1, 0, 0,
|
|
0, 0, 1, 0);
|
|
|
|
Matx34d RT2(0.998297, 0.0064108, -0.0579766, 143.614334,
|
|
-0.0065818, 0.999975, -0.00275888, -5.160085,
|
|
0.0579574, 0.00313577, 0.998314, 96.066109);
|
|
|
|
Matx34d P1 = K1*RT1;
|
|
Matx34d P2 = K2*RT2;
|
|
|
|
float x1data[] = { 438.f, 19.f };
|
|
float x2data[] = { 452.363600f, 16.452225f };
|
|
float Xdata[] = { -81.049530f, -215.702804f, 2401.645449f };
|
|
Mat x1(2, 1, CV_32F, x1data);
|
|
Mat x2(2, 1, CV_32F, x2data);
|
|
Mat res0(1, 3, CV_32F, Xdata);
|
|
Mat res_, res;
|
|
|
|
triangulatePoints(P1, P2, x1, x2, res_);
|
|
cv::transpose(res_, res_); // TODO cvtest (transpose doesn't support inplace)
|
|
convertPointsFromHomogeneous(res_, res);
|
|
res = res.reshape(1, 1);
|
|
|
|
cout << "[2]:" << endl;
|
|
cout << "\tres0: " << res0 << endl;
|
|
cout << "\tres: " << res << endl;
|
|
|
|
ASSERT_LE(cvtest::norm(res, res0, NORM_INF), 2);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TEST(CV_RecoverPoseTest, regression_15341)
|
|
{
|
|
// initialize test data
|
|
const int invalid_point_count = 2;
|
|
const float _points1_[] = {
|
|
1537.7f, 166.8f,
|
|
1599.1f, 179.6f,
|
|
1288.0f, 207.5f,
|
|
1507.1f, 193.2f,
|
|
1742.7f, 210.0f,
|
|
1041.6f, 271.7f,
|
|
1591.8f, 247.2f,
|
|
1524.0f, 261.3f,
|
|
1330.3f, 285.0f,
|
|
1403.1f, 284.0f,
|
|
1506.6f, 342.9f,
|
|
1502.8f, 347.3f,
|
|
1344.9f, 364.9f,
|
|
0.0f, 0.0f // last point is initial invalid
|
|
};
|
|
|
|
const float _points2_[] = {
|
|
1533.4f, 532.9f,
|
|
1596.6f, 552.4f,
|
|
1277.0f, 556.4f,
|
|
1502.1f, 557.6f,
|
|
1744.4f, 601.3f,
|
|
1023.0f, 612.6f,
|
|
1589.2f, 621.6f,
|
|
1519.4f, 629.0f,
|
|
1320.3f, 637.3f,
|
|
1395.2f, 642.2f,
|
|
1501.5f, 710.3f,
|
|
1497.6f, 714.2f,
|
|
1335.1f, 719.61f,
|
|
1000.0f, 1000.0f // last point is initial invalid
|
|
};
|
|
|
|
vector<Point2f> _points1; Mat(14, 1, CV_32FC2, (void*)_points1_).copyTo(_points1);
|
|
vector<Point2f> _points2; Mat(14, 1, CV_32FC2, (void*)_points2_).copyTo(_points2);
|
|
|
|
const int point_count = (int) _points1.size();
|
|
CV_Assert(point_count == (int) _points2.size());
|
|
|
|
// camera matrix with both focal lengths = 1, and principal point = (0, 0)
|
|
const Mat cameraMatrix = Mat::eye(3, 3, CV_64F);
|
|
|
|
// camera matrix with focal lengths 0.5 and 0.6 respectively and principal point = (100, 200)
|
|
double cameraMatrix2Data[] = { 0.5, 0, 100,
|
|
0, 0.6, 200,
|
|
0, 0, 1 };
|
|
const Mat cameraMatrix2( 3, 3, CV_64F, cameraMatrix2Data );
|
|
|
|
// zero and nonzero distortion coefficients
|
|
double nonZeroDistCoeffsData[] = { 0.01, 0.0001, 0, 0, 1e-04, 0.2, 0.02, 0.0002 }; // k1, k2, p1, p2, k3, k4, k5, k6
|
|
vector<Mat> distCoeffsList = {Mat::zeros(1, 5, CV_64F), Mat{1, 8, CV_64F, nonZeroDistCoeffsData}};
|
|
const auto &zeroDistCoeffs = distCoeffsList[0];
|
|
|
|
int Inliers = 0;
|
|
|
|
const int ntests = 3;
|
|
for (int testcase = 1; testcase <= ntests; ++testcase)
|
|
{
|
|
if (testcase == 1) // testcase with vector input data
|
|
{
|
|
// init temporary test data
|
|
vector<unsigned char> mask(point_count);
|
|
vector<Point2f> points1(_points1);
|
|
vector<Point2f> points2(_points2);
|
|
|
|
// Estimation of fundamental matrix using the RANSAC algorithm
|
|
Mat E, E2, R, t;
|
|
|
|
// Check pose when camera matrices are different.
|
|
for (const auto &distCoeffs: distCoeffsList)
|
|
{
|
|
E = findEssentialMat(points1, points2, cameraMatrix, distCoeffs, cameraMatrix2, distCoeffs, RANSAC, 0.999, 1.0, mask);
|
|
recoverPose(points1, points2, cameraMatrix, distCoeffs, cameraMatrix2, distCoeffs, E2, R, t, RANSAC, 0.999, 1.0, mask);
|
|
EXPECT_LT(cv::norm(E, E2, NORM_INF), 1e-4) <<
|
|
"Two big difference between the same essential matrices computed using different functions with different cameras, testcase " << testcase;
|
|
EXPECT_EQ(0, (int)mask[13]) << "Detecting outliers in function failed with different cameras, testcase " << testcase;
|
|
}
|
|
|
|
// Check pose when camera matrices are the same.
|
|
E = findEssentialMat(points1, points2, cameraMatrix, RANSAC, 0.999, 1.0, 1000/*maxIters*/, mask);
|
|
E2 = findEssentialMat(points1, points2, cameraMatrix, zeroDistCoeffs, cameraMatrix, zeroDistCoeffs, RANSAC, 0.999, 1.0, mask);
|
|
EXPECT_LT(cv::norm(E, E2, NORM_INF), 1e-4) <<
|
|
"Two big difference between the same essential matrices computed using different functions with same cameras, testcase " << testcase;
|
|
EXPECT_EQ(0, (int)mask[13]) << "Detecting outliers in function findEssentialMat failed with same cameras, testcase " << testcase;
|
|
points2[12] = Point2f(0.0f, 0.0f); // provoke another outlier detection for recover Pose
|
|
Inliers = recoverPose(E, points1, points2, cameraMatrix, R, t, mask);
|
|
EXPECT_EQ(0, (int)mask[12]) << "Detecting outliers in function failed with same cameras, testcase " << testcase;
|
|
}
|
|
else // testcase with mat input data
|
|
{
|
|
Mat points1(_points1, true);
|
|
Mat points2(_points2, true);
|
|
Mat mask;
|
|
|
|
if (testcase == 2)
|
|
{
|
|
// init temporary testdata
|
|
mask = Mat::zeros(point_count, 1, CV_8UC1);
|
|
}
|
|
else // testcase == 3 - with transposed mask
|
|
{
|
|
mask = Mat::zeros(1, point_count, CV_8UC1);
|
|
}
|
|
|
|
// Estimation of fundamental matrix using the RANSAC algorithm
|
|
Mat E, E2, R, t;
|
|
|
|
// Check pose when camera matrices are different.
|
|
for (const auto &distCoeffs: distCoeffsList)
|
|
{
|
|
E = findEssentialMat(points1, points2, cameraMatrix, distCoeffs, cameraMatrix2, distCoeffs, RANSAC, 0.999, 1.0, mask);
|
|
recoverPose(points1, points2, cameraMatrix, distCoeffs, cameraMatrix2, distCoeffs, E2, R, t, RANSAC, 0.999, 1.0, mask);
|
|
EXPECT_LT(cv::norm(E, E2, NORM_INF), 1e-4) <<
|
|
"Two big difference between the same essential matrices computed using different functions with different cameras, testcase " << testcase;
|
|
EXPECT_EQ(0, (int)mask.at<unsigned char>(13)) << "Detecting outliers in function failed with different cameras, testcase " << testcase;
|
|
}
|
|
|
|
// Check pose when camera matrices are the same.
|
|
E = findEssentialMat(points1, points2, cameraMatrix, RANSAC, 0.999, 1.0, 1000/*maxIters*/, mask);
|
|
E2 = findEssentialMat(points1, points2, cameraMatrix, zeroDistCoeffs, cameraMatrix, zeroDistCoeffs, RANSAC, 0.999, 1.0, mask);
|
|
EXPECT_LT(cv::norm(E, E2, NORM_INF), 1e-4) <<
|
|
"Two big difference between the same essential matrices computed using different functions with same cameras, testcase " << testcase;
|
|
EXPECT_EQ(0, (int)mask.at<unsigned char>(13)) << "Detecting outliers in function findEssentialMat failed with same cameras, testcase " << testcase;
|
|
points2.at<Point2f>(12) = Point2f(0.0f, 0.0f); // provoke an outlier detection
|
|
Inliers = recoverPose(E, points1, points2, cameraMatrix, R, t, mask);
|
|
EXPECT_EQ(0, (int)mask.at<unsigned char>(12)) << "Detecting outliers in function failed with same cameras, testcase " << testcase;
|
|
}
|
|
EXPECT_EQ(Inliers, point_count - invalid_point_count) <<
|
|
"Number of inliers differs from expected number of inliers, testcase " << testcase;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------- CV_CalibrationMatrixValuesTest --------------------------------
|
|
|
|
class CV_CalibrationMatrixValuesTest : public cvtest::BaseTest
|
|
{
|
|
public:
|
|
CV_CalibrationMatrixValuesTest() {}
|
|
protected:
|
|
void run(int);
|
|
virtual void calibMatrixValues( const Mat& cameraMatrix, Size imageSize,
|
|
double apertureWidth, double apertureHeight, double& fovx, double& fovy, double& focalLength,
|
|
Point2d& principalPoint, double& aspectRatio ) = 0;
|
|
};
|
|
|
|
void CV_CalibrationMatrixValuesTest::run(int)
|
|
{
|
|
int code = cvtest::TS::OK;
|
|
const double fcMinVal = 1e-5;
|
|
const double fcMaxVal = 1000;
|
|
const double apertureMaxVal = 0.01;
|
|
|
|
RNG rng = ts->get_rng();
|
|
|
|
double fx, fy, cx, cy, nx, ny;
|
|
Mat cameraMatrix( 3, 3, CV_64FC1 );
|
|
cameraMatrix.setTo( Scalar(0) );
|
|
fx = cameraMatrix.at<double>(0,0) = rng.uniform( fcMinVal, fcMaxVal );
|
|
fy = cameraMatrix.at<double>(1,1) = rng.uniform( fcMinVal, fcMaxVal );
|
|
cx = cameraMatrix.at<double>(0,2) = rng.uniform( fcMinVal, fcMaxVal );
|
|
cy = cameraMatrix.at<double>(1,2) = rng.uniform( fcMinVal, fcMaxVal );
|
|
cameraMatrix.at<double>(2,2) = 1;
|
|
|
|
Size imageSize( 600, 400 );
|
|
|
|
double apertureWidth = (double)rng * apertureMaxVal,
|
|
apertureHeight = (double)rng * apertureMaxVal;
|
|
|
|
double fovx, fovy, focalLength, aspectRatio,
|
|
goodFovx, goodFovy, goodFocalLength, goodAspectRatio;
|
|
Point2d principalPoint, goodPrincipalPoint;
|
|
|
|
|
|
calibMatrixValues( cameraMatrix, imageSize, apertureWidth, apertureHeight,
|
|
fovx, fovy, focalLength, principalPoint, aspectRatio );
|
|
|
|
// calculate calibration matrix values
|
|
goodAspectRatio = fy / fx;
|
|
|
|
if( apertureWidth != 0.0 && apertureHeight != 0.0 )
|
|
{
|
|
nx = imageSize.width / apertureWidth;
|
|
ny = imageSize.height / apertureHeight;
|
|
}
|
|
else
|
|
{
|
|
nx = 1.0;
|
|
ny = goodAspectRatio;
|
|
}
|
|
|
|
goodFovx = (atan2(cx, fx) + atan2(imageSize.width - cx, fx)) * 180.0 / CV_PI;
|
|
goodFovy = (atan2(cy, fy) + atan2(imageSize.height - cy, fy)) * 180.0 / CV_PI;
|
|
|
|
goodFocalLength = fx / nx;
|
|
|
|
goodPrincipalPoint.x = cx / nx;
|
|
goodPrincipalPoint.y = cy / ny;
|
|
|
|
// check results
|
|
if( fabs(fovx - goodFovx) > FLT_EPSILON )
|
|
{
|
|
ts->printf( cvtest::TS::LOG, "bad fovx (real=%f, good = %f\n", fovx, goodFovx );
|
|
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
|
goto _exit_;
|
|
}
|
|
if( fabs(fovy - goodFovy) > FLT_EPSILON )
|
|
{
|
|
ts->printf( cvtest::TS::LOG, "bad fovy (real=%f, good = %f\n", fovy, goodFovy );
|
|
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
|
goto _exit_;
|
|
}
|
|
if( fabs(focalLength - goodFocalLength) > FLT_EPSILON )
|
|
{
|
|
ts->printf( cvtest::TS::LOG, "bad focalLength (real=%f, good = %f\n", focalLength, goodFocalLength );
|
|
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
|
goto _exit_;
|
|
}
|
|
if( fabs(aspectRatio - goodAspectRatio) > FLT_EPSILON )
|
|
{
|
|
ts->printf( cvtest::TS::LOG, "bad aspectRatio (real=%f, good = %f\n", aspectRatio, goodAspectRatio );
|
|
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
|
goto _exit_;
|
|
}
|
|
if( cv::norm(principalPoint - goodPrincipalPoint) > FLT_EPSILON ) // Point2d
|
|
{
|
|
ts->printf( cvtest::TS::LOG, "bad principalPoint\n" );
|
|
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
|
goto _exit_;
|
|
}
|
|
|
|
_exit_:
|
|
RNG& _rng = ts->get_rng();
|
|
_rng = rng;
|
|
ts->set_failed_test_info( code );
|
|
}
|
|
|
|
//----------------------------------------- CV_CalibrationMatrixValuesTest_CPP --------------------------------
|
|
|
|
class CV_CalibrationMatrixValuesTest_CPP : public CV_CalibrationMatrixValuesTest
|
|
{
|
|
public:
|
|
CV_CalibrationMatrixValuesTest_CPP() {}
|
|
protected:
|
|
virtual void calibMatrixValues( const Mat& cameraMatrix, Size imageSize,
|
|
double apertureWidth, double apertureHeight, double& fovx, double& fovy, double& focalLength,
|
|
Point2d& principalPoint, double& aspectRatio );
|
|
};
|
|
|
|
void CV_CalibrationMatrixValuesTest_CPP::calibMatrixValues( const Mat& cameraMatrix, Size imageSize,
|
|
double apertureWidth, double apertureHeight,
|
|
double& fovx, double& fovy, double& focalLength,
|
|
Point2d& principalPoint, double& aspectRatio )
|
|
{
|
|
calibrationMatrixValues( cameraMatrix, imageSize, apertureWidth, apertureHeight,
|
|
fovx, fovy, focalLength, principalPoint, aspectRatio );
|
|
}
|
|
|
|
TEST(Calib3d_CalibrationMatrixValues_CPP, accuracy) { CV_CalibrationMatrixValuesTest_CPP test; test.safe_run(); }
|
|
|
|
}} // namespace
|