Skip to content

Commit 8088d67

Browse files
MathLensalalek
authored andcommitted
Merge pull request opencv#8585 from tonyke1993:ap3p
Enable p3p and ap3p in solvePnPRansac (opencv#8585) * add paper info * allow p3p and ap3p being RANSAC kernel * keep previous code * apply catrees comment * fix getMat * add comment * add solvep3p test * test return value * fix warnings
1 parent dcf3d98 commit 8088d67

File tree

7 files changed

+260
-7
lines changed

7 files changed

+260
-7
lines changed

modules/calib3d/include/opencv2/calib3d.hpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,9 @@ F.Moreno-Noguer. "Exhaustive Linearization for Robust Camera Pose and Focal Leng
561561
Estimation" (@cite penate2013exhaustive). In this case the function also estimates the parameters \f$f_x\f$ and \f$f_y\f$
562562
assuming that both have the same value. Then the cameraMatrix is updated with the estimated
563563
focal length.
564+
- **SOLVEPNP_AP3P** Method is based on the paper of Tong Ke and Stergios I. Roumeliotis.
565+
"An Efficient Algebraic Solution to the Perspective-Three-Point Problem". In this case the
566+
function requires exactly four object and image points.
564567
565568
The function estimates the object pose given a set of object points, their corresponding image
566569
projections, as well as the camera matrix and the distortion coefficients.
@@ -631,6 +634,33 @@ CV_EXPORTS_W bool solvePnPRansac( InputArray objectPoints, InputArray imagePoint
631634
bool useExtrinsicGuess = false, int iterationsCount = 100,
632635
float reprojectionError = 8.0, double confidence = 0.99,
633636
OutputArray inliers = noArray(), int flags = SOLVEPNP_ITERATIVE );
637+
/** @brief Finds an object pose from 3 3D-2D point correspondences.
638+
639+
@param objectPoints Array of object points in the object coordinate space, 3x3 1-channel or
640+
1x3/3x1 3-channel. vector\<Point3f\> can be also passed here.
641+
@param imagePoints Array of corresponding image points, 3x2 1-channel or 1x3/3x1 2-channel.
642+
vector\<Point2f\> can be also passed here.
643+
@param cameraMatrix Input camera matrix \f$A = \vecthreethree{fx}{0}{cx}{0}{fy}{cy}{0}{0}{1}\f$ .
644+
@param distCoeffs Input vector of distortion coefficients
645+
\f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6 [, s_1, s_2, s_3, s_4[, \tau_x, \tau_y]]]])\f$ of
646+
4, 5, 8, 12 or 14 elements. If the vector is NULL/empty, the zero distortion coefficients are
647+
assumed.
648+
@param rvecs Output rotation vectors (see Rodrigues ) that, together with tvecs , brings points from
649+
the model coordinate system to the camera coordinate system. A P3P problem has up to 4 solutions.
650+
@param tvecs Output translation vectors.
651+
@param flags Method for solving a P3P problem:
652+
- **SOLVEPNP_P3P** Method is based on the paper of X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang
653+
"Complete Solution Classification for the Perspective-Three-Point Problem".
654+
- **SOLVEPNP_AP3P** Method is based on the paper of Tong Ke and Stergios I. Roumeliotis.
655+
"An Efficient Algebraic Solution to the Perspective-Three-Point Problem".
656+
657+
The function estimates the object pose given 3 object points, their corresponding image
658+
projections, as well as the camera matrix and the distortion coefficients.
659+
*/
660+
CV_EXPORTS_W int solveP3P( InputArray objectPoints, InputArray imagePoints,
661+
InputArray cameraMatrix, InputArray distCoeffs,
662+
OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs,
663+
int flags );
634664

635665
/** @brief Finds an initial camera matrix from 3D-2D point correspondences.
636666

modules/calib3d/src/ap3p.cpp

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,38 @@ bool ap3p::solve(cv::Mat &R, cv::Mat &tvec, const cv::Mat &opoints, const cv::Ma
313313
return result;
314314
}
315315

316+
int ap3p::solve(std::vector<cv::Mat> &Rs, std::vector<cv::Mat> &tvecs, const cv::Mat &opoints, const cv::Mat &ipoints) {
317+
CV_INSTRUMENT_REGION()
318+
319+
double rotation_matrix[4][3][3], translation[4][3];
320+
std::vector<double> points;
321+
if (opoints.depth() == ipoints.depth()) {
322+
if (opoints.depth() == CV_32F)
323+
extract_points<cv::Point3f, cv::Point2f>(opoints, ipoints, points);
324+
else
325+
extract_points<cv::Point3d, cv::Point2d>(opoints, ipoints, points);
326+
} else if (opoints.depth() == CV_32F)
327+
extract_points<cv::Point3f, cv::Point2d>(opoints, ipoints, points);
328+
else
329+
extract_points<cv::Point3d, cv::Point2f>(opoints, ipoints, points);
330+
331+
int solutions = solve(rotation_matrix, translation,
332+
points[0], points[1], points[2], points[3], points[4],
333+
points[5], points[6], points[7], points[8], points[9],
334+
points[10], points[11], points[12], points[13], points[14]);
335+
336+
for (int i = 0; i < solutions; i++) {
337+
cv::Mat R, tvec;
338+
cv::Mat(3, 1, CV_64F, translation[i]).copyTo(tvec);
339+
cv::Mat(3, 3, CV_64F, rotation_matrix[i]).copyTo(R);
340+
341+
Rs.push_back(R);
342+
tvecs.push_back(tvec);
343+
}
344+
345+
return solutions;
346+
}
347+
316348
bool
317349
ap3p::solve(double R[3][3], double t[3], double mu0, double mv0, double X0, double Y0, double Z0, double mu1,
318350
double mv1,
@@ -383,4 +415,4 @@ int ap3p::solve(double R[4][3][3], double t[4][3], double mu0, double mv0, doubl
383415

384416
return computePoses(featureVectors, worldPoints, R, t);
385417
}
386-
}
418+
}

modules/calib3d/src/ap3p.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ class ap3p {
1717
template<typename OpointType, typename IpointType>
1818
void extract_points(const cv::Mat &opoints, const cv::Mat &ipoints, std::vector<double> &points) {
1919
points.clear();
20-
points.resize(20);
21-
for (int i = 0; i < 4; i++) {
20+
int npoints = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F));
21+
points.resize(5*npoints);
22+
for (int i = 0; i < npoints; i++) {
2223
points[i * 5] = ipoints.at<IpointType>(i).x * fx + cx;
2324
points[i * 5 + 1] = ipoints.at<IpointType>(i).y * fy + cy;
2425
points[i * 5 + 2] = opoints.at<OpointType>(i).x;
@@ -39,6 +40,7 @@ class ap3p {
3940
ap3p(cv::Mat cameraMatrix);
4041

4142
bool solve(cv::Mat &R, cv::Mat &tvec, const cv::Mat &opoints, const cv::Mat &ipoints);
43+
int solve(std::vector<cv::Mat> &Rs, std::vector<cv::Mat> &tvecs, const cv::Mat &opoints, const cv::Mat &ipoints);
4244

4345
int solve(double R[4][3][3], double t[4][3],
4446
double mu0, double mv0, double X0, double Y0, double Z0,

modules/calib3d/src/p3p.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,41 @@ bool p3p::solve(cv::Mat& R, cv::Mat& tvec, const cv::Mat& opoints, const cv::Mat
5757
return result;
5858
}
5959

60+
int p3p::solve(std::vector<cv::Mat>& Rs, std::vector<cv::Mat>& tvecs, const cv::Mat& opoints, const cv::Mat& ipoints)
61+
{
62+
CV_INSTRUMENT_REGION()
63+
64+
double rotation_matrix[4][3][3], translation[4][3];
65+
std::vector<double> points;
66+
if (opoints.depth() == ipoints.depth())
67+
{
68+
if (opoints.depth() == CV_32F)
69+
extract_points<cv::Point3f,cv::Point2f>(opoints, ipoints, points);
70+
else
71+
extract_points<cv::Point3d,cv::Point2d>(opoints, ipoints, points);
72+
}
73+
else if (opoints.depth() == CV_32F)
74+
extract_points<cv::Point3f,cv::Point2d>(opoints, ipoints, points);
75+
else
76+
extract_points<cv::Point3d,cv::Point2f>(opoints, ipoints, points);
77+
78+
int solutions = solve(rotation_matrix, translation,
79+
points[0], points[1], points[2], points[3], points[4],
80+
points[5], points[6], points[7], points[8], points[9],
81+
points[10], points[11], points[12], points[13], points[14]);
82+
83+
for (int i = 0; i < solutions; i++) {
84+
cv::Mat R, tvec;
85+
cv::Mat(3, 1, CV_64F, translation[i]).copyTo(tvec);
86+
cv::Mat(3, 3, CV_64F, rotation_matrix[i]).copyTo(R);
87+
88+
Rs.push_back(R);
89+
tvecs.push_back(tvec);
90+
}
91+
92+
return solutions;
93+
}
94+
6095
bool p3p::solve(double R[3][3], double t[3],
6196
double mu0, double mv0, double X0, double Y0, double Z0,
6297
double mu1, double mv1, double X1, double Y1, double Z1,

modules/calib3d/src/p3p.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class p3p
1111
p3p(cv::Mat cameraMatrix);
1212

1313
bool solve(cv::Mat& R, cv::Mat& tvec, const cv::Mat& opoints, const cv::Mat& ipoints);
14+
int solve(std::vector<cv::Mat>& Rs, std::vector<cv::Mat>& tvecs, const cv::Mat& opoints, const cv::Mat& ipoints);
1415
int solve(double R[4][3][3], double t[4][3],
1516
double mu0, double mv0, double X0, double Y0, double Z0,
1617
double mu1, double mv1, double X1, double Y1, double Z1,
@@ -34,8 +35,9 @@ class p3p
3435
void extract_points(const cv::Mat& opoints, const cv::Mat& ipoints, std::vector<double>& points)
3536
{
3637
points.clear();
37-
points.resize(20);
38-
for(int i = 0; i < 4; i++)
38+
int npoints = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F));
39+
points.resize(5*npoints);
40+
for(int i = 0; i < npoints; i++)
3941
{
4042
points[i*5] = ipoints.at<IpointType>(i).x*fx + cx;
4143
points[i*5+1] = ipoints.at<IpointType>(i).y*fy + cy;

modules/calib3d/src/solvepnp.cpp

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,12 @@ bool solvePnPRansac(InputArray _opoints, InputArray _ipoints,
268268
int model_points = 5;
269269
int ransac_kernel_method = SOLVEPNP_EPNP;
270270

271-
if( npoints == 4 )
271+
if( flags == SOLVEPNP_P3P || flags == SOLVEPNP_AP3P)
272+
{
273+
model_points = 4;
274+
ransac_kernel_method = flags;
275+
}
276+
else if( npoints == 4 )
272277
{
273278
model_points = 4;
274279
ransac_kernel_method = SOLVEPNP_P3P;
@@ -337,4 +342,57 @@ bool solvePnPRansac(InputArray _opoints, InputArray _ipoints,
337342
return true;
338343
}
339344

345+
int solveP3P( InputArray _opoints, InputArray _ipoints,
346+
InputArray _cameraMatrix, InputArray _distCoeffs,
347+
OutputArrayOfArrays _rvecs, OutputArrayOfArrays _tvecs, int flags) {
348+
CV_INSTRUMENT_REGION()
349+
350+
Mat opoints = _opoints.getMat(), ipoints = _ipoints.getMat();
351+
int npoints = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F));
352+
CV_Assert( npoints == 3 && npoints == std::max(ipoints.checkVector(2, CV_32F), ipoints.checkVector(2, CV_64F)) );
353+
CV_Assert( flags == SOLVEPNP_P3P || flags == SOLVEPNP_AP3P );
354+
355+
Mat cameraMatrix0 = _cameraMatrix.getMat();
356+
Mat distCoeffs0 = _distCoeffs.getMat();
357+
Mat cameraMatrix = Mat_<double>(cameraMatrix0);
358+
Mat distCoeffs = Mat_<double>(distCoeffs0);
359+
360+
Mat undistortedPoints;
361+
undistortPoints(ipoints, undistortedPoints, cameraMatrix, distCoeffs);
362+
std::vector<Mat> Rs, ts;
363+
364+
int solutions = 0;
365+
if (flags == SOLVEPNP_P3P)
366+
{
367+
p3p P3Psolver(cameraMatrix);
368+
solutions = P3Psolver.solve(Rs, ts, opoints, undistortedPoints);
369+
}
370+
else if (flags == SOLVEPNP_AP3P)
371+
{
372+
ap3p P3Psolver(cameraMatrix);
373+
solutions = P3Psolver.solve(Rs, ts, opoints, undistortedPoints);
374+
}
375+
376+
if (solutions == 0) {
377+
return 0;
378+
}
379+
380+
if (_rvecs.needed()) {
381+
_rvecs.create(solutions, 1, CV_64F);
382+
}
383+
384+
if (_tvecs.needed()) {
385+
_tvecs.create(solutions, 1, CV_64F);
386+
}
387+
388+
for (int i = 0; i < solutions; i++) {
389+
Mat rvec;
390+
Rodrigues(Rs[i], rvec);
391+
_tvecs.getMatRef(i) = ts[i];
392+
_rvecs.getMatRef(i) = rvec;
393+
}
394+
395+
return solutions;
396+
}
397+
340398
}

modules/calib3d/test/test_solvepnp_ransac.cpp

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ class CV_solvePnPRansac_Test : public cvtest::BaseTest
153153
return isTestSuccess;
154154
}
155155

156-
void run(int)
156+
virtual void run(int)
157157
{
158158
ts->set_failed_test_info(cvtest::TS::OK);
159159

@@ -253,6 +253,100 @@ class CV_solvePnP_Test : public CV_solvePnPRansac_Test
253253
}
254254
};
255255

256+
class CV_solveP3P_Test : public CV_solvePnPRansac_Test
257+
{
258+
public:
259+
CV_solveP3P_Test()
260+
{
261+
eps[SOLVEPNP_P3P] = 1.0e-4;
262+
eps[SOLVEPNP_AP3P] = 1.0e-4;
263+
totalTestsCount = 1000;
264+
}
265+
266+
~CV_solveP3P_Test() {}
267+
protected:
268+
virtual bool runTest(RNG& rng, int mode, int method, const vector<Point3f>& points, const double* epsilon, double& maxError)
269+
{
270+
std::vector<Mat> rvecs, tvecs;
271+
Mat trueRvec, trueTvec;
272+
Mat intrinsics, distCoeffs;
273+
generateCameraMatrix(intrinsics, rng);
274+
if (mode == 0)
275+
distCoeffs = Mat::zeros(4, 1, CV_64FC1);
276+
else
277+
generateDistCoeffs(distCoeffs, rng);
278+
generatePose(trueRvec, trueTvec, rng);
279+
280+
std::vector<Point3f> opoints;
281+
opoints = std::vector<Point3f>(points.begin(), points.begin()+3);
282+
283+
vector<Point2f> projectedPoints;
284+
projectedPoints.resize(opoints.size());
285+
projectPoints(Mat(opoints), trueRvec, trueTvec, intrinsics, distCoeffs, projectedPoints);
286+
287+
int num_of_solutions = solveP3P(opoints, projectedPoints, intrinsics, distCoeffs, rvecs, tvecs, method);
288+
if (num_of_solutions != (int) rvecs.size() || num_of_solutions != (int) tvecs.size() || num_of_solutions == 0)
289+
return false;
290+
291+
double min_rvecDiff = DBL_MAX, min_tvecDiff = DBL_MAX;
292+
for (unsigned int i = 0; i < rvecs.size(); ++i) {
293+
double rvecDiff = norm(rvecs[i]-trueRvec);
294+
min_rvecDiff = std::min(rvecDiff, min_rvecDiff);
295+
}
296+
for (unsigned int i = 0; i < tvecs.size(); ++i) {
297+
double tvecDiff = norm(tvecs[i]-trueTvec);
298+
min_tvecDiff = std::min(tvecDiff, min_tvecDiff);
299+
}
300+
bool isTestSuccess = min_rvecDiff < epsilon[method] && min_tvecDiff < epsilon[method];
301+
302+
double error = std::max(min_rvecDiff, min_tvecDiff);
303+
if (error > maxError)
304+
maxError = error;
305+
306+
return isTestSuccess;
307+
}
308+
309+
virtual void run(int)
310+
{
311+
ts->set_failed_test_info(cvtest::TS::OK);
312+
313+
vector<Point3f> points, points_dls;
314+
const int pointsCount = 500;
315+
points.resize(pointsCount);
316+
generate3DPointCloud(points);
317+
318+
const int methodsCount = 2;
319+
int methods[methodsCount] = {SOLVEPNP_P3P, SOLVEPNP_AP3P};
320+
RNG rng = ts->get_rng();
321+
322+
for (int mode = 0; mode < 2; mode++)
323+
{
324+
for (int method = 0; method < methodsCount; method++)
325+
{
326+
double maxError = 0;
327+
int successfulTestsCount = 0;
328+
for (int testIndex = 0; testIndex < totalTestsCount; testIndex++)
329+
{
330+
if (runTest(rng, mode, methods[method], points, eps, maxError))
331+
successfulTestsCount++;
332+
}
333+
//cout << maxError << " " << successfulTestsCount << endl;
334+
if (successfulTestsCount < 0.7*totalTestsCount)
335+
{
336+
ts->printf( cvtest::TS::LOG, "Invalid accuracy for method %d, failed %d tests from %d, maximum error equals %f, distortion mode equals %d\n",
337+
method, totalTestsCount - successfulTestsCount, totalTestsCount, maxError, mode);
338+
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
339+
}
340+
cout << "mode: " << mode << ", method: " << method << " -> "
341+
<< ((double)successfulTestsCount / totalTestsCount) * 100 << "%"
342+
<< " (err < " << maxError << ")" << endl;
343+
}
344+
}
345+
}
346+
};
347+
348+
349+
TEST(Calib3d_SolveP3P, accuracy) { CV_solveP3P_Test test; test.safe_run();}
256350
TEST(Calib3d_SolvePnPRansac, accuracy) { CV_solvePnPRansac_Test test; test.safe_run(); }
257351
TEST(Calib3d_SolvePnP, accuracy) { CV_solvePnP_Test test; test.safe_run(); }
258352

0 commit comments

Comments
 (0)