mirror of
https://github.com/opencv/opencv.git
synced 2025-06-11 20:09:23 +08:00
Merge pull request #14362 from catree:feat_ippe
This commit is contained in:
commit
a433394870
@ -209,7 +209,21 @@
|
||||
hal_id = {inria-00350283},
|
||||
hal_version = {v1},
|
||||
}
|
||||
|
||||
@article{Collins14
|
||||
year = {2014},
|
||||
issn = {0920-5691},
|
||||
journal = {International Journal of Computer Vision},
|
||||
volume = {109},
|
||||
number = {3},
|
||||
doi = {10.1007/s11263-014-0725-5},
|
||||
title = {Infinitesimal Plane-Based Pose Estimation},
|
||||
url = {http://dx.doi.org/10.1007/s11263-014-0725-5},
|
||||
publisher = {Springer US},
|
||||
keywords = {Plane; Pose; SfM; PnP; Homography},
|
||||
author = {Collins, Toby and Bartoli, Adrien},
|
||||
pages = {252-286},
|
||||
language = {English}
|
||||
}
|
||||
@article{Daniilidis98,
|
||||
author = {Konstantinos Daniilidis},
|
||||
title = {Hand-Eye Calibration Using Dual Quaternions},
|
||||
|
@ -231,13 +231,25 @@ enum { LMEDS = 4, //!< least-median of squares algorithm
|
||||
RHO = 16 //!< RHO algorithm
|
||||
};
|
||||
|
||||
enum { SOLVEPNP_ITERATIVE = 0,
|
||||
SOLVEPNP_EPNP = 1, //!< EPnP: Efficient Perspective-n-Point Camera Pose Estimation @cite lepetit2009epnp
|
||||
SOLVEPNP_P3P = 2, //!< Complete Solution Classification for the Perspective-Three-Point Problem @cite gao2003complete
|
||||
SOLVEPNP_DLS = 3, //!< A Direct Least-Squares (DLS) Method for PnP @cite hesch2011direct
|
||||
SOLVEPNP_UPNP = 4, //!< Exhaustive Linearization for Robust Camera Pose and Focal Length Estimation @cite penate2013exhaustive
|
||||
SOLVEPNP_AP3P = 5, //!< An Efficient Algebraic Solution to the Perspective-Three-Point Problem @cite Ke17
|
||||
SOLVEPNP_MAX_COUNT //!< Used for count
|
||||
enum SolvePnPMethod {
|
||||
SOLVEPNP_ITERATIVE = 0,
|
||||
SOLVEPNP_EPNP = 1, //!< EPnP: Efficient Perspective-n-Point Camera Pose Estimation @cite lepetit2009epnp
|
||||
SOLVEPNP_P3P = 2, //!< Complete Solution Classification for the Perspective-Three-Point Problem @cite gao2003complete
|
||||
SOLVEPNP_DLS = 3, //!< A Direct Least-Squares (DLS) Method for PnP @cite hesch2011direct
|
||||
SOLVEPNP_UPNP = 4, //!< Exhaustive Linearization for Robust Camera Pose and Focal Length Estimation @cite penate2013exhaustive
|
||||
SOLVEPNP_AP3P = 5, //!< An Efficient Algebraic Solution to the Perspective-Three-Point Problem @cite Ke17
|
||||
SOLVEPNP_IPPE = 6, //!< Infinitesimal Plane-Based Pose Estimation @cite Collins14 \n
|
||||
//!< Object points must be coplanar.
|
||||
SOLVEPNP_IPPE_SQUARE = 7, //!< Infinitesimal Plane-Based Pose Estimation @cite Collins14 \n
|
||||
//!< This is a special case suitable for marker pose estimation.\n
|
||||
//!< 4 coplanar object points must be defined in the following order:
|
||||
//!< - point 0: [-squareLength / 2, squareLength / 2, 0]
|
||||
//!< - point 1: [ squareLength / 2, squareLength / 2, 0]
|
||||
//!< - point 2: [ squareLength / 2, -squareLength / 2, 0]
|
||||
//!< - point 3: [-squareLength / 2, -squareLength / 2, 0]
|
||||
#ifndef CV_DOXYGEN
|
||||
SOLVEPNP_MAX_COUNT //!< Used for count
|
||||
#endif
|
||||
};
|
||||
|
||||
enum { CALIB_CB_ADAPTIVE_THRESH = 1,
|
||||
@ -540,6 +552,17 @@ Check @ref tutorial_homography "the corresponding tutorial" for more details
|
||||
*/
|
||||
|
||||
/** @brief Finds an object pose from 3D-2D point correspondences.
|
||||
This function returns the rotation and the translation vectors that transform a 3D point expressed in the object
|
||||
coordinate frame to the camera coordinate frame, using different methods:
|
||||
- P3P methods (@ref SOLVEPNP_P3P, @ref SOLVEPNP_AP3P): need 4 input points to return a unique solution.
|
||||
- @ref SOLVEPNP_IPPE Input points must be >= 4 and object points must be coplanar.
|
||||
- @ref SOLVEPNP_IPPE_SQUARE Special case suitable for marker pose estimation.
|
||||
Number of input points must be 4. Object points must be defined in the following order:
|
||||
- point 0: [-squareLength / 2, squareLength / 2, 0]
|
||||
- point 1: [ squareLength / 2, squareLength / 2, 0]
|
||||
- point 2: [ squareLength / 2, -squareLength / 2, 0]
|
||||
- point 3: [-squareLength / 2, -squareLength / 2, 0]
|
||||
- for all the other flags, number of input points must be >= 4 and object points can be in any configuration.
|
||||
|
||||
@param objectPoints Array of object points in the object coordinate space, Nx3 1-channel or
|
||||
1xN/Nx1 3-channel, where N is the number of points. vector\<Point3f\> can be also passed here.
|
||||
@ -550,14 +573,14 @@ where N is the number of points. vector\<Point2f\> can be also passed here.
|
||||
\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
|
||||
4, 5, 8, 12 or 14 elements. If the vector is NULL/empty, the zero distortion coefficients are
|
||||
assumed.
|
||||
@param rvec Output rotation vector (see @ref Rodrigues ) that, together with tvec , brings points from
|
||||
@param rvec Output rotation vector (see @ref Rodrigues ) that, together with tvec, brings points from
|
||||
the model coordinate system to the camera coordinate system.
|
||||
@param tvec Output translation vector.
|
||||
@param useExtrinsicGuess Parameter used for #SOLVEPNP_ITERATIVE. If true (1), the function uses
|
||||
the provided rvec and tvec values as initial approximations of the rotation and translation
|
||||
vectors, respectively, and further optimizes them.
|
||||
@param flags Method for solving a PnP problem:
|
||||
- **SOLVEPNP_ITERATIVE** Iterative method is based on Levenberg-Marquardt optimization. In
|
||||
- **SOLVEPNP_ITERATIVE** Iterative method is based on a Levenberg-Marquardt optimization. In
|
||||
this case the function finds such a pose that minimizes reprojection error, that is the sum
|
||||
of squared distances between the observed projections imagePoints and the projected (using
|
||||
projectPoints ) objectPoints .
|
||||
@ -567,18 +590,24 @@ In this case the function requires exactly four object and image points.
|
||||
- **SOLVEPNP_AP3P** Method is based on the paper of T. Ke, S. Roumeliotis
|
||||
"An Efficient Algebraic Solution to the Perspective-Three-Point Problem" (@cite Ke17).
|
||||
In this case the function requires exactly four object and image points.
|
||||
- **SOLVEPNP_EPNP** Method has been introduced by F.Moreno-Noguer, V.Lepetit and P.Fua in the
|
||||
- **SOLVEPNP_EPNP** Method has been introduced by F. Moreno-Noguer, V. Lepetit and P. Fua in the
|
||||
paper "EPnP: Efficient Perspective-n-Point Camera Pose Estimation" (@cite lepetit2009epnp).
|
||||
- **SOLVEPNP_DLS** Method is based on the paper of Joel A. Hesch and Stergios I. Roumeliotis.
|
||||
- **SOLVEPNP_DLS** Method is based on the paper of J. Hesch and S. Roumeliotis.
|
||||
"A Direct Least-Squares (DLS) Method for PnP" (@cite hesch2011direct).
|
||||
- **SOLVEPNP_UPNP** Method is based on the paper of A.Penate-Sanchez, J.Andrade-Cetto,
|
||||
F.Moreno-Noguer. "Exhaustive Linearization for Robust Camera Pose and Focal Length
|
||||
- **SOLVEPNP_UPNP** Method is based on the paper of A. Penate-Sanchez, J. Andrade-Cetto,
|
||||
F. Moreno-Noguer. "Exhaustive Linearization for Robust Camera Pose and Focal Length
|
||||
Estimation" (@cite penate2013exhaustive). In this case the function also estimates the parameters \f$f_x\f$ and \f$f_y\f$
|
||||
assuming that both have the same value. Then the cameraMatrix is updated with the estimated
|
||||
focal length.
|
||||
- **SOLVEPNP_AP3P** Method is based on the paper of Tong Ke and Stergios I. Roumeliotis.
|
||||
"An Efficient Algebraic Solution to the Perspective-Three-Point Problem" (@cite Ke17). In this case the
|
||||
function requires exactly four object and image points.
|
||||
- **SOLVEPNP_IPPE** Method is based on the paper of T. Collins and A. Bartoli.
|
||||
"Infinitesimal Plane-Based Pose Estimation" (@cite Collins14). This method requires coplanar object points.
|
||||
- **SOLVEPNP_IPPE_SQUARE** Method is based on the paper of Toby Collins and Adrien Bartoli.
|
||||
"Infinitesimal Plane-Based Pose Estimation" (@cite Collins14). This method is suitable for marker pose estimation.
|
||||
It requires 4 coplanar object points defined in the following order:
|
||||
- point 0: [-squareLength / 2, squareLength / 2, 0]
|
||||
- point 1: [ squareLength / 2, squareLength / 2, 0]
|
||||
- point 2: [ squareLength / 2, -squareLength / 2, 0]
|
||||
- point 3: [-squareLength / 2, -squareLength / 2, 0]
|
||||
|
||||
The function estimates the object pose given a set of object points, their corresponding image
|
||||
projections, as well as the camera matrix and the distortion coefficients, see the figure below
|
||||
@ -634,7 +663,7 @@ using the perspective projection model \f$ \Pi \f$ and the camera intrinsic para
|
||||
\end{align*}
|
||||
\f]
|
||||
|
||||
The estimated pose is thus the rotation (`rvec`) and the translation (`tvec`) vectors that allow to transform
|
||||
The estimated pose is thus the rotation (`rvec`) and the translation (`tvec`) vectors that allow transforming
|
||||
a 3D point expressed in the world frame into the camera frame:
|
||||
|
||||
\f[
|
||||
@ -695,6 +724,13 @@ a 3D point expressed in the world frame into the camera frame:
|
||||
- With **SOLVEPNP_ITERATIVE** method and `useExtrinsicGuess=true`, the minimum number of points is 3 (3 points
|
||||
are sufficient to compute a pose but there are up to 4 solutions). The initial solution should be close to the
|
||||
global solution to converge.
|
||||
- With **SOLVEPNP_IPPE** input points must be >= 4 and object points must be coplanar.
|
||||
- With **SOLVEPNP_IPPE_SQUARE** this is a special case suitable for marker pose estimation.
|
||||
Number of input points must be 4. Object points must be defined in the following order:
|
||||
- point 0: [-squareLength / 2, squareLength / 2, 0]
|
||||
- point 1: [ squareLength / 2, squareLength / 2, 0]
|
||||
- point 2: [ squareLength / 2, -squareLength / 2, 0]
|
||||
- point 3: [-squareLength / 2, -squareLength / 2, 0]
|
||||
*/
|
||||
CV_EXPORTS_W bool solvePnP( InputArray objectPoints, InputArray imagePoints,
|
||||
InputArray cameraMatrix, InputArray distCoeffs,
|
||||
@ -712,10 +748,10 @@ where N is the number of points. vector\<Point2f\> can be also passed here.
|
||||
\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
|
||||
4, 5, 8, 12 or 14 elements. If the vector is NULL/empty, the zero distortion coefficients are
|
||||
assumed.
|
||||
@param rvec Output rotation vector (see Rodrigues ) that, together with tvec , brings points from
|
||||
@param rvec Output rotation vector (see @ref Rodrigues ) that, together with tvec, brings points from
|
||||
the model coordinate system to the camera coordinate system.
|
||||
@param tvec Output translation vector.
|
||||
@param useExtrinsicGuess Parameter used for SOLVEPNP_ITERATIVE. If true (1), the function uses
|
||||
@param useExtrinsicGuess Parameter used for @ref SOLVEPNP_ITERATIVE. If true (1), the function uses
|
||||
the provided rvec and tvec values as initial approximations of the rotation and translation
|
||||
vectors, respectively, and further optimizes them.
|
||||
@param iterationsCount Number of iterations.
|
||||
@ -724,12 +760,12 @@ is the maximum allowed distance between the observed and computed point projecti
|
||||
an inlier.
|
||||
@param confidence The probability that the algorithm produces a useful result.
|
||||
@param inliers Output vector that contains indices of inliers in objectPoints and imagePoints .
|
||||
@param flags Method for solving a PnP problem (see solvePnP ).
|
||||
@param flags Method for solving a PnP problem (see @ref solvePnP ).
|
||||
|
||||
The function estimates an object pose given a set of object points, their corresponding image
|
||||
projections, as well as the camera matrix and the distortion coefficients. This function finds such
|
||||
a pose that minimizes reprojection error, that is, the sum of squared distances between the observed
|
||||
projections imagePoints and the projected (using projectPoints ) objectPoints. The use of RANSAC
|
||||
projections imagePoints and the projected (using @ref projectPoints ) objectPoints. The use of RANSAC
|
||||
makes the function resistant to outliers.
|
||||
|
||||
@note
|
||||
@ -749,6 +785,7 @@ CV_EXPORTS_W bool solvePnPRansac( InputArray objectPoints, InputArray imagePoint
|
||||
bool useExtrinsicGuess = false, int iterationsCount = 100,
|
||||
float reprojectionError = 8.0, double confidence = 0.99,
|
||||
OutputArray inliers = noArray(), int flags = SOLVEPNP_ITERATIVE );
|
||||
|
||||
/** @brief Finds an object pose from 3 3D-2D point correspondences.
|
||||
|
||||
@param objectPoints Array of object points in the object coordinate space, 3x3 1-channel or
|
||||
@ -760,17 +797,20 @@ CV_EXPORTS_W bool solvePnPRansac( InputArray objectPoints, InputArray imagePoint
|
||||
\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
|
||||
4, 5, 8, 12 or 14 elements. If the vector is NULL/empty, the zero distortion coefficients are
|
||||
assumed.
|
||||
@param rvecs Output rotation vectors (see Rodrigues ) that, together with tvecs , brings points from
|
||||
@param rvecs Output rotation vectors (see @ref Rodrigues ) that, together with tvecs, brings points from
|
||||
the model coordinate system to the camera coordinate system. A P3P problem has up to 4 solutions.
|
||||
@param tvecs Output translation vectors.
|
||||
@param flags Method for solving a P3P problem:
|
||||
- **SOLVEPNP_P3P** Method is based on the paper of X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang
|
||||
"Complete Solution Classification for the Perspective-Three-Point Problem" (@cite gao2003complete).
|
||||
- **SOLVEPNP_AP3P** Method is based on the paper of Tong Ke and Stergios I. Roumeliotis.
|
||||
- **SOLVEPNP_AP3P** Method is based on the paper of T. Ke and S. Roumeliotis.
|
||||
"An Efficient Algebraic Solution to the Perspective-Three-Point Problem" (@cite Ke17).
|
||||
|
||||
The function estimates the object pose given 3 object points, their corresponding image
|
||||
projections, as well as the camera matrix and the distortion coefficients.
|
||||
|
||||
@note
|
||||
The solutions are sorted by reprojection errors (lowest to highest).
|
||||
*/
|
||||
CV_EXPORTS_W int solveP3P( InputArray objectPoints, InputArray imagePoints,
|
||||
InputArray cameraMatrix, InputArray distCoeffs,
|
||||
@ -789,7 +829,7 @@ where N is the number of points. vector\<Point2f\> can also be passed here.
|
||||
\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
|
||||
4, 5, 8, 12 or 14 elements. If the vector is NULL/empty, the zero distortion coefficients are
|
||||
assumed.
|
||||
@param rvec Input/Output rotation vector (see @ref Rodrigues ) that, together with tvec , brings points from
|
||||
@param rvec Input/Output rotation vector (see @ref Rodrigues ) that, together with tvec, brings points from
|
||||
the model coordinate system to the camera coordinate system. Input values are used as an initial solution.
|
||||
@param tvec Input/Output translation vector. Input values are used as an initial solution.
|
||||
@param criteria Criteria when to stop the Levenberg-Marquard iterative algorithm.
|
||||
@ -817,12 +857,12 @@ where N is the number of points. vector\<Point2f\> can also be passed here.
|
||||
\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
|
||||
4, 5, 8, 12 or 14 elements. If the vector is NULL/empty, the zero distortion coefficients are
|
||||
assumed.
|
||||
@param rvec Input/Output rotation vector (see @ref Rodrigues ) that, together with tvec , brings points from
|
||||
@param rvec Input/Output rotation vector (see @ref Rodrigues ) that, together with tvec, brings points from
|
||||
the model coordinate system to the camera coordinate system. Input values are used as an initial solution.
|
||||
@param tvec Input/Output translation vector. Input values are used as an initial solution.
|
||||
@param criteria Criteria when to stop the Levenberg-Marquard iterative algorithm.
|
||||
@param VVSlambda Gain for the virtual visual servoing control law, equivalent to the \f$\alpha\f$
|
||||
gain in the Gauss-Newton formulation.
|
||||
gain in the Damped Gauss-Newton formulation.
|
||||
|
||||
The function refines the object pose given at least 3 object points, their corresponding image
|
||||
projections, an initial solution for the rotation and translation vector,
|
||||
@ -836,6 +876,202 @@ CV_EXPORTS_W void solvePnPRefineVVS( InputArray objectPoints, InputArray imagePo
|
||||
TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 20, FLT_EPSILON),
|
||||
double VVSlambda = 1);
|
||||
|
||||
/** @brief Finds an object pose from 3D-2D point correspondences.
|
||||
This function returns a list of all the possible solutions (a solution is a <rotation vector, translation vector>
|
||||
couple), depending on the number of input points and the chosen method:
|
||||
- P3P methods (@ref SOLVEPNP_P3P, @ref SOLVEPNP_AP3P): 3 or 4 input points. Number of returned solutions can be between 0 and 4 with 3 input points.
|
||||
- @ref SOLVEPNP_IPPE Input points must be >= 4 and object points must be coplanar. Returns 2 solutions.
|
||||
- @ref SOLVEPNP_IPPE_SQUARE Special case suitable for marker pose estimation.
|
||||
Number of input points must be 4 and 2 solutions are returned. Object points must be defined in the following order:
|
||||
- point 0: [-squareLength / 2, squareLength / 2, 0]
|
||||
- point 1: [ squareLength / 2, squareLength / 2, 0]
|
||||
- point 2: [ squareLength / 2, -squareLength / 2, 0]
|
||||
- point 3: [-squareLength / 2, -squareLength / 2, 0]
|
||||
- for all the other flags, number of input points must be >= 4 and object points can be in any configuration.
|
||||
Only 1 solution is returned.
|
||||
|
||||
@param objectPoints Array of object points in the object coordinate space, Nx3 1-channel or
|
||||
1xN/Nx1 3-channel, where N is the number of points. vector\<Point3f\> can be also passed here.
|
||||
@param imagePoints Array of corresponding image points, Nx2 1-channel or 1xN/Nx1 2-channel,
|
||||
where N is the number of points. vector\<Point2f\> can be also passed here.
|
||||
@param cameraMatrix Input camera matrix \f$A = \vecthreethree{fx}{0}{cx}{0}{fy}{cy}{0}{0}{1}\f$ .
|
||||
@param distCoeffs Input vector of distortion coefficients
|
||||
\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
|
||||
4, 5, 8, 12 or 14 elements. If the vector is NULL/empty, the zero distortion coefficients are
|
||||
assumed.
|
||||
@param rvecs Vector of output rotation vectors (see @ref Rodrigues ) that, together with tvecs, brings points from
|
||||
the model coordinate system to the camera coordinate system.
|
||||
@param tvecs Vector of output translation vectors.
|
||||
@param useExtrinsicGuess Parameter used for #SOLVEPNP_ITERATIVE. If true (1), the function uses
|
||||
the provided rvec and tvec values as initial approximations of the rotation and translation
|
||||
vectors, respectively, and further optimizes them.
|
||||
@param flags Method for solving a PnP problem:
|
||||
- **SOLVEPNP_ITERATIVE** Iterative method is based on a Levenberg-Marquardt optimization. In
|
||||
this case the function finds such a pose that minimizes reprojection error, that is the sum
|
||||
of squared distances between the observed projections imagePoints and the projected (using
|
||||
projectPoints ) objectPoints .
|
||||
- **SOLVEPNP_P3P** Method is based on the paper of X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang
|
||||
"Complete Solution Classification for the Perspective-Three-Point Problem" (@cite gao2003complete).
|
||||
In this case the function requires exactly four object and image points.
|
||||
- **SOLVEPNP_AP3P** Method is based on the paper of T. Ke, S. Roumeliotis
|
||||
"An Efficient Algebraic Solution to the Perspective-Three-Point Problem" (@cite Ke17).
|
||||
In this case the function requires exactly four object and image points.
|
||||
- **SOLVEPNP_EPNP** Method has been introduced by F.Moreno-Noguer, V.Lepetit and P.Fua in the
|
||||
paper "EPnP: Efficient Perspective-n-Point Camera Pose Estimation" (@cite lepetit2009epnp).
|
||||
- **SOLVEPNP_DLS** Method is based on the paper of Joel A. Hesch and Stergios I. Roumeliotis.
|
||||
"A Direct Least-Squares (DLS) Method for PnP" (@cite hesch2011direct).
|
||||
- **SOLVEPNP_UPNP** Method is based on the paper of A.Penate-Sanchez, J.Andrade-Cetto,
|
||||
F.Moreno-Noguer. "Exhaustive Linearization for Robust Camera Pose and Focal Length
|
||||
Estimation" (@cite penate2013exhaustive). In this case the function also estimates the parameters \f$f_x\f$ and \f$f_y\f$
|
||||
assuming that both have the same value. Then the cameraMatrix is updated with the estimated
|
||||
focal length.
|
||||
- **SOLVEPNP_IPPE** Method is based on the paper of T. Collins and A. Bartoli.
|
||||
"Infinitesimal Plane-Based Pose Estimation" (@cite Collins14). This method requires coplanar object points.
|
||||
- **SOLVEPNP_IPPE_SQUARE** Method is based on the paper of Toby Collins and Adrien Bartoli.
|
||||
"Infinitesimal Plane-Based Pose Estimation" (@cite Collins14). This method is suitable for marker pose estimation.
|
||||
It requires 4 coplanar object points defined in the following order:
|
||||
- point 0: [-squareLength / 2, squareLength / 2, 0]
|
||||
- point 1: [ squareLength / 2, squareLength / 2, 0]
|
||||
- point 2: [ squareLength / 2, -squareLength / 2, 0]
|
||||
- point 3: [-squareLength / 2, -squareLength / 2, 0]
|
||||
@param rvec Rotation vector used to initialize an iterative PnP refinement algorithm, when flag is SOLVEPNP_ITERATIVE
|
||||
and useExtrinsicGuess is set to true.
|
||||
@param tvec Translation vector used to initialize an iterative PnP refinement algorithm, when flag is SOLVEPNP_ITERATIVE
|
||||
and useExtrinsicGuess is set to true.
|
||||
@param reprojectionError Optional vector of reprojection error, that is the RMS error
|
||||
(\f$ \text{RMSE} = \sqrt{\frac{\sum_{i}^{N} \left ( \hat{y_i} - y_i \right )^2}{N}} \f$) between the input image points
|
||||
and the 3D object points projected with the estimated pose.
|
||||
|
||||
The function estimates the object pose given a set of object points, their corresponding image
|
||||
projections, as well as the camera matrix and the distortion coefficients, see the figure below
|
||||
(more precisely, the X-axis of the camera frame is pointing to the right, the Y-axis downward
|
||||
and the Z-axis forward).
|
||||
|
||||

|
||||
|
||||
Points expressed in the world frame \f$ \bf{X}_w \f$ are projected into the image plane \f$ \left[ u, v \right] \f$
|
||||
using the perspective projection model \f$ \Pi \f$ and the camera intrinsic parameters matrix \f$ \bf{A} \f$:
|
||||
|
||||
\f[
|
||||
\begin{align*}
|
||||
\begin{bmatrix}
|
||||
u \\
|
||||
v \\
|
||||
1
|
||||
\end{bmatrix} &=
|
||||
\bf{A} \hspace{0.1em} \Pi \hspace{0.2em} ^{c}\bf{M}_w
|
||||
\begin{bmatrix}
|
||||
X_{w} \\
|
||||
Y_{w} \\
|
||||
Z_{w} \\
|
||||
1
|
||||
\end{bmatrix} \\
|
||||
\begin{bmatrix}
|
||||
u \\
|
||||
v \\
|
||||
1
|
||||
\end{bmatrix} &=
|
||||
\begin{bmatrix}
|
||||
f_x & 0 & c_x \\
|
||||
0 & f_y & c_y \\
|
||||
0 & 0 & 1
|
||||
\end{bmatrix}
|
||||
\begin{bmatrix}
|
||||
1 & 0 & 0 & 0 \\
|
||||
0 & 1 & 0 & 0 \\
|
||||
0 & 0 & 1 & 0
|
||||
\end{bmatrix}
|
||||
\begin{bmatrix}
|
||||
r_{11} & r_{12} & r_{13} & t_x \\
|
||||
r_{21} & r_{22} & r_{23} & t_y \\
|
||||
r_{31} & r_{32} & r_{33} & t_z \\
|
||||
0 & 0 & 0 & 1
|
||||
\end{bmatrix}
|
||||
\begin{bmatrix}
|
||||
X_{w} \\
|
||||
Y_{w} \\
|
||||
Z_{w} \\
|
||||
1
|
||||
\end{bmatrix}
|
||||
\end{align*}
|
||||
\f]
|
||||
|
||||
The estimated pose is thus the rotation (`rvec`) and the translation (`tvec`) vectors that allow transforming
|
||||
a 3D point expressed in the world frame into the camera frame:
|
||||
|
||||
\f[
|
||||
\begin{align*}
|
||||
\begin{bmatrix}
|
||||
X_c \\
|
||||
Y_c \\
|
||||
Z_c \\
|
||||
1
|
||||
\end{bmatrix} &=
|
||||
\hspace{0.2em} ^{c}\bf{M}_w
|
||||
\begin{bmatrix}
|
||||
X_{w} \\
|
||||
Y_{w} \\
|
||||
Z_{w} \\
|
||||
1
|
||||
\end{bmatrix} \\
|
||||
\begin{bmatrix}
|
||||
X_c \\
|
||||
Y_c \\
|
||||
Z_c \\
|
||||
1
|
||||
\end{bmatrix} &=
|
||||
\begin{bmatrix}
|
||||
r_{11} & r_{12} & r_{13} & t_x \\
|
||||
r_{21} & r_{22} & r_{23} & t_y \\
|
||||
r_{31} & r_{32} & r_{33} & t_z \\
|
||||
0 & 0 & 0 & 1
|
||||
\end{bmatrix}
|
||||
\begin{bmatrix}
|
||||
X_{w} \\
|
||||
Y_{w} \\
|
||||
Z_{w} \\
|
||||
1
|
||||
\end{bmatrix}
|
||||
\end{align*}
|
||||
\f]
|
||||
|
||||
@note
|
||||
- An example of how to use solvePnP for planar augmented reality can be found at
|
||||
opencv_source_code/samples/python/plane_ar.py
|
||||
- If you are using Python:
|
||||
- Numpy array slices won't work as input because solvePnP requires contiguous
|
||||
arrays (enforced by the assertion using cv::Mat::checkVector() around line 55 of
|
||||
modules/calib3d/src/solvepnp.cpp version 2.4.9)
|
||||
- The P3P algorithm requires image points to be in an array of shape (N,1,2) due
|
||||
to its calling of cv::undistortPoints (around line 75 of modules/calib3d/src/solvepnp.cpp version 2.4.9)
|
||||
which requires 2-channel information.
|
||||
- Thus, given some data D = np.array(...) where D.shape = (N,M), in order to use a subset of
|
||||
it as, e.g., imagePoints, one must effectively copy it into a new array: imagePoints =
|
||||
np.ascontiguousarray(D[:,:2]).reshape((N,1,2))
|
||||
- The methods **SOLVEPNP_DLS** and **SOLVEPNP_UPNP** cannot be used as the current implementations are
|
||||
unstable and sometimes give completely wrong results. If you pass one of these two
|
||||
flags, **SOLVEPNP_EPNP** method will be used instead.
|
||||
- The minimum number of points is 4 in the general case. In the case of **SOLVEPNP_P3P** and **SOLVEPNP_AP3P**
|
||||
methods, it is required to use exactly 4 points (the first 3 points are used to estimate all the solutions
|
||||
of the P3P problem, the last one is used to retain the best solution that minimizes the reprojection error).
|
||||
- With **SOLVEPNP_ITERATIVE** method and `useExtrinsicGuess=true`, the minimum number of points is 3 (3 points
|
||||
are sufficient to compute a pose but there are up to 4 solutions). The initial solution should be close to the
|
||||
global solution to converge.
|
||||
- With **SOLVEPNP_IPPE** input points must be >= 4 and object points must be coplanar.
|
||||
- With **SOLVEPNP_IPPE_SQUARE** this is a special case suitable for marker pose estimation.
|
||||
Number of input points must be 4. Object points must be defined in the following order:
|
||||
- point 0: [-squareLength / 2, squareLength / 2, 0]
|
||||
- point 1: [ squareLength / 2, squareLength / 2, 0]
|
||||
- point 2: [ squareLength / 2, -squareLength / 2, 0]
|
||||
- point 3: [-squareLength / 2, -squareLength / 2, 0]
|
||||
*/
|
||||
CV_EXPORTS_W int solvePnPGeneric( InputArray objectPoints, InputArray imagePoints,
|
||||
InputArray cameraMatrix, InputArray distCoeffs,
|
||||
OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs,
|
||||
bool useExtrinsicGuess = false, SolvePnPMethod flags = SOLVEPNP_ITERATIVE,
|
||||
InputArray rvec = noArray(), InputArray tvec = noArray(),
|
||||
OutputArray reprojectionError = noArray() );
|
||||
|
||||
/** @brief Finds an initial camera matrix from 3D-2D point correspondences.
|
||||
|
||||
@param objectPoints Vector of vectors of the calibration pattern points in the calibration pattern
|
||||
@ -933,7 +1169,7 @@ CV_EXPORTS_W void drawChessboardCorners( InputOutputArray image, Size patternSiz
|
||||
@param distCoeffs Input vector of distortion coefficients
|
||||
\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
|
||||
4, 5, 8, 12 or 14 elements. If the vector is empty, the zero distortion coefficients are assumed.
|
||||
@param rvec Rotation vector (see @ref Rodrigues ) that, together with tvec , brings points from
|
||||
@param rvec Rotation vector (see @ref Rodrigues ) that, together with tvec, brings points from
|
||||
the model coordinate system to the camera coordinate system.
|
||||
@param tvec Translation vector.
|
||||
@param length Length of the painted axes in the same unit than tvec (usually in meters).
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "precomp.hpp"
|
||||
#include "ap3p.h"
|
||||
|
||||
#include <cmath>
|
||||
@ -154,10 +155,11 @@ ap3p::ap3p(double _fx, double _fy, double _cx, double _cy) {
|
||||
// worldPoints: The positions of the 3 feature points stored as column vectors
|
||||
// solutionsR: 4 possible solutions of rotation matrix of the world w.r.t the camera frame
|
||||
// solutionsT: 4 possible solutions of translation of the world origin w.r.t the camera frame
|
||||
int ap3p::computePoses(const double featureVectors[3][3],
|
||||
const double worldPoints[3][3],
|
||||
int ap3p::computePoses(const double featureVectors[3][4],
|
||||
const double worldPoints[3][4],
|
||||
double solutionsR[4][3][3],
|
||||
double solutionsT[4][3]) {
|
||||
double solutionsT[4][3],
|
||||
bool p4p) {
|
||||
|
||||
//world point vectors
|
||||
double w1[3] = {worldPoints[0][0], worldPoints[1][0], worldPoints[2][0]};
|
||||
@ -246,6 +248,13 @@ int ap3p::computePoses(const double featureVectors[3][3],
|
||||
double b3p[3];
|
||||
vect_scale((delta / k3b3), b3, b3p);
|
||||
|
||||
double X3 = worldPoints[0][3];
|
||||
double Y3 = worldPoints[1][3];
|
||||
double Z3 = worldPoints[2][3];
|
||||
double mu3 = featureVectors[0][3];
|
||||
double mv3 = featureVectors[1][3];
|
||||
double reproj_errors[4];
|
||||
|
||||
int nb_solutions = 0;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
double ctheta1p = s[i];
|
||||
@ -290,9 +299,29 @@ int ap3p::computePoses(const double featureVectors[3][3],
|
||||
solutionsR[nb_solutions][1][2] = R[2][1];
|
||||
solutionsR[nb_solutions][2][2] = R[2][2];
|
||||
|
||||
if (p4p) {
|
||||
double X3p = solutionsR[nb_solutions][0][0] * X3 + solutionsR[nb_solutions][0][1] * Y3 + solutionsR[nb_solutions][0][2] * Z3 + solutionsT[nb_solutions][0];
|
||||
double Y3p = solutionsR[nb_solutions][1][0] * X3 + solutionsR[nb_solutions][1][1] * Y3 + solutionsR[nb_solutions][1][2] * Z3 + solutionsT[nb_solutions][1];
|
||||
double Z3p = solutionsR[nb_solutions][2][0] * X3 + solutionsR[nb_solutions][2][1] * Y3 + solutionsR[nb_solutions][2][2] * Z3 + solutionsT[nb_solutions][2];
|
||||
double mu3p = X3p / Z3p;
|
||||
double mv3p = Y3p / Z3p;
|
||||
reproj_errors[nb_solutions] = (mu3p - mu3) * (mu3p - mu3) + (mv3p - mv3) * (mv3p - mv3);
|
||||
}
|
||||
|
||||
nb_solutions++;
|
||||
}
|
||||
|
||||
//sort the solutions
|
||||
if (p4p) {
|
||||
for (int i = 1; i < nb_solutions; i++) {
|
||||
for (int j = i; j > 0 && reproj_errors[j-1] > reproj_errors[j]; j--) {
|
||||
std::swap(reproj_errors[j], reproj_errors[j-1]);
|
||||
std::swap(solutionsR[j], solutionsR[j-1]);
|
||||
std::swap(solutionsT[j], solutionsT[j-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nb_solutions;
|
||||
}
|
||||
|
||||
@ -311,9 +340,10 @@ bool ap3p::solve(cv::Mat &R, cv::Mat &tvec, const cv::Mat &opoints, const cv::Ma
|
||||
else
|
||||
extract_points<cv::Point3d, cv::Point2f>(opoints, ipoints, points);
|
||||
|
||||
bool result = solve(rotation_matrix, translation, points[0], points[1], points[2], points[3], points[4], points[5],
|
||||
points[6], points[7], points[8], points[9], points[10], points[11], points[12], points[13],
|
||||
points[14],
|
||||
bool result = solve(rotation_matrix, translation,
|
||||
points[0], points[1], points[2], points[3], points[4],
|
||||
points[5], points[6], points[7], points[8], points[9],
|
||||
points[10], points[11], points[12], points[13],points[14],
|
||||
points[15], points[16], points[17], points[18], points[19]);
|
||||
cv::Mat(3, 1, CV_64F, translation).copyTo(tvec);
|
||||
cv::Mat(3, 3, CV_64F, rotation_matrix).copyTo(R);
|
||||
@ -335,10 +365,13 @@ int ap3p::solve(std::vector<cv::Mat> &Rs, std::vector<cv::Mat> &tvecs, const cv:
|
||||
else
|
||||
extract_points<cv::Point3d, cv::Point2f>(opoints, ipoints, points);
|
||||
|
||||
const bool p4p = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F)) == 4;
|
||||
int solutions = solve(rotation_matrix, translation,
|
||||
points[0], points[1], points[2], points[3], points[4],
|
||||
points[5], points[6], points[7], points[8], points[9],
|
||||
points[10], points[11], points[12], points[13], points[14]);
|
||||
points[10], points[11], points[12], points[13], points[14],
|
||||
points[15], points[16], points[17], points[18], points[19],
|
||||
p4p);
|
||||
|
||||
for (int i = 0; i < solutions; i++) {
|
||||
cv::Mat R, tvec;
|
||||
@ -353,42 +386,33 @@ int ap3p::solve(std::vector<cv::Mat> &Rs, std::vector<cv::Mat> &tvecs, const cv:
|
||||
}
|
||||
|
||||
bool
|
||||
ap3p::solve(double R[3][3], double t[3], double mu0, double mv0, double X0, double Y0, double Z0, double mu1,
|
||||
double mv1,
|
||||
double X1, double Y1, double Z1, double mu2, double mv2, double X2, double Y2, double Z2, double mu3,
|
||||
double mv3, double X3, double Y3, double Z3) {
|
||||
ap3p::solve(double R[3][3], double t[3],
|
||||
double mu0, double mv0, double X0, double Y0, double Z0,
|
||||
double mu1, double mv1, double X1, double Y1, double Z1,
|
||||
double mu2, double mv2, double X2, double Y2, double Z2,
|
||||
double mu3, double mv3, double X3, double Y3, double Z3) {
|
||||
double Rs[4][3][3], ts[4][3];
|
||||
|
||||
int n = solve(Rs, ts, mu0, mv0, X0, Y0, Z0, mu1, mv1, X1, Y1, Z1, mu2, mv2, X2, Y2, Z2);
|
||||
const bool p4p = true;
|
||||
int n = solve(Rs, ts, mu0, mv0, X0, Y0, Z0, mu1, mv1, X1, Y1, Z1, mu2, mv2, X2, Y2, Z2, mu3, mv3, X3, Y3, Z3, p4p);
|
||||
if (n == 0)
|
||||
return false;
|
||||
|
||||
int ns = 0;
|
||||
double min_reproj = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
double X3p = Rs[i][0][0] * X3 + Rs[i][0][1] * Y3 + Rs[i][0][2] * Z3 + ts[i][0];
|
||||
double Y3p = Rs[i][1][0] * X3 + Rs[i][1][1] * Y3 + Rs[i][1][2] * Z3 + ts[i][1];
|
||||
double Z3p = Rs[i][2][0] * X3 + Rs[i][2][1] * Y3 + Rs[i][2][2] * Z3 + ts[i][2];
|
||||
double mu3p = cx + fx * X3p / Z3p;
|
||||
double mv3p = cy + fy * Y3p / Z3p;
|
||||
double reproj = (mu3p - mu3) * (mu3p - mu3) + (mv3p - mv3) * (mv3p - mv3);
|
||||
if (i == 0 || min_reproj > reproj) {
|
||||
ns = i;
|
||||
min_reproj = reproj;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
for (int j = 0; j < 3; j++)
|
||||
R[i][j] = Rs[ns][i][j];
|
||||
t[i] = ts[ns][i];
|
||||
R[i][j] = Rs[0][i][j];
|
||||
t[i] = ts[0][i];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int ap3p::solve(double R[4][3][3], double t[4][3], double mu0, double mv0, double X0, double Y0, double Z0, double mu1,
|
||||
double mv1, double X1, double Y1, double Z1, double mu2, double mv2, double X2, double Y2, double Z2) {
|
||||
int ap3p::solve(double R[4][3][3], double t[4][3],
|
||||
double mu0, double mv0, double X0, double Y0, double Z0,
|
||||
double mu1, double mv1, double X1, double Y1, double Z1,
|
||||
double mu2, double mv2, double X2, double Y2, double Z2,
|
||||
double mu3, double mv3, double X3, double Y3, double Z3,
|
||||
bool p4p) {
|
||||
double mk0, mk1, mk2;
|
||||
double norm;
|
||||
|
||||
@ -413,13 +437,17 @@ int ap3p::solve(double R[4][3][3], double t[4][3], double mu0, double mv0, doubl
|
||||
mu2 *= mk2;
|
||||
mv2 *= mk2;
|
||||
|
||||
double featureVectors[3][3] = {{mu0, mu1, mu2},
|
||||
{mv0, mv1, mv2},
|
||||
{mk0, mk1, mk2}};
|
||||
double worldPoints[3][3] = {{X0, X1, X2},
|
||||
{Y0, Y1, Y2},
|
||||
{Z0, Z1, Z2}};
|
||||
mu3 = inv_fx * mu3 - cx_fx;
|
||||
mv3 = inv_fy * mv3 - cy_fy;
|
||||
double mk3 = 1; //not used
|
||||
|
||||
return computePoses(featureVectors, worldPoints, R, t);
|
||||
double featureVectors[3][4] = {{mu0, mu1, mu2, mu3},
|
||||
{mv0, mv1, mv2, mv3},
|
||||
{mk0, mk1, mk2, mk3}};
|
||||
double worldPoints[3][4] = {{X0, X1, X2, X3},
|
||||
{Y0, Y1, Y2, Y3},
|
||||
{Z0, Z1, Z2, Z3}};
|
||||
|
||||
return computePoses(featureVectors, worldPoints, R, t, p4p);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#ifndef P3P_P3P_H
|
||||
#define P3P_P3P_H
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include <opencv2/core.hpp>
|
||||
|
||||
namespace cv {
|
||||
class ap3p {
|
||||
@ -18,7 +18,7 @@ private:
|
||||
void extract_points(const cv::Mat &opoints, const cv::Mat &ipoints, std::vector<double> &points) {
|
||||
points.clear();
|
||||
int npoints = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F));
|
||||
points.resize(5*npoints);
|
||||
points.resize(5*4); //resize vector to fit for p4p case
|
||||
for (int i = 0; i < npoints; i++) {
|
||||
points[i * 5] = ipoints.at<IpointType>(i).x * fx + cx;
|
||||
points[i * 5 + 1] = ipoints.at<IpointType>(i).y * fy + cy;
|
||||
@ -26,6 +26,12 @@ private:
|
||||
points[i * 5 + 3] = opoints.at<OpointType>(i).y;
|
||||
points[i * 5 + 4] = opoints.at<OpointType>(i).z;
|
||||
}
|
||||
//Fill vectors with unused values for p3p case
|
||||
for (int i = npoints; i < 4; i++) {
|
||||
for (int j = 0; j < 5; j++) {
|
||||
points[i * 5 + j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_inverse_parameters();
|
||||
@ -45,7 +51,9 @@ public:
|
||||
int solve(double R[4][3][3], double t[4][3],
|
||||
double mu0, double mv0, double X0, double Y0, double Z0,
|
||||
double mu1, double mv1, double X1, double Y1, double Z1,
|
||||
double mu2, double mv2, double X2, double Y2, double Z2);
|
||||
double mu2, double mv2, double X2, double Y2, double Z2,
|
||||
double mu3, double mv3, double X3, double Y3, double Z3,
|
||||
bool p4p);
|
||||
|
||||
bool solve(double R[3][3], double t[3],
|
||||
double mu0, double mv0, double X0, double Y0, double Z0,
|
||||
@ -59,8 +67,8 @@ public:
|
||||
// worldPoints: Positions of the 3 feature points stored as column vectors
|
||||
// solutionsR: 4 possible solutions of rotation matrix of the world w.r.t the camera frame
|
||||
// solutionsT: 4 possible solutions of translation of the world origin w.r.t the camera frame
|
||||
int computePoses(const double featureVectors[3][3], const double worldPoints[3][3], double solutionsR[4][3][3],
|
||||
double solutionsT[4][3]);
|
||||
int computePoses(const double featureVectors[3][4], const double worldPoints[3][4], double solutionsR[4][3][3],
|
||||
double solutionsT[4][3], bool p4p);
|
||||
|
||||
};
|
||||
}
|
||||
|
1100
modules/calib3d/src/ippe.cpp
Normal file
1100
modules/calib3d/src/ippe.cpp
Normal file
File diff suppressed because it is too large
Load Diff
259
modules/calib3d/src/ippe.hpp
Normal file
259
modules/calib3d/src/ippe.hpp
Normal file
@ -0,0 +1,259 @@
|
||||
// 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
|
||||
|
||||
// This file is based on file issued with the following license:
|
||||
|
||||
/*============================================================================
|
||||
|
||||
Copyright 2017 Toby Collins
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef OPENCV_CALIB3D_IPPE_HPP
|
||||
#define OPENCV_CALIB3D_IPPE_HPP
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
|
||||
namespace cv {
|
||||
namespace IPPE {
|
||||
|
||||
class PoseSolver {
|
||||
public:
|
||||
/**
|
||||
* @brief PoseSolver constructor
|
||||
*/
|
||||
PoseSolver();
|
||||
|
||||
/**
|
||||
* @brief Finds the two possible poses of a planar object given a set of correspondences and their respective reprojection errors.
|
||||
* The poses are sorted with the first having the lowest reprojection error.
|
||||
* @param objectPoints Array of 4 or more coplanar object points defined in object coordinates.
|
||||
* 1xN/Nx1 3-channel (float or double) where N is the number of points
|
||||
* @param imagePoints Array of corresponding image points, 1xN/Nx1 2-channel. Points are in normalized pixel coordinates.
|
||||
* @param rvec1 First rotation solution (3x1 rotation vector)
|
||||
* @param tvec1 First translation solution (3x1 vector)
|
||||
* @param reprojErr1 Reprojection error of first solution
|
||||
* @param rvec2 Second rotation solution (3x1 rotation vector)
|
||||
* @param tvec2 Second translation solution (3x1 vector)
|
||||
* @param reprojErr2 Reprojection error of second solution
|
||||
*/
|
||||
void solveGeneric(InputArray objectPoints, InputArray imagePoints, OutputArray rvec1, OutputArray tvec1,
|
||||
float& reprojErr1, OutputArray rvec2, OutputArray tvec2, float& reprojErr2);
|
||||
|
||||
/**
|
||||
* @brief Finds the two possible poses of a square planar object and their respective reprojection errors using IPPE.
|
||||
* The poses are sorted so that the first one is the one with the lowest reprojection error.
|
||||
*
|
||||
* @param objectPoints Array of 4 coplanar object points defined in the following object coordinates:
|
||||
* - point 0: [-squareLength / 2.0, squareLength / 2.0, 0]
|
||||
* - point 1: [squareLength / 2.0, squareLength / 2.0, 0]
|
||||
* - point 2: [squareLength / 2.0, -squareLength / 2.0, 0]
|
||||
* - point 3: [-squareLength / 2.0, -squareLength / 2.0, 0]
|
||||
* 1xN/Nx1 3-channel (float or double) where N is the number of points
|
||||
* @param imagePoints Array of corresponding image points, 1xN/Nx1 2-channel. Points are in normalized pixel coordinates.
|
||||
* @param rvec1 First rotation solution (3x1 rotation vector)
|
||||
* @param tvec1 First translation solution (3x1 vector)
|
||||
* @param reprojErr1 Reprojection error of first solution
|
||||
* @param rvec2 Second rotation solution (3x1 rotation vector)
|
||||
* @param tvec2 Second translation solution (3x1 vector)
|
||||
* @param reprojErr2 Reprojection error of second solution
|
||||
*/
|
||||
void solveSquare(InputArray objectPoints, InputArray imagePoints, OutputArray rvec1, OutputArray tvec1,
|
||||
float& reprojErr1, OutputArray rvec2, OutputArray tvec2, float& reprojErr2);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Finds the two possible poses of a planar object given a set of correspondences in normalized pixel coordinates.
|
||||
* These poses are **NOT** sorted on reprojection error. Note that the returned poses are object-to-camera transforms, and not camera-to-object transforms.
|
||||
* @param objectPoints Array of 4 or more coplanar object points defined in object coordinates. 1xN/Nx1 3-channel (float or double).
|
||||
* @param normalizedImagePoints Array of corresponding image points in normalized pixel coordinates, 1xN/Nx1 2-channel (float or double).
|
||||
* @param Ma First pose solution (unsorted)
|
||||
* @param Mb Second pose solution (unsorted)
|
||||
*/
|
||||
void solveGeneric(InputArray objectPoints, InputArray normalizedImagePoints, OutputArray Ma, OutputArray Mb);
|
||||
|
||||
/**
|
||||
* @brief Finds the two possible poses of a planar object in its canonical position, given a set of correspondences in normalized pixel coordinates.
|
||||
* These poses are **NOT** sorted on reprojection error. Note that the returned poses are object-to-camera transforms, and not camera-to-object transforms.
|
||||
* @param canonicalObjPoints Array of 4 or more coplanar object points defined in object coordinates. 1xN/Nx1 3-channel (double) where N is the number of points
|
||||
* @param normalizedInputPoints Array of corresponding image points in normalized pixel coordinates, 1xN/Nx1 2-channel (double) where N is the number of points
|
||||
* @param H Homography mapping canonicalObjPoints to normalizedInputPoints.
|
||||
* @param Ma
|
||||
* @param Mb
|
||||
*/
|
||||
void solveCanonicalForm(InputArray canonicalObjPoints, InputArray normalizedInputPoints, const Matx33d& H,
|
||||
OutputArray Ma, OutputArray Mb);
|
||||
|
||||
/**
|
||||
* @brief Computes the translation solution for a given rotation solution
|
||||
* @param objectPoints Array of corresponding object points, 1xN/Nx1 3-channel where N is the number of points
|
||||
* @param normalizedImagePoints Array of corresponding image points (undistorted), 1xN/Nx1 2-channel where N is the number of points
|
||||
* @param R Rotation solution (3x1 rotation vector)
|
||||
* @param t Translation solution (3x1 rotation vector)
|
||||
*/
|
||||
void computeTranslation(InputArray objectPoints, InputArray normalizedImgPoints, InputArray R, OutputArray t);
|
||||
|
||||
/**
|
||||
* @brief Computes the two rotation solutions from the Jacobian of a homography matrix H at a point (ux,uy) on the object plane.
|
||||
* For highest accuracy the Jacobian should be computed at the centroid of the point correspondences (see the IPPE paper for the explanation of this).
|
||||
* For a point (ux,uy) on the object plane, suppose the homography H maps (ux,uy) to a point (p,q) in the image (in normalized pixel coordinates).
|
||||
* The Jacobian matrix [J00, J01; J10,J11] is the Jacobian of the mapping evaluated at (ux,uy).
|
||||
* @param j00 Homography jacobian coefficent at (ux,uy)
|
||||
* @param j01 Homography jacobian coefficent at (ux,uy)
|
||||
* @param j10 Homography jacobian coefficent at (ux,uy)
|
||||
* @param j11 Homography jacobian coefficent at (ux,uy)
|
||||
* @param p The x coordinate of point (ux,uy) mapped into the image (undistorted and normalized position)
|
||||
* @param q The y coordinate of point (ux,uy) mapped into the image (undistorted and normalized position)
|
||||
*/
|
||||
void computeRotations(double j00, double j01, double j10, double j11, double p, double q, OutputArray _R1, OutputArray _R2);
|
||||
|
||||
/**
|
||||
* @brief Closed-form solution for the homography mapping with four corner correspondences of a square (it maps source points to target points).
|
||||
* The source points are the four corners of a zero-centred squared defined by:
|
||||
* - point 0: [-squareLength / 2.0, squareLength / 2.0]
|
||||
* - point 1: [squareLength / 2.0, squareLength / 2.0]
|
||||
* - point 2: [squareLength / 2.0, -squareLength / 2.0]
|
||||
* - point 3: [-squareLength / 2.0, -squareLength / 2.0]
|
||||
*
|
||||
* @param targetPoints Array of four corresponding target points, 1x4/4x1 2-channel. Note that the points should be ordered to correspond with points 0, 1, 2 and 3.
|
||||
* @param halfLength The square's half length (i.e. squareLength/2.0)
|
||||
* @param H Homograhy mapping the source points to the target points, 3x3 single channel
|
||||
*/
|
||||
void homographyFromSquarePoints(InputArray targetPoints, double halfLength, OutputArray H);
|
||||
|
||||
/**
|
||||
* @brief Fast conversion from a rotation matrix to a rotation vector using Rodrigues' formula
|
||||
* @param R Input rotation matrix, 3x3 1-channel (double)
|
||||
* @param r Output rotation vector, 3x1/1x3 1-channel (double)
|
||||
*/
|
||||
void rot2vec(InputArray R, OutputArray r);
|
||||
|
||||
/**
|
||||
* @brief Takes a set of planar object points and transforms them to 'canonical' object coordinates This is when they have zero mean and are on the plane z=0
|
||||
* @param objectPoints Array of 4 or more coplanar object points defined in object coordinates. 1xN/Nx1 3-channel (float or double) where N is the number of points
|
||||
* @param canonicalObjectPoints Object points in canonical coordinates 1xN/Nx1 2-channel (double)
|
||||
* @param MobjectPoints2Canonical Transform matrix mapping _objectPoints to _canonicalObjectPoints: 4x4 1-channel (double)
|
||||
*/
|
||||
void makeCanonicalObjectPoints(InputArray objectPoints, OutputArray canonicalObjectPoints, OutputArray MobjectPoints2Canonical);
|
||||
|
||||
/**
|
||||
* @brief Evaluates the Root Mean Squared (RMS) reprojection error of a pose solution.
|
||||
* @param objectPoints Array of 4 or more coplanar object points defined in object coordinates. 1xN/Nx1 3-channel (float or double) where N is the number of points
|
||||
* @param imagePoints Array of corresponding image points, 1xN/Nx1 2-channel. This can either be in pixel coordinates or normalized pixel coordinates.
|
||||
* @param M Pose matrix from 3D object to camera coordinates: 4x4 1-channel (double)
|
||||
* @param err RMS reprojection error
|
||||
*/
|
||||
void evalReprojError(InputArray objectPoints, InputArray imagePoints, InputArray M, float& err);
|
||||
|
||||
/**
|
||||
* @brief Sorts two pose solutions according to their RMS reprojection error (lowest first).
|
||||
* @param objectPoints Array of 4 or more coplanar object points defined in object coordinates. 1xN/Nx1 3-channel (float or double) where N is the number of points
|
||||
* @param imagePoints Array of corresponding image points, 1xN/Nx1 2-channel. This can either be in pixel coordinates or normalized pixel coordinates.
|
||||
* @param Ma Pose matrix 1: 4x4 1-channel
|
||||
* @param Mb Pose matrix 2: 4x4 1-channel
|
||||
* @param M1 Member of (Ma,Mb} with lowest RMS reprojection error. Performs deep copy.
|
||||
* @param M2 Member of (Ma,Mb} with highest RMS reprojection error. Performs deep copy.
|
||||
* @param err1 RMS reprojection error of _M1
|
||||
* @param err2 RMS reprojection error of _M2
|
||||
*/
|
||||
void sortPosesByReprojError(InputArray objectPoints, InputArray imagePoints, InputArray Ma, InputArray Mb, OutputArray M1, OutputArray M2, float& err1, float& err2);
|
||||
|
||||
/**
|
||||
* @brief Finds the rotation _Ra that rotates a vector _a to the z axis (0,0,1)
|
||||
* @param a vector: 3x1 mat (double)
|
||||
* @param Ra Rotation: 3x3 mat (double)
|
||||
*/
|
||||
void rotateVec2ZAxis(const Matx31d& a, Matx33d& Ra);
|
||||
|
||||
/**
|
||||
* @brief Computes the rotation _R that rotates the object points to the plane z=0. This uses the cross-product method with the first three object points.
|
||||
* @param objectPoints Array of N>=3 coplanar object points defined in object coordinates. 1xN/Nx1 3-channel (float or double) where N is the number of points
|
||||
* @param R Rotation Mat: 3x3 (double)
|
||||
* @return Success (true) or failure (false)
|
||||
*/
|
||||
bool computeObjextSpaceR3Pts(InputArray objectPoints, Matx33d& R);
|
||||
|
||||
/**
|
||||
* @brief computeObjextSpaceRSvD Computes the rotation _R that rotates the object points to the plane z=0. This uses the cross-product method with the first three object points.
|
||||
* @param objectPointsZeroMean Zero-meaned coplanar object points: 3xN matrix (double) where N>=3
|
||||
* @param R Rotation Mat: 3x3 (double)
|
||||
*/
|
||||
void computeObjextSpaceRSvD(InputArray objectPointsZeroMean, OutputArray R);
|
||||
|
||||
/**
|
||||
* @brief Generates the 4 object points of a square planar object
|
||||
* @param squareLength The square's length (which is also it's width) in object coordinate units (e.g. millimeters, meters, etc.)
|
||||
* @param objectPoints Set of 4 object points (1x4 3-channel double)
|
||||
*/
|
||||
void generateSquareObjectCorners3D(double squareLength, OutputArray objectPoints);
|
||||
|
||||
/**
|
||||
* @brief Generates the 4 object points of a square planar object, without including the z-component (which is z=0 for all points).
|
||||
* @param squareLength The square's length (which is also it's width) in object coordinate units (e.g. millimeters, meters, etc.)
|
||||
* @param objectPoints Set of 4 object points (1x4 2-channel double)
|
||||
*/
|
||||
void generateSquareObjectCorners2D(double squareLength, OutputArray objectPoints);
|
||||
|
||||
/**
|
||||
* @brief Computes the average depth of an object given its pose in camera coordinates
|
||||
* @param objectPoints: Object points defined in 3D object space
|
||||
* @param rvec: Rotation component of pose
|
||||
* @param tvec: Translation component of pose
|
||||
* @return: average depth of the object
|
||||
*/
|
||||
double meanSceneDepth(InputArray objectPoints, InputArray rvec, InputArray tvec);
|
||||
|
||||
//! a small constant used to test 'small' values close to zero.
|
||||
double IPPE_SMALL;
|
||||
};
|
||||
} //namespace IPPE
|
||||
|
||||
namespace HomographyHO {
|
||||
|
||||
/**
|
||||
* @brief Computes the best-fitting homography matrix from source to target points using Harker and O'Leary's method:
|
||||
* Harker, M., O'Leary, P., Computation of Homographies, Proceedings of the British Machine Vision Conference 2005, Oxford, England.
|
||||
* This is not the author's implementation.
|
||||
* @param srcPoints Array of source points: 1xN/Nx1 2-channel (float or double) where N is the number of points
|
||||
* @param targPoints Array of target points: 1xN/Nx1 2-channel (float or double)
|
||||
* @param H Homography from source to target: 3x3 1-channel (double)
|
||||
*/
|
||||
void homographyHO(InputArray srcPoints, InputArray targPoints, Matx33d& H);
|
||||
|
||||
/**
|
||||
* @brief Performs data normalization before homography estimation. For details see Hartley, R., Zisserman, A., Multiple View Geometry in Computer Vision,
|
||||
* Cambridge University Press, Cambridge, 2001
|
||||
* @param Data Array of source data points: 1xN/Nx1 2-channel (float or double) where N is the number of points
|
||||
* @param DataN Normalized data points: 1xN/Nx1 2-channel (float or double) where N is the number of points
|
||||
* @param T Homogeneous transform from source to normalized: 3x3 1-channel (double)
|
||||
* @param Ti Homogeneous transform from normalized to source: 3x3 1-channel (double)
|
||||
*/
|
||||
void normalizeDataIsotropic(InputArray Data, OutputArray DataN, OutputArray T, OutputArray Ti);
|
||||
|
||||
}
|
||||
} //namespace cv
|
||||
#endif
|
@ -49,9 +49,11 @@ bool p3p::solve(cv::Mat& R, cv::Mat& tvec, const cv::Mat& opoints, const cv::Mat
|
||||
else
|
||||
extract_points<cv::Point3d,cv::Point2f>(opoints, ipoints, points);
|
||||
|
||||
bool result = solve(rotation_matrix, translation, points[0], points[1], points[2], points[3], points[4], points[5],
|
||||
points[6], points[7], points[8], points[9], points[10], points[11], points[12], points[13], points[14],
|
||||
points[15], points[16], points[17], points[18], points[19]);
|
||||
bool result = solve(rotation_matrix, translation,
|
||||
points[0], points[1], points[2], points[3], points[4],
|
||||
points[5], points[6], points[7], points[8], points[9],
|
||||
points[10], points[11], points[12], points[13], points[14],
|
||||
points[15], points[16], points[17], points[18], points[19]);
|
||||
cv::Mat(3, 1, CV_64F, translation).copyTo(tvec);
|
||||
cv::Mat(3, 3, CV_64F, rotation_matrix).copyTo(R);
|
||||
return result;
|
||||
@ -75,10 +77,13 @@ int p3p::solve(std::vector<cv::Mat>& Rs, std::vector<cv::Mat>& tvecs, const cv::
|
||||
else
|
||||
extract_points<cv::Point3d,cv::Point2f>(opoints, ipoints, points);
|
||||
|
||||
const bool p4p = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F)) == 4;
|
||||
int solutions = solve(rotation_matrix, translation,
|
||||
points[0], points[1], points[2], points[3], points[4],
|
||||
points[5], points[6], points[7], points[8], points[9],
|
||||
points[10], points[11], points[12], points[13], points[14]);
|
||||
points[10], points[11], points[12], points[13], points[14],
|
||||
points[15], points[16], points[17], points[18], points[19],
|
||||
p4p);
|
||||
|
||||
for (int i = 0; i < solutions; i++) {
|
||||
cv::Mat R, tvec;
|
||||
@ -100,39 +105,27 @@ bool p3p::solve(double R[3][3], double t[3],
|
||||
{
|
||||
double Rs[4][3][3], ts[4][3];
|
||||
|
||||
int n = solve(Rs, ts, mu0, mv0, X0, Y0, Z0, mu1, mv1, X1, Y1, Z1, mu2, mv2, X2, Y2, Z2);
|
||||
const bool p4p = true;
|
||||
int n = solve(Rs, ts, mu0, mv0, X0, Y0, Z0, mu1, mv1, X1, Y1, Z1, mu2, mv2, X2, Y2, Z2, mu3, mv3, X3, Y3, Z3, p4p);
|
||||
|
||||
if (n == 0)
|
||||
return false;
|
||||
|
||||
int ns = 0;
|
||||
double min_reproj = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
double X3p = Rs[i][0][0] * X3 + Rs[i][0][1] * Y3 + Rs[i][0][2] * Z3 + ts[i][0];
|
||||
double Y3p = Rs[i][1][0] * X3 + Rs[i][1][1] * Y3 + Rs[i][1][2] * Z3 + ts[i][1];
|
||||
double Z3p = Rs[i][2][0] * X3 + Rs[i][2][1] * Y3 + Rs[i][2][2] * Z3 + ts[i][2];
|
||||
double mu3p = cx + fx * X3p / Z3p;
|
||||
double mv3p = cy + fy * Y3p / Z3p;
|
||||
double reproj = (mu3p - mu3) * (mu3p - mu3) + (mv3p - mv3) * (mv3p - mv3);
|
||||
if (i == 0 || min_reproj > reproj) {
|
||||
ns = i;
|
||||
min_reproj = reproj;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < 3; i++) {
|
||||
for(int j = 0; j < 3; j++)
|
||||
R[i][j] = Rs[ns][i][j];
|
||||
t[i] = ts[ns][i];
|
||||
R[i][j] = Rs[0][i][j];
|
||||
t[i] = ts[0][i];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int p3p::solve(double R[4][3][3], double t[4][3],
|
||||
double mu0, double mv0, double X0, double Y0, double Z0,
|
||||
double mu1, double mv1, double X1, double Y1, double Z1,
|
||||
double mu2, double mv2, double X2, double Y2, double Z2)
|
||||
double mu0, double mv0, double X0, double Y0, double Z0,
|
||||
double mu1, double mv1, double X1, double Y1, double Z1,
|
||||
double mu2, double mv2, double X2, double Y2, double Z2,
|
||||
double mu3, double mv3, double X3, double Y3, double Z3,
|
||||
bool p4p)
|
||||
{
|
||||
double mk0, mk1, mk2;
|
||||
double norm;
|
||||
@ -152,6 +145,9 @@ int p3p::solve(double R[4][3][3], double t[4][3],
|
||||
norm = sqrt(mu2 * mu2 + mv2 * mv2 + 1);
|
||||
mk2 = 1. / norm; mu2 *= mk2; mv2 *= mk2;
|
||||
|
||||
mu3 = inv_fx * mu3 - cx_fx;
|
||||
mv3 = inv_fy * mv3 - cy_fy;
|
||||
|
||||
double distances[3];
|
||||
distances[0] = sqrt( (X1 - X2) * (X1 - X2) + (Y1 - Y2) * (Y1 - Y2) + (Z1 - Z2) * (Z1 - Z2) );
|
||||
distances[1] = sqrt( (X0 - X2) * (X0 - X2) + (Y0 - Y2) * (Y0 - Y2) + (Z0 - Z2) * (Z0 - Z2) );
|
||||
@ -167,6 +163,7 @@ int p3p::solve(double R[4][3][3], double t[4][3],
|
||||
int n = solve_for_lengths(lengths, distances, cosines);
|
||||
|
||||
int nb_solutions = 0;
|
||||
double reproj_errors[4];
|
||||
for(int i = 0; i < n; i++) {
|
||||
double M_orig[3][3];
|
||||
|
||||
@ -185,9 +182,29 @@ int p3p::solve(double R[4][3][3], double t[4][3],
|
||||
if (!align(M_orig, X0, Y0, Z0, X1, Y1, Z1, X2, Y2, Z2, R[nb_solutions], t[nb_solutions]))
|
||||
continue;
|
||||
|
||||
if (p4p) {
|
||||
double X3p = R[nb_solutions][0][0] * X3 + R[nb_solutions][0][1] * Y3 + R[nb_solutions][0][2] * Z3 + t[nb_solutions][0];
|
||||
double Y3p = R[nb_solutions][1][0] * X3 + R[nb_solutions][1][1] * Y3 + R[nb_solutions][1][2] * Z3 + t[nb_solutions][1];
|
||||
double Z3p = R[nb_solutions][2][0] * X3 + R[nb_solutions][2][1] * Y3 + R[nb_solutions][2][2] * Z3 + t[nb_solutions][2];
|
||||
double mu3p = X3p / Z3p;
|
||||
double mv3p = Y3p / Z3p;
|
||||
reproj_errors[nb_solutions] = (mu3p - mu3) * (mu3p - mu3) + (mv3p - mv3) * (mv3p - mv3);
|
||||
}
|
||||
|
||||
nb_solutions++;
|
||||
}
|
||||
|
||||
if (p4p) {
|
||||
//sort the solutions
|
||||
for (int i = 1; i < nb_solutions; i++) {
|
||||
for (int j = i; j > 0 && reproj_errors[j-1] > reproj_errors[j]; j--) {
|
||||
std::swap(reproj_errors[j], reproj_errors[j-1]);
|
||||
std::swap(R[j], R[j-1]);
|
||||
std::swap(t[j], t[j-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nb_solutions;
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,9 @@ class p3p
|
||||
int solve(double R[4][3][3], double t[4][3],
|
||||
double mu0, double mv0, double X0, double Y0, double Z0,
|
||||
double mu1, double mv1, double X1, double Y1, double Z1,
|
||||
double mu2, double mv2, double X2, double Y2, double Z2);
|
||||
double mu2, double mv2, double X2, double Y2, double Z2,
|
||||
double mu3, double mv3, double X3, double Y3, double Z3,
|
||||
bool p4p);
|
||||
bool solve(double R[3][3], double t[3],
|
||||
double mu0, double mv0, double X0, double Y0, double Z0,
|
||||
double mu1, double mv1, double X1, double Y1, double Z1,
|
||||
@ -36,7 +38,7 @@ class p3p
|
||||
{
|
||||
points.clear();
|
||||
int npoints = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F));
|
||||
points.resize(5*npoints);
|
||||
points.resize(5*4); //resize vector to fit for p4p case
|
||||
for(int i = 0; i < npoints; i++)
|
||||
{
|
||||
points[i*5] = ipoints.at<IpointType>(i).x*fx + cx;
|
||||
@ -45,6 +47,12 @@ class p3p
|
||||
points[i*5+3] = opoints.at<OpointType>(i).y;
|
||||
points[i*5+4] = opoints.at<OpointType>(i).z;
|
||||
}
|
||||
//Fill vectors with unused values for p3p case
|
||||
for (int i = npoints; i < 4; i++) {
|
||||
for (int j = 0; j < 5; j++) {
|
||||
points[i * 5 + j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
void init_inverse_parameters();
|
||||
int solve_for_lengths(double lengths[4][3], double distances[3], double cosines[3]);
|
||||
|
@ -46,12 +46,44 @@
|
||||
#include "epnp.h"
|
||||
#include "p3p.h"
|
||||
#include "ap3p.h"
|
||||
#include "ippe.hpp"
|
||||
#include "opencv2/calib3d/calib3d_c.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
#if defined _DEBUG || defined CV_STATIC_ANALYSIS
|
||||
static bool isPlanarObjectPoints(InputArray _objectPoints, double threshold)
|
||||
{
|
||||
CV_CheckType(_objectPoints.type(), _objectPoints.type() == CV_32FC3 || _objectPoints.type() == CV_64FC3,
|
||||
"Type of _objectPoints must be CV_32FC3 or CV_64FC3");
|
||||
Mat objectPoints;
|
||||
if (_objectPoints.type() == CV_32FC3)
|
||||
{
|
||||
_objectPoints.getMat().convertTo(objectPoints, CV_64F);
|
||||
}
|
||||
else
|
||||
{
|
||||
objectPoints = _objectPoints.getMat();
|
||||
}
|
||||
|
||||
Scalar meanValues = mean(objectPoints);
|
||||
int nbPts = objectPoints.checkVector(3, CV_64F);
|
||||
Mat objectPointsCentred = objectPoints - meanValues;
|
||||
objectPointsCentred = objectPointsCentred.reshape(1, nbPts);
|
||||
|
||||
Mat w, u, vt;
|
||||
Mat MM = objectPointsCentred.t() * objectPointsCentred;
|
||||
SVDecomp(MM, w, u, vt);
|
||||
|
||||
return (w.at<double>(2) < w.at<double>(1) * threshold);
|
||||
}
|
||||
|
||||
static bool approxEqual(double a, double b, double eps)
|
||||
{
|
||||
return std::fabs(a-b) < eps;
|
||||
}
|
||||
#endif
|
||||
|
||||
void drawFrameAxes(InputOutputArray image, InputArray cameraMatrix, InputArray distCoeffs,
|
||||
InputArray rvec, InputArray tvec, float length, int thickness)
|
||||
{
|
||||
@ -80,120 +112,24 @@ void drawFrameAxes(InputOutputArray image, InputArray cameraMatrix, InputArray d
|
||||
line(image, imagePoints[0], imagePoints[3], Scalar(255, 0, 0), thickness);
|
||||
}
|
||||
|
||||
bool solvePnP( InputArray _opoints, InputArray _ipoints,
|
||||
InputArray _cameraMatrix, InputArray _distCoeffs,
|
||||
OutputArray _rvec, OutputArray _tvec, bool useExtrinsicGuess, int flags )
|
||||
bool solvePnP( InputArray opoints, InputArray ipoints,
|
||||
InputArray cameraMatrix, InputArray distCoeffs,
|
||||
OutputArray rvec, OutputArray tvec, bool useExtrinsicGuess, int flags )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat opoints = _opoints.getMat(), ipoints = _ipoints.getMat();
|
||||
int npoints = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F));
|
||||
CV_Assert( ( (npoints >= 4) || (npoints == 3 && flags == SOLVEPNP_ITERATIVE && useExtrinsicGuess) )
|
||||
&& npoints == std::max(ipoints.checkVector(2, CV_32F), ipoints.checkVector(2, CV_64F)) );
|
||||
vector<Mat> rvecs, tvecs;
|
||||
int solutions = solvePnPGeneric(opoints, ipoints, cameraMatrix, distCoeffs, rvecs, tvecs, useExtrinsicGuess, (SolvePnPMethod)flags, rvec, tvec);
|
||||
|
||||
Mat rvec, tvec;
|
||||
if( flags != SOLVEPNP_ITERATIVE )
|
||||
useExtrinsicGuess = false;
|
||||
|
||||
if( useExtrinsicGuess )
|
||||
if (solutions > 0)
|
||||
{
|
||||
int rtype = _rvec.type(), ttype = _tvec.type();
|
||||
Size rsize = _rvec.size(), tsize = _tvec.size();
|
||||
CV_Assert( (rtype == CV_32F || rtype == CV_64F) &&
|
||||
(ttype == CV_32F || ttype == CV_64F) );
|
||||
CV_Assert( (rsize == Size(1, 3) || rsize == Size(3, 1)) &&
|
||||
(tsize == Size(1, 3) || tsize == Size(3, 1)) );
|
||||
int rdepth = rvec.empty() ? CV_64F : rvec.depth();
|
||||
int tdepth = tvec.empty() ? CV_64F : tvec.depth();
|
||||
rvecs[0].convertTo(rvec, rdepth);
|
||||
tvecs[0].convertTo(tvec, tdepth);
|
||||
}
|
||||
else
|
||||
{
|
||||
int mtype = CV_64F;
|
||||
// use CV_32F if all PnP inputs are CV_32F and outputs are empty
|
||||
if (_ipoints.depth() == _cameraMatrix.depth() && _ipoints.depth() == _opoints.depth() &&
|
||||
_rvec.empty() && _tvec.empty())
|
||||
mtype = _opoints.depth();
|
||||
|
||||
_rvec.create(3, 1, mtype);
|
||||
_tvec.create(3, 1, mtype);
|
||||
}
|
||||
rvec = _rvec.getMat();
|
||||
tvec = _tvec.getMat();
|
||||
|
||||
Mat cameraMatrix0 = _cameraMatrix.getMat();
|
||||
Mat distCoeffs0 = _distCoeffs.getMat();
|
||||
Mat cameraMatrix = Mat_<double>(cameraMatrix0);
|
||||
Mat distCoeffs = Mat_<double>(distCoeffs0);
|
||||
bool result = false;
|
||||
|
||||
if (flags == SOLVEPNP_EPNP || flags == SOLVEPNP_DLS || flags == SOLVEPNP_UPNP)
|
||||
{
|
||||
Mat undistortedPoints;
|
||||
undistortPoints(ipoints, undistortedPoints, cameraMatrix, distCoeffs);
|
||||
epnp PnP(cameraMatrix, opoints, undistortedPoints);
|
||||
|
||||
Mat R;
|
||||
PnP.compute_pose(R, tvec);
|
||||
Rodrigues(R, rvec);
|
||||
result = true;
|
||||
}
|
||||
else if (flags == SOLVEPNP_P3P)
|
||||
{
|
||||
CV_Assert( npoints == 4);
|
||||
Mat undistortedPoints;
|
||||
undistortPoints(ipoints, undistortedPoints, cameraMatrix, distCoeffs);
|
||||
p3p P3Psolver(cameraMatrix);
|
||||
|
||||
Mat R;
|
||||
result = P3Psolver.solve(R, tvec, opoints, undistortedPoints);
|
||||
if (result)
|
||||
Rodrigues(R, rvec);
|
||||
}
|
||||
else if (flags == SOLVEPNP_AP3P)
|
||||
{
|
||||
CV_Assert( npoints == 4);
|
||||
Mat undistortedPoints;
|
||||
undistortPoints(ipoints, undistortedPoints, cameraMatrix, distCoeffs);
|
||||
ap3p P3Psolver(cameraMatrix);
|
||||
|
||||
Mat R;
|
||||
result = P3Psolver.solve(R, tvec, opoints, undistortedPoints);
|
||||
if (result)
|
||||
Rodrigues(R, rvec);
|
||||
}
|
||||
else if (flags == SOLVEPNP_ITERATIVE)
|
||||
{
|
||||
CvMat c_objectPoints = cvMat(opoints), c_imagePoints = cvMat(ipoints);
|
||||
CvMat c_cameraMatrix = cvMat(cameraMatrix), c_distCoeffs = cvMat(distCoeffs);
|
||||
CvMat c_rvec = cvMat(rvec), c_tvec = cvMat(tvec);
|
||||
cvFindExtrinsicCameraParams2(&c_objectPoints, &c_imagePoints, &c_cameraMatrix,
|
||||
(c_distCoeffs.rows && c_distCoeffs.cols) ? &c_distCoeffs : 0,
|
||||
&c_rvec, &c_tvec, useExtrinsicGuess );
|
||||
result = true;
|
||||
}
|
||||
/*else if (flags == SOLVEPNP_DLS)
|
||||
{
|
||||
Mat undistortedPoints;
|
||||
undistortPoints(ipoints, undistortedPoints, cameraMatrix, distCoeffs);
|
||||
|
||||
dls PnP(opoints, undistortedPoints);
|
||||
|
||||
Mat R, rvec = _rvec.getMat(), tvec = _tvec.getMat();
|
||||
bool result = PnP.compute_pose(R, tvec);
|
||||
if (result)
|
||||
Rodrigues(R, rvec);
|
||||
return result;
|
||||
}
|
||||
else if (flags == SOLVEPNP_UPNP)
|
||||
{
|
||||
upnp PnP(cameraMatrix, opoints, ipoints);
|
||||
|
||||
Mat R, rvec = _rvec.getMat(), tvec = _tvec.getMat();
|
||||
PnP.compute_pose(R, tvec);
|
||||
Rodrigues(R, rvec);
|
||||
return true;
|
||||
}*/
|
||||
else
|
||||
CV_Error(CV_StsBadArg, "The flags argument must be one of SOLVEPNP_ITERATIVE, SOLVEPNP_P3P, SOLVEPNP_EPNP or SOLVEPNP_DLS");
|
||||
return result;
|
||||
return solutions > 0;
|
||||
}
|
||||
|
||||
class PnPRansacCallback CV_FINAL : public PointSetRegistrator::Callback
|
||||
@ -258,10 +194,10 @@ public:
|
||||
};
|
||||
|
||||
bool solvePnPRansac(InputArray _opoints, InputArray _ipoints,
|
||||
InputArray _cameraMatrix, InputArray _distCoeffs,
|
||||
OutputArray _rvec, OutputArray _tvec, bool useExtrinsicGuess,
|
||||
int iterationsCount, float reprojectionError, double confidence,
|
||||
OutputArray _inliers, int flags)
|
||||
InputArray _cameraMatrix, InputArray _distCoeffs,
|
||||
OutputArray _rvec, OutputArray _tvec, bool useExtrinsicGuess,
|
||||
int iterationsCount, float reprojectionError, double confidence,
|
||||
OutputArray _inliers, int flags)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
@ -410,7 +346,8 @@ int solveP3P( InputArray _opoints, InputArray _ipoints,
|
||||
|
||||
Mat opoints = _opoints.getMat(), ipoints = _ipoints.getMat();
|
||||
int npoints = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F));
|
||||
CV_Assert( npoints == 3 && npoints == std::max(ipoints.checkVector(2, CV_32F), ipoints.checkVector(2, CV_64F)) );
|
||||
CV_Assert( npoints == std::max(ipoints.checkVector(2, CV_32F), ipoints.checkVector(2, CV_64F)) );
|
||||
CV_Assert( npoints == 3 || npoints == 4 );
|
||||
CV_Assert( flags == SOLVEPNP_P3P || flags == SOLVEPNP_AP3P );
|
||||
|
||||
Mat cameraMatrix0 = _cameraMatrix.getMat();
|
||||
@ -420,7 +357,7 @@ int solveP3P( InputArray _opoints, InputArray _ipoints,
|
||||
|
||||
Mat undistortedPoints;
|
||||
undistortPoints(ipoints, undistortedPoints, cameraMatrix, distCoeffs);
|
||||
std::vector<Mat> Rs, ts;
|
||||
std::vector<Mat> Rs, ts, rvecs;
|
||||
|
||||
int solutions = 0;
|
||||
if (flags == SOLVEPNP_P3P)
|
||||
@ -438,19 +375,91 @@ int solveP3P( InputArray _opoints, InputArray _ipoints,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_rvecs.needed()) {
|
||||
_rvecs.create(solutions, 1, CV_64F);
|
||||
Mat objPts, imgPts;
|
||||
opoints.convertTo(objPts, CV_64F);
|
||||
ipoints.convertTo(imgPts, CV_64F);
|
||||
if (imgPts.cols > 1)
|
||||
{
|
||||
imgPts = imgPts.reshape(1);
|
||||
imgPts = imgPts.t();
|
||||
}
|
||||
else
|
||||
imgPts = imgPts.reshape(1, 2*imgPts.rows);
|
||||
|
||||
if (_tvecs.needed()) {
|
||||
_tvecs.create(solutions, 1, CV_64F);
|
||||
}
|
||||
|
||||
for (int i = 0; i < solutions; i++) {
|
||||
vector<double> reproj_errors(solutions);
|
||||
for (size_t i = 0; i < reproj_errors.size(); i++)
|
||||
{
|
||||
Mat rvec;
|
||||
Rodrigues(Rs[i], rvec);
|
||||
_tvecs.getMatRef(i) = ts[i];
|
||||
_rvecs.getMatRef(i) = rvec;
|
||||
rvecs.push_back(rvec);
|
||||
|
||||
Mat projPts;
|
||||
projectPoints(objPts, rvec, ts[i], _cameraMatrix, _distCoeffs, projPts);
|
||||
|
||||
projPts = projPts.reshape(1, 2*projPts.rows);
|
||||
Mat err = imgPts - projPts;
|
||||
|
||||
err = err.t() * err;
|
||||
reproj_errors[i] = err.at<double>(0,0);
|
||||
}
|
||||
|
||||
//sort the solutions
|
||||
for (int i = 1; i < solutions; i++)
|
||||
{
|
||||
for (int j = i; j > 0 && reproj_errors[j-1] > reproj_errors[j]; j--)
|
||||
{
|
||||
std::swap(reproj_errors[j], reproj_errors[j-1]);
|
||||
std::swap(rvecs[j], rvecs[j-1]);
|
||||
std::swap(ts[j], ts[j-1]);
|
||||
}
|
||||
}
|
||||
|
||||
int depthRot = _rvecs.fixedType() ? _rvecs.depth() : CV_64F;
|
||||
int depthTrans = _tvecs.fixedType() ? _tvecs.depth() : CV_64F;
|
||||
_rvecs.create(solutions, 1, CV_MAKETYPE(depthRot, _rvecs.fixedType() && _rvecs.kind() == _InputArray::STD_VECTOR ? 3 : 1));
|
||||
_tvecs.create(solutions, 1, CV_MAKETYPE(depthTrans, _tvecs.fixedType() && _tvecs.kind() == _InputArray::STD_VECTOR ? 3 : 1));
|
||||
|
||||
for (int i = 0; i < solutions; i++)
|
||||
{
|
||||
Mat rvec0, tvec0;
|
||||
if (depthRot == CV_64F)
|
||||
rvec0 = rvecs[i];
|
||||
else
|
||||
rvecs[i].convertTo(rvec0, depthRot);
|
||||
|
||||
if (depthTrans == CV_64F)
|
||||
tvec0 = ts[i];
|
||||
else
|
||||
ts[i].convertTo(tvec0, depthTrans);
|
||||
|
||||
if (_rvecs.fixedType() && _rvecs.kind() == _InputArray::STD_VECTOR)
|
||||
{
|
||||
Mat rref = _rvecs.getMat_();
|
||||
|
||||
if (_rvecs.depth() == CV_32F)
|
||||
rref.at<Vec3f>(0,i) = Vec3f(rvec0.at<float>(0,0), rvec0.at<float>(1,0), rvec0.at<float>(2,0));
|
||||
else
|
||||
rref.at<Vec3d>(0,i) = Vec3d(rvec0.at<double>(0,0), rvec0.at<double>(1,0), rvec0.at<double>(2,0));
|
||||
}
|
||||
else
|
||||
{
|
||||
_rvecs.getMatRef(i) = rvec0;
|
||||
}
|
||||
|
||||
if (_tvecs.fixedType() && _tvecs.kind() == _InputArray::STD_VECTOR)
|
||||
{
|
||||
|
||||
Mat tref = _tvecs.getMat_();
|
||||
|
||||
if (_tvecs.depth() == CV_32F)
|
||||
tref.at<Vec3f>(0,i) = Vec3f(tvec0.at<float>(0,0), tvec0.at<float>(1,0), tvec0.at<float>(2,0));
|
||||
else
|
||||
tref.at<Vec3d>(0,i) = Vec3d(tvec0.at<double>(0,0), tvec0.at<double>(1,0), tvec0.at<double>(2,0));
|
||||
}
|
||||
else
|
||||
{
|
||||
_tvecs.getMatRef(i) = tvec0;
|
||||
}
|
||||
}
|
||||
|
||||
return solutions;
|
||||
@ -723,4 +732,314 @@ void solvePnPRefineVVS(InputArray _objectPoints, InputArray _imagePoints,
|
||||
solvePnPRefine(_objectPoints, _imagePoints, _cameraMatrix, _distCoeffs, _rvec, _tvec, SOLVEPNP_REFINE_VVS, _criteria, _VVSlambda);
|
||||
}
|
||||
|
||||
int solvePnPGeneric( InputArray _opoints, InputArray _ipoints,
|
||||
InputArray _cameraMatrix, InputArray _distCoeffs,
|
||||
OutputArrayOfArrays _rvecs, OutputArrayOfArrays _tvecs,
|
||||
bool useExtrinsicGuess, SolvePnPMethod flags,
|
||||
InputArray _rvec, InputArray _tvec,
|
||||
OutputArray reprojectionError) {
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat opoints = _opoints.getMat(), ipoints = _ipoints.getMat();
|
||||
int npoints = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F));
|
||||
CV_Assert( ( (npoints >= 4) || (npoints == 3 && flags == SOLVEPNP_ITERATIVE && useExtrinsicGuess) )
|
||||
&& npoints == std::max(ipoints.checkVector(2, CV_32F), ipoints.checkVector(2, CV_64F)) );
|
||||
|
||||
if( flags != SOLVEPNP_ITERATIVE )
|
||||
useExtrinsicGuess = false;
|
||||
|
||||
if (useExtrinsicGuess)
|
||||
CV_Assert( !_rvec.empty() && !_tvec.empty() );
|
||||
|
||||
if( useExtrinsicGuess )
|
||||
{
|
||||
int rtype = _rvec.type(), ttype = _tvec.type();
|
||||
Size rsize = _rvec.size(), tsize = _tvec.size();
|
||||
CV_Assert( (rtype == CV_32FC1 || rtype == CV_64FC1) &&
|
||||
(ttype == CV_32FC1 || ttype == CV_64FC1) );
|
||||
CV_Assert( (rsize == Size(1, 3) || rsize == Size(3, 1)) &&
|
||||
(tsize == Size(1, 3) || tsize == Size(3, 1)) );
|
||||
}
|
||||
|
||||
Mat cameraMatrix0 = _cameraMatrix.getMat();
|
||||
Mat distCoeffs0 = _distCoeffs.getMat();
|
||||
Mat cameraMatrix = Mat_<double>(cameraMatrix0);
|
||||
Mat distCoeffs = Mat_<double>(distCoeffs0);
|
||||
|
||||
vector<Mat> vec_rvecs, vec_tvecs;
|
||||
if (flags == SOLVEPNP_EPNP || flags == SOLVEPNP_DLS || flags == SOLVEPNP_UPNP)
|
||||
{
|
||||
Mat undistortedPoints;
|
||||
undistortPoints(ipoints, undistortedPoints, cameraMatrix, distCoeffs);
|
||||
epnp PnP(cameraMatrix, opoints, undistortedPoints);
|
||||
|
||||
Mat rvec, tvec, R;
|
||||
PnP.compute_pose(R, tvec);
|
||||
Rodrigues(R, rvec);
|
||||
|
||||
vec_rvecs.push_back(rvec);
|
||||
vec_tvecs.push_back(tvec);
|
||||
}
|
||||
else if (flags == SOLVEPNP_P3P || flags == SOLVEPNP_AP3P)
|
||||
{
|
||||
vector<Mat> rvecs, tvecs;
|
||||
solveP3P(_opoints, _ipoints, _cameraMatrix, _distCoeffs, rvecs, tvecs, flags);
|
||||
vec_rvecs.insert(vec_rvecs.end(), rvecs.begin(), rvecs.end());
|
||||
vec_tvecs.insert(vec_tvecs.end(), tvecs.begin(), tvecs.end());
|
||||
}
|
||||
else if (flags == SOLVEPNP_ITERATIVE)
|
||||
{
|
||||
Mat rvec, tvec;
|
||||
if (useExtrinsicGuess)
|
||||
{
|
||||
rvec = _rvec.getMat();
|
||||
tvec = _tvec.getMat();
|
||||
}
|
||||
else
|
||||
{
|
||||
rvec.create(3, 1, CV_64FC1);
|
||||
tvec.create(3, 1, CV_64FC1);
|
||||
}
|
||||
|
||||
CvMat c_objectPoints = cvMat(opoints), c_imagePoints = cvMat(ipoints);
|
||||
CvMat c_cameraMatrix = cvMat(cameraMatrix), c_distCoeffs = cvMat(distCoeffs);
|
||||
CvMat c_rvec = cvMat(rvec), c_tvec = cvMat(tvec);
|
||||
cvFindExtrinsicCameraParams2(&c_objectPoints, &c_imagePoints, &c_cameraMatrix,
|
||||
(c_distCoeffs.rows && c_distCoeffs.cols) ? &c_distCoeffs : 0,
|
||||
&c_rvec, &c_tvec, useExtrinsicGuess );
|
||||
|
||||
vec_rvecs.push_back(rvec);
|
||||
vec_tvecs.push_back(tvec);
|
||||
}
|
||||
else if (flags == SOLVEPNP_IPPE)
|
||||
{
|
||||
CV_DbgAssert(isPlanarObjectPoints(opoints, 1e-3));
|
||||
Mat undistortedPoints;
|
||||
undistortPoints(ipoints, undistortedPoints, cameraMatrix, distCoeffs);
|
||||
|
||||
IPPE::PoseSolver poseSolver;
|
||||
Mat rvec1, tvec1, rvec2, tvec2;
|
||||
float reprojErr1, reprojErr2;
|
||||
try
|
||||
{
|
||||
poseSolver.solveGeneric(opoints, undistortedPoints, rvec1, tvec1, reprojErr1, rvec2, tvec2, reprojErr2);
|
||||
|
||||
if (reprojErr1 < reprojErr2)
|
||||
{
|
||||
vec_rvecs.push_back(rvec1);
|
||||
vec_tvecs.push_back(tvec1);
|
||||
|
||||
vec_rvecs.push_back(rvec2);
|
||||
vec_tvecs.push_back(tvec2);
|
||||
}
|
||||
else
|
||||
{
|
||||
vec_rvecs.push_back(rvec2);
|
||||
vec_tvecs.push_back(tvec2);
|
||||
|
||||
vec_rvecs.push_back(rvec1);
|
||||
vec_tvecs.push_back(tvec1);
|
||||
}
|
||||
}
|
||||
catch (...) { }
|
||||
}
|
||||
else if (flags == SOLVEPNP_IPPE_SQUARE)
|
||||
{
|
||||
CV_Assert(npoints == 4);
|
||||
|
||||
#if defined _DEBUG || defined CV_STATIC_ANALYSIS
|
||||
double Xs[4][3];
|
||||
if (opoints.depth() == CV_32F)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
Xs[i][j] = opoints.ptr<Vec3f>(0)[i](j);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
Xs[i][j] = opoints.ptr<Vec3d>(0)[i](j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const double equalThreshold = 1e-9;
|
||||
//Z must be zero
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
CV_DbgCheck(Xs[i][2], approxEqual(Xs[i][2], 0, equalThreshold), "Z object point coordinate must be zero!");
|
||||
}
|
||||
//Y0 == Y1 && Y2 == Y3
|
||||
CV_DbgCheck(Xs[0][1], approxEqual(Xs[0][1], Xs[1][1], equalThreshold), "Object points must be: Y0 == Y1!");
|
||||
CV_DbgCheck(Xs[2][1], approxEqual(Xs[2][1], Xs[3][1], equalThreshold), "Object points must be: Y2 == Y3!");
|
||||
//X0 == X3 && X1 == X2
|
||||
CV_DbgCheck(Xs[0][0], approxEqual(Xs[0][0], Xs[3][0], equalThreshold), "Object points must be: X0 == X3!");
|
||||
CV_DbgCheck(Xs[1][0], approxEqual(Xs[1][0], Xs[2][0], equalThreshold), "Object points must be: X1 == X2!");
|
||||
//X1 == Y1 && X3 == Y3
|
||||
CV_DbgCheck(Xs[1][0], approxEqual(Xs[1][0], Xs[1][1], equalThreshold), "Object points must be: X1 == Y1!");
|
||||
CV_DbgCheck(Xs[3][0], approxEqual(Xs[3][0], Xs[3][1], equalThreshold), "Object points must be: X3 == Y3!");
|
||||
#endif
|
||||
|
||||
Mat undistortedPoints;
|
||||
undistortPoints(ipoints, undistortedPoints, cameraMatrix, distCoeffs);
|
||||
|
||||
IPPE::PoseSolver poseSolver;
|
||||
Mat rvec1, tvec1, rvec2, tvec2;
|
||||
float reprojErr1, reprojErr2;
|
||||
try
|
||||
{
|
||||
poseSolver.solveSquare(opoints, undistortedPoints, rvec1, tvec1, reprojErr1, rvec2, tvec2, reprojErr2);
|
||||
|
||||
if (reprojErr1 < reprojErr2)
|
||||
{
|
||||
vec_rvecs.push_back(rvec1);
|
||||
vec_tvecs.push_back(tvec1);
|
||||
|
||||
vec_rvecs.push_back(rvec2);
|
||||
vec_tvecs.push_back(tvec2);
|
||||
}
|
||||
else
|
||||
{
|
||||
vec_rvecs.push_back(rvec2);
|
||||
vec_tvecs.push_back(tvec2);
|
||||
|
||||
vec_rvecs.push_back(rvec1);
|
||||
vec_tvecs.push_back(tvec1);
|
||||
}
|
||||
} catch (...) { }
|
||||
}
|
||||
/*else if (flags == SOLVEPNP_DLS)
|
||||
{
|
||||
Mat undistortedPoints;
|
||||
undistortPoints(ipoints, undistortedPoints, cameraMatrix, distCoeffs);
|
||||
|
||||
dls PnP(opoints, undistortedPoints);
|
||||
|
||||
Mat rvec, tvec, R;
|
||||
bool result = PnP.compute_pose(R, tvec);
|
||||
if (result)
|
||||
{
|
||||
Rodrigues(R, rvec);
|
||||
vec_rvecs.push_back(rvec);
|
||||
vec_tvecs.push_back(tvec);
|
||||
}
|
||||
}
|
||||
else if (flags == SOLVEPNP_UPNP)
|
||||
{
|
||||
upnp PnP(cameraMatrix, opoints, ipoints);
|
||||
|
||||
Mat rvec, tvec, R;
|
||||
PnP.compute_pose(R, tvec);
|
||||
Rodrigues(R, rvec);
|
||||
vec_rvecs.push_back(rvec);
|
||||
vec_tvecs.push_back(tvec);
|
||||
}*/
|
||||
else
|
||||
CV_Error(CV_StsBadArg, "The flags argument must be one of SOLVEPNP_ITERATIVE, SOLVEPNP_P3P, SOLVEPNP_EPNP or SOLVEPNP_DLS");
|
||||
|
||||
CV_Assert(vec_rvecs.size() == vec_tvecs.size());
|
||||
|
||||
int solutions = static_cast<int>(vec_rvecs.size());
|
||||
|
||||
int depthRot = _rvecs.fixedType() ? _rvecs.depth() : CV_64F;
|
||||
int depthTrans = _tvecs.fixedType() ? _tvecs.depth() : CV_64F;
|
||||
_rvecs.create(solutions, 1, CV_MAKETYPE(depthRot, _rvecs.fixedType() && _rvecs.kind() == _InputArray::STD_VECTOR ? 3 : 1));
|
||||
_tvecs.create(solutions, 1, CV_MAKETYPE(depthTrans, _tvecs.fixedType() && _tvecs.kind() == _InputArray::STD_VECTOR ? 3 : 1));
|
||||
|
||||
for (int i = 0; i < solutions; i++)
|
||||
{
|
||||
Mat rvec0, tvec0;
|
||||
if (depthRot == CV_64F)
|
||||
rvec0 = vec_rvecs[i];
|
||||
else
|
||||
vec_rvecs[i].convertTo(rvec0, depthRot);
|
||||
|
||||
if (depthTrans == CV_64F)
|
||||
tvec0 = vec_tvecs[i];
|
||||
else
|
||||
vec_tvecs[i].convertTo(tvec0, depthTrans);
|
||||
|
||||
if (_rvecs.fixedType() && _rvecs.kind() == _InputArray::STD_VECTOR)
|
||||
{
|
||||
Mat rref = _rvecs.getMat_();
|
||||
|
||||
if (_rvecs.depth() == CV_32F)
|
||||
rref.at<Vec3f>(0,i) = Vec3f(rvec0.at<float>(0,0), rvec0.at<float>(1,0), rvec0.at<float>(2,0));
|
||||
else
|
||||
rref.at<Vec3d>(0,i) = Vec3d(rvec0.at<double>(0,0), rvec0.at<double>(1,0), rvec0.at<double>(2,0));
|
||||
}
|
||||
else
|
||||
{
|
||||
_rvecs.getMatRef(i) = rvec0;
|
||||
}
|
||||
|
||||
if (_tvecs.fixedType() && _tvecs.kind() == _InputArray::STD_VECTOR)
|
||||
{
|
||||
|
||||
Mat tref = _tvecs.getMat_();
|
||||
|
||||
if (_tvecs.depth() == CV_32F)
|
||||
tref.at<Vec3f>(0,i) = Vec3f(tvec0.at<float>(0,0), tvec0.at<float>(1,0), tvec0.at<float>(2,0));
|
||||
else
|
||||
tref.at<Vec3d>(0,i) = Vec3d(tvec0.at<double>(0,0), tvec0.at<double>(1,0), tvec0.at<double>(2,0));
|
||||
}
|
||||
else
|
||||
{
|
||||
_tvecs.getMatRef(i) = tvec0;
|
||||
}
|
||||
}
|
||||
|
||||
if (reprojectionError.needed())
|
||||
{
|
||||
int type = reprojectionError.type();
|
||||
reprojectionError.create(solutions, 1, type);
|
||||
CV_CheckType(reprojectionError.type(), type == CV_32FC1 || type == CV_64FC1,
|
||||
"Type of reprojectionError must be CV_32FC1 or CV_64FC1!");
|
||||
|
||||
Mat objectPoints, imagePoints;
|
||||
if (_opoints.depth() == CV_32F)
|
||||
{
|
||||
_opoints.getMat().convertTo(objectPoints, CV_64F);
|
||||
}
|
||||
else
|
||||
{
|
||||
objectPoints = _opoints.getMat();
|
||||
}
|
||||
if (_ipoints.depth() == CV_32F)
|
||||
{
|
||||
_ipoints.getMat().convertTo(imagePoints, CV_64F);
|
||||
}
|
||||
else
|
||||
{
|
||||
imagePoints = _ipoints.getMat();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < vec_rvecs.size(); i++)
|
||||
{
|
||||
vector<Point2d> projectedPoints;
|
||||
projectPoints(objectPoints, vec_rvecs[i], vec_tvecs[i], cameraMatrix, distCoeffs, projectedPoints);
|
||||
double rmse = norm(projectedPoints, imagePoints, NORM_L2) / sqrt(2*projectedPoints.size());
|
||||
|
||||
Mat err = reprojectionError.getMat();
|
||||
if (type == CV_32F)
|
||||
{
|
||||
err.at<float>(0,static_cast<int>(i)) = static_cast<float>(rmse);
|
||||
}
|
||||
else
|
||||
{
|
||||
err.at<double>(0,static_cast<int>(i)) = rmse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return solutions;
|
||||
}
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user