mirror of https://github.com/opencv/opencv.git
commit
0105f8fa38
16 changed files with 1338 additions and 133 deletions
@ -0,0 +1,775 @@ |
||||
// 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:
|
||||
|
||||
/*
|
||||
BSD 3-Clause License |
||||
|
||||
Copyright (c) 2020, George Terzakis |
||||
All rights reserved. |
||||
|
||||
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. |
||||
*/ |
||||
|
||||
#include "precomp.hpp" |
||||
#include "sqpnp.hpp" |
||||
|
||||
#include <opencv2/calib3d.hpp> |
||||
|
||||
namespace cv { |
||||
namespace sqpnp { |
||||
|
||||
const double PoseSolver::RANK_TOLERANCE = 1e-7; |
||||
const double PoseSolver::SQP_SQUARED_TOLERANCE = 1e-10; |
||||
const double PoseSolver::SQP_DET_THRESHOLD = 1.001; |
||||
const double PoseSolver::ORTHOGONALITY_SQUARED_ERROR_THRESHOLD = 1e-8; |
||||
const double PoseSolver::EQUAL_VECTORS_SQUARED_DIFF = 1e-10; |
||||
const double PoseSolver::EQUAL_SQUARED_ERRORS_DIFF = 1e-6; |
||||
const double PoseSolver::POINT_VARIANCE_THRESHOLD = 1e-5; |
||||
const double PoseSolver::SQRT3 = std::sqrt(3); |
||||
const int PoseSolver::SQP_MAX_ITERATION = 15; |
||||
|
||||
//No checking done here for overflow, since this is not public all call instances
|
||||
//are assumed to be valid
|
||||
template <typename tp, int snrows, int sncols, |
||||
int dnrows, int dncols> |
||||
void set(int row, int col, cv::Matx<tp, dnrows, dncols>& dest, |
||||
const cv::Matx<tp, snrows, sncols>& source) |
||||
{ |
||||
for (int y = 0; y < snrows; y++) |
||||
{ |
||||
for (int x = 0; x < sncols; x++) |
||||
{ |
||||
dest(row + y, col + x) = source(y, x); |
||||
} |
||||
} |
||||
} |
||||
|
||||
PoseSolver::PoseSolver() |
||||
: num_null_vectors_(-1), |
||||
num_solutions_(0) |
||||
{ |
||||
} |
||||
|
||||
|
||||
void PoseSolver::solve(InputArray objectPoints, InputArray imagePoints, OutputArrayOfArrays rvecs, |
||||
OutputArrayOfArrays tvecs) |
||||
{ |
||||
//Input checking
|
||||
int objType = objectPoints.getMat().type(); |
||||
CV_CheckType(objType, objType == CV_32FC3 || objType == CV_64FC3, |
||||
"Type of objectPoints must be CV_32FC3 or CV_64FC3"); |
||||
|
||||
int imgType = imagePoints.getMat().type(); |
||||
CV_CheckType(imgType, imgType == CV_32FC2 || imgType == CV_64FC2, |
||||
"Type of imagePoints must be CV_32FC2 or CV_64FC2"); |
||||
|
||||
CV_Assert(objectPoints.rows() == 1 || objectPoints.cols() == 1); |
||||
CV_Assert(objectPoints.rows() >= 3 || objectPoints.cols() >= 3); |
||||
CV_Assert(imagePoints.rows() == 1 || imagePoints.cols() == 1); |
||||
CV_Assert(imagePoints.rows() * imagePoints.cols() == objectPoints.rows() * objectPoints.cols()); |
||||
|
||||
Mat _imagePoints; |
||||
if (imgType == CV_32FC2) |
||||
{ |
||||
imagePoints.getMat().convertTo(_imagePoints, CV_64F); |
||||
} |
||||
else |
||||
{ |
||||
_imagePoints = imagePoints.getMat(); |
||||
} |
||||
|
||||
Mat _objectPoints; |
||||
if (objType == CV_32FC3) |
||||
{ |
||||
objectPoints.getMat().convertTo(_objectPoints, CV_64F); |
||||
} |
||||
else |
||||
{ |
||||
_objectPoints = objectPoints.getMat(); |
||||
} |
||||
|
||||
num_null_vectors_ = -1; |
||||
num_solutions_ = 0; |
||||
|
||||
computeOmega(_objectPoints, _imagePoints); |
||||
solveInternal(); |
||||
|
||||
int depthRot = rvecs.fixedType() ? rvecs.depth() : CV_64F; |
||||
int depthTrans = tvecs.fixedType() ? tvecs.depth() : CV_64F; |
||||
|
||||
rvecs.create(num_solutions_, 1, CV_MAKETYPE(depthRot, rvecs.fixedType() && rvecs.kind() == _InputArray::STD_VECTOR ? 3 : 1)); |
||||
tvecs.create(num_solutions_, 1, CV_MAKETYPE(depthTrans, tvecs.fixedType() && tvecs.kind() == _InputArray::STD_VECTOR ? 3 : 1)); |
||||
|
||||
for (int i = 0; i < num_solutions_; i++) |
||||
{ |
||||
|
||||
Mat rvec; |
||||
Mat rotation = Mat(solutions_[i].r_hat).reshape(1, 3); |
||||
Rodrigues(rotation, rvec); |
||||
|
||||
rvecs.getMatRef(i) = rvec; |
||||
tvecs.getMatRef(i) = Mat(solutions_[i].t); |
||||
} |
||||
} |
||||
|
||||
void PoseSolver::computeOmega(InputArray objectPoints, InputArray imagePoints) |
||||
{ |
||||
omega_ = cv::Matx<double, 9, 9>::zeros(); |
||||
cv::Matx<double, 3, 9> qa_sum = cv::Matx<double, 3, 9>::zeros(); |
||||
|
||||
cv::Point2d sum_img(0, 0); |
||||
cv::Point3d sum_obj(0, 0, 0); |
||||
double sq_norm_sum = 0; |
||||
|
||||
Mat _imagePoints = imagePoints.getMat(); |
||||
Mat _objectPoints = objectPoints.getMat(); |
||||
|
||||
int n = _objectPoints.cols * _objectPoints.rows; |
||||
|
||||
for (int i = 0; i < n; i++) |
||||
{ |
||||
const cv::Point2d& img_pt = _imagePoints.at<cv::Point2d>(i); |
||||
const cv::Point3d& obj_pt = _objectPoints.at<cv::Point3d>(i); |
||||
|
||||
sum_img += img_pt; |
||||
sum_obj += obj_pt; |
||||
|
||||
const double& x = img_pt.x, & y = img_pt.y; |
||||
const double& X = obj_pt.x, & Y = obj_pt.y, & Z = obj_pt.z; |
||||
double sq_norm = x * x + y * y; |
||||
sq_norm_sum += sq_norm; |
||||
|
||||
double X2 = X * X, |
||||
XY = X * Y, |
||||
XZ = X * Z, |
||||
Y2 = Y * Y, |
||||
YZ = Y * Z, |
||||
Z2 = Z * Z; |
||||
|
||||
omega_(0, 0) += X2; |
||||
omega_(0, 1) += XY; |
||||
omega_(0, 2) += XZ; |
||||
omega_(1, 1) += Y2; |
||||
omega_(1, 2) += YZ; |
||||
omega_(2, 2) += Z2; |
||||
|
||||
|
||||
//Populating this manually saves operations by only calculating upper triangle
|
||||
omega_(0, 6) += -x * X2; omega_(0, 7) += -x * XY; omega_(0, 8) += -x * XZ; |
||||
omega_(1, 7) += -x * Y2; omega_(1, 8) += -x * YZ; |
||||
omega_(2, 8) += -x * Z2; |
||||
|
||||
omega_(3, 6) += -y * X2; omega_(3, 7) += -y * XY; omega_(3, 8) += -y * XZ; |
||||
omega_(4, 7) += -y * Y2; omega_(4, 8) += -y * YZ; |
||||
omega_(5, 8) += -y * Z2; |
||||
|
||||
|
||||
omega_(6, 6) += sq_norm * X2; omega_(6, 7) += sq_norm * XY; omega_(6, 8) += sq_norm * XZ; |
||||
omega_(7, 7) += sq_norm * Y2; omega_(7, 8) += sq_norm * YZ; |
||||
omega_(8, 8) += sq_norm * Z2; |
||||
|
||||
//Compute qa_sum
|
||||
qa_sum(0, 0) += X; qa_sum(0, 1) += Y; qa_sum(0, 2) += Z; |
||||
qa_sum(1, 3) += X; qa_sum(1, 4) += Y; qa_sum(1, 5) += Z; |
||||
|
||||
qa_sum(0, 6) += -x * X; qa_sum(0, 7) += -x * Y; qa_sum(0, 8) += -x * Z; |
||||
qa_sum(1, 6) += -y * X; qa_sum(1, 7) += -y * Y; qa_sum(1, 8) += -y * Z; |
||||
|
||||
qa_sum(2, 0) += -x * X; qa_sum(2, 1) += -x * Y; qa_sum(2, 2) += -x * Z; |
||||
qa_sum(2, 3) += -y * X; qa_sum(2, 4) += -y * Y; qa_sum(2, 5) += -y * Z; |
||||
|
||||
qa_sum(2, 6) += sq_norm * X; qa_sum(2, 7) += sq_norm * Y; qa_sum(2, 8) += sq_norm * Z; |
||||
} |
||||
|
||||
|
||||
omega_(1, 6) = omega_(0, 7); omega_(2, 6) = omega_(0, 8); omega_(2, 7) = omega_(1, 8); |
||||
omega_(4, 6) = omega_(3, 7); omega_(5, 6) = omega_(3, 8); omega_(5, 7) = omega_(4, 8); |
||||
omega_(7, 6) = omega_(6, 7); omega_(8, 6) = omega_(6, 8); omega_(8, 7) = omega_(7, 8); |
||||
|
||||
|
||||
omega_(3, 3) = omega_(0, 0); omega_(3, 4) = omega_(0, 1); omega_(3, 5) = omega_(0, 2); |
||||
omega_(4, 4) = omega_(1, 1); omega_(4, 5) = omega_(1, 2); |
||||
omega_(5, 5) = omega_(2, 2); |
||||
|
||||
//Mirror upper triangle to lower triangle
|
||||
for (int r = 0; r < 9; r++) |
||||
{ |
||||
for (int c = 0; c < r; c++) |
||||
{ |
||||
omega_(r, c) = omega_(c, r); |
||||
} |
||||
} |
||||
|
||||
cv::Matx<double, 3, 3> q; |
||||
q(0, 0) = n; q(0, 1) = 0; q(0, 2) = -sum_img.x; |
||||
q(1, 0) = 0; q(1, 1) = n; q(1, 2) = -sum_img.y; |
||||
q(2, 0) = -sum_img.x; q(2, 1) = -sum_img.y; q(2, 2) = sq_norm_sum; |
||||
|
||||
double inv_n = 1.0 / n; |
||||
double detQ = n * (n * sq_norm_sum - sum_img.y * sum_img.y - sum_img.x * sum_img.x); |
||||
double point_coordinate_variance = detQ * inv_n * inv_n * inv_n; |
||||
|
||||
CV_Assert(point_coordinate_variance >= POINT_VARIANCE_THRESHOLD); |
||||
|
||||
Matx<double, 3, 3> q_inv; |
||||
analyticalInverse3x3Symm(q, q_inv); |
||||
|
||||
p_ = -q_inv * qa_sum; |
||||
|
||||
omega_ += qa_sum.t() * p_; |
||||
|
||||
cv::SVD omega_svd(omega_, cv::SVD::FULL_UV); |
||||
s_ = omega_svd.w; |
||||
u_ = cv::Mat(omega_svd.vt.t()); |
||||
|
||||
CV_Assert(s_(0) >= 1e-7); |
||||
|
||||
while (s_(7 - num_null_vectors_) < RANK_TOLERANCE) num_null_vectors_++; |
||||
|
||||
CV_Assert(++num_null_vectors_ <= 6); |
||||
|
||||
point_mean_ = cv::Vec3d(sum_obj.x / n, sum_obj.y / n, sum_obj.z / n); |
||||
} |
||||
|
||||
void PoseSolver::solveInternal() |
||||
{ |
||||
double min_sq_err = std::numeric_limits<double>::max(); |
||||
int num_eigen_points = num_null_vectors_ > 0 ? num_null_vectors_ : 1; |
||||
|
||||
for (int i = 9 - num_eigen_points; i < 9; i++) |
||||
{ |
||||
const cv::Matx<double, 9, 1> e = SQRT3 * u_.col(i); |
||||
double orthogonality_sq_err = orthogonalityError(e); |
||||
|
||||
SQPSolution solutions[2]; |
||||
|
||||
//If e is orthogonal, we can skip SQP
|
||||
if (orthogonality_sq_err < ORTHOGONALITY_SQUARED_ERROR_THRESHOLD) |
||||
{ |
||||
solutions[0].r_hat = det3x3(e) * e; |
||||
solutions[0].t = p_ * solutions[0].r_hat; |
||||
checkSolution(solutions[0], min_sq_err); |
||||
} |
||||
else |
||||
{ |
||||
Matx<double, 9, 1> r; |
||||
nearestRotationMatrix(e, r); |
||||
solutions[0] = runSQP(r); |
||||
solutions[0].t = p_ * solutions[0].r_hat; |
||||
checkSolution(solutions[0], min_sq_err); |
||||
|
||||
nearestRotationMatrix(-e, r); |
||||
solutions[1] = runSQP(r); |
||||
solutions[1].t = p_ * solutions[1].r_hat; |
||||
checkSolution(solutions[1], min_sq_err); |
||||
} |
||||
} |
||||
|
||||
int c = 1; |
||||
|
||||
while (min_sq_err > 3 * s_[9 - num_eigen_points - c] && 9 - num_eigen_points - c > 0) |
||||
{ |
||||
int index = 9 - num_eigen_points - c; |
||||
|
||||
const cv::Matx<double, 9, 1> e = u_.col(index); |
||||
SQPSolution solutions[2]; |
||||
|
||||
Matx<double, 9, 1> r; |
||||
nearestRotationMatrix(e, r); |
||||
solutions[0] = runSQP(r); |
||||
solutions[0].t = p_ * solutions[0].r_hat; |
||||
checkSolution(solutions[0], min_sq_err); |
||||
|
||||
nearestRotationMatrix(-e, r); |
||||
solutions[1] = runSQP(r); |
||||
solutions[1].t = p_ * solutions[1].r_hat; |
||||
checkSolution(solutions[1], min_sq_err); |
||||
|
||||
c++; |
||||
} |
||||
} |
||||
|
||||
PoseSolver::SQPSolution PoseSolver::runSQP(const cv::Matx<double, 9, 1>& r0) |
||||
{ |
||||
cv::Matx<double, 9, 1> r = r0; |
||||
|
||||
double delta_squared_norm = std::numeric_limits<double>::max(); |
||||
cv::Matx<double, 9, 1> delta; |
||||
|
||||
int step = 0; |
||||
while (delta_squared_norm > SQP_SQUARED_TOLERANCE && step++ < SQP_MAX_ITERATION) |
||||
{ |
||||
solveSQPSystem(r, delta); |
||||
r += delta; |
||||
delta_squared_norm = cv::norm(delta, cv::NORM_L2SQR); |
||||
} |
||||
|
||||
SQPSolution solution; |
||||
|
||||
double det_r = det3x3(r); |
||||
if (det_r < 0) |
||||
{ |
||||
r = -r; |
||||
det_r = -det_r; |
||||
} |
||||
|
||||
if (det_r > SQP_DET_THRESHOLD) |
||||
{ |
||||
nearestRotationMatrix(r, solution.r_hat); |
||||
} |
||||
else |
||||
{ |
||||
solution.r_hat = r; |
||||
} |
||||
|
||||
return solution; |
||||
} |
||||
|
||||
void PoseSolver::solveSQPSystem(const cv::Matx<double, 9, 1>& r, cv::Matx<double, 9, 1>& delta) |
||||
{ |
||||
double sqnorm_r1 = r(0) * r(0) + r(1) * r(1) + r(2) * r(2), |
||||
sqnorm_r2 = r(3) * r(3) + r(4) * r(4) + r(5) * r(5), |
||||
sqnorm_r3 = r(6) * r(6) + r(7) * r(7) + r(8) * r(8); |
||||
double dot_r1r2 = r(0) * r(3) + r(1) * r(4) + r(2) * r(5), |
||||
dot_r1r3 = r(0) * r(6) + r(1) * r(7) + r(2) * r(8), |
||||
dot_r2r3 = r(3) * r(6) + r(4) * r(7) + r(5) * r(8); |
||||
|
||||
cv::Matx<double, 9, 3> N; |
||||
cv::Matx<double, 9, 6> H; |
||||
cv::Matx<double, 6, 6> JH; |
||||
|
||||
computeRowAndNullspace(r, H, N, JH); |
||||
|
||||
cv::Matx<double, 6, 1> g; |
||||
g(0) = 1 - sqnorm_r1; g(1) = 1 - sqnorm_r2; g(2) = 1 - sqnorm_r3; g(3) = -dot_r1r2; g(4) = -dot_r2r3; g(5) = -dot_r1r3; |
||||
|
||||
cv::Matx<double, 6, 1> x; |
||||
x(0) = g(0) / JH(0, 0); |
||||
x(1) = g(1) / JH(1, 1); |
||||
x(2) = g(2) / JH(2, 2); |
||||
x(3) = (g(3) - JH(3, 0) * x(0) - JH(3, 1) * x(1)) / JH(3, 3); |
||||
x(4) = (g(4) - JH(4, 1) * x(1) - JH(4, 2) * x(2) - JH(4, 3) * x(3)) / JH(4, 4); |
||||
x(5) = (g(5) - JH(5, 0) * x(0) - JH(5, 2) * x(2) - JH(5, 3) * x(3) - JH(5, 4) * x(4)) / JH(5, 5); |
||||
|
||||
delta = H * x; |
||||
|
||||
|
||||
cv::Matx<double, 3, 9> nt_omega = N.t() * omega_; |
||||
cv::Matx<double, 3, 3> W = nt_omega * N, W_inv; |
||||
|
||||
analyticalInverse3x3Symm(W, W_inv); |
||||
|
||||
cv::Matx<double, 3, 1> y = -W_inv * nt_omega * (delta + r); |
||||
delta += N * y; |
||||
} |
||||
|
||||
bool PoseSolver::analyticalInverse3x3Symm(const cv::Matx<double, 3, 3>& Q, |
||||
cv::Matx<double, 3, 3>& Qinv, |
||||
const double& threshold) |
||||
{ |
||||
// 1. Get the elements of the matrix
|
||||
double a = Q(0, 0), |
||||
b = Q(1, 0), d = Q(1, 1), |
||||
c = Q(2, 0), e = Q(2, 1), f = Q(2, 2); |
||||
|
||||
// 2. Determinant
|
||||
double t2, t4, t7, t9, t12; |
||||
t2 = e * e; |
||||
t4 = a * d; |
||||
t7 = b * b; |
||||
t9 = b * c; |
||||
t12 = c * c; |
||||
double det = -t4 * f + a * t2 + t7 * f - 2.0 * t9 * e + t12 * d; |
||||
|
||||
if (fabs(det) < threshold) return false; |
||||
|
||||
// 3. Inverse
|
||||
double t15, t20, t24, t30; |
||||
t15 = 1.0 / det; |
||||
t20 = (-b * f + c * e) * t15; |
||||
t24 = (b * e - c * d) * t15; |
||||
t30 = (a * e - t9) * t15; |
||||
Qinv(0, 0) = (-d * f + t2) * t15; |
||||
Qinv(0, 1) = Qinv(1, 0) = -t20; |
||||
Qinv(0, 2) = Qinv(2, 0) = -t24; |
||||
Qinv(1, 1) = -(a * f - t12) * t15; |
||||
Qinv(1, 2) = Qinv(2, 1) = t30; |
||||
Qinv(2, 2) = -(t4 - t7) * t15; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
void PoseSolver::computeRowAndNullspace(const cv::Matx<double, 9, 1>& r, |
||||
cv::Matx<double, 9, 6>& H, |
||||
cv::Matx<double, 9, 3>& N, |
||||
cv::Matx<double, 6, 6>& K, |
||||
const double& norm_threshold) |
||||
{ |
||||
H = cv::Matx<double, 9, 6>::zeros(); |
||||
|
||||
// 1. q1
|
||||
double norm_r1 = sqrt(r(0) * r(0) + r(1) * r(1) + r(2) * r(2)); |
||||
double inv_norm_r1 = norm_r1 > 1e-5 ? 1.0 / norm_r1 : 0.0; |
||||
H(0, 0) = r(0) * inv_norm_r1; |
||||
H(1, 0) = r(1) * inv_norm_r1; |
||||
H(2, 0) = r(2) * inv_norm_r1; |
||||
K(0, 0) = 2 * norm_r1; |
||||
|
||||
// 2. q2
|
||||
double norm_r2 = sqrt(r(3) * r(3) + r(4) * r(4) + r(5) * r(5)); |
||||
double inv_norm_r2 = 1.0 / norm_r2; |
||||
H(3, 1) = r(3) * inv_norm_r2; |
||||
H(4, 1) = r(4) * inv_norm_r2; |
||||
H(5, 1) = r(5) * inv_norm_r2; |
||||
K(1, 0) = 0; |
||||
K(1, 1) = 2 * norm_r2; |
||||
|
||||
// 3. q3 = (r3'*q2)*q2 - (r3'*q1)*q1 ; q3 = q3/norm(q3)
|
||||
double norm_r3 = sqrt(r(6) * r(6) + r(7) * r(7) + r(8) * r(8)); |
||||
double inv_norm_r3 = 1.0 / norm_r3; |
||||
H(6, 2) = r(6) * inv_norm_r3; |
||||
H(7, 2) = r(7) * inv_norm_r3; |
||||
H(8, 2) = r(8) * inv_norm_r3; |
||||
K(2, 0) = K(2, 1) = 0; |
||||
K(2, 2) = 2 * norm_r3; |
||||
|
||||
// 4. q4
|
||||
double dot_j4q1 = r(3) * H(0, 0) + r(4) * H(1, 0) + r(5) * H(2, 0), |
||||
dot_j4q2 = r(0) * H(3, 1) + r(1) * H(4, 1) + r(2) * H(5, 1); |
||||
|
||||
H(0, 3) = r(3) - dot_j4q1 * H(0, 0); |
||||
H(1, 3) = r(4) - dot_j4q1 * H(1, 0); |
||||
H(2, 3) = r(5) - dot_j4q1 * H(2, 0); |
||||
H(3, 3) = r(0) - dot_j4q2 * H(3, 1); |
||||
H(4, 3) = r(1) - dot_j4q2 * H(4, 1); |
||||
H(5, 3) = r(2) - dot_j4q2 * H(5, 1); |
||||
double inv_norm_j4 = 1.0 / sqrt(H(0, 3) * H(0, 3) + H(1, 3) * H(1, 3) + H(2, 3) * H(2, 3) + |
||||
H(3, 3) * H(3, 3) + H(4, 3) * H(4, 3) + H(5, 3) * H(5, 3)); |
||||
|
||||
H(0, 3) *= inv_norm_j4; |
||||
H(1, 3) *= inv_norm_j4; |
||||
H(2, 3) *= inv_norm_j4; |
||||
H(3, 3) *= inv_norm_j4; |
||||
H(4, 3) *= inv_norm_j4; |
||||
H(5, 3) *= inv_norm_j4; |
||||
|
||||
K(3, 0) = r(3) * H(0, 0) + r(4) * H(1, 0) + r(5) * H(2, 0); |
||||
K(3, 1) = r(0) * H(3, 1) + r(1) * H(4, 1) + r(2) * H(5, 1); |
||||
K(3, 2) = 0; |
||||
K(3, 3) = r(3) * H(0, 3) + r(4) * H(1, 3) + r(5) * H(2, 3) + r(0) * H(3, 3) + r(1) * H(4, 3) + r(2) * H(5, 3); |
||||
|
||||
// 5. q5
|
||||
double dot_j5q2 = r(6) * H(3, 1) + r(7) * H(4, 1) + r(8) * H(5, 1); |
||||
double dot_j5q3 = r(3) * H(6, 2) + r(4) * H(7, 2) + r(5) * H(8, 2); |
||||
double dot_j5q4 = r(6) * H(3, 3) + r(7) * H(4, 3) + r(8) * H(5, 3); |
||||
|
||||
H(0, 4) = -dot_j5q4 * H(0, 3); |
||||
H(1, 4) = -dot_j5q4 * H(1, 3); |
||||
H(2, 4) = -dot_j5q4 * H(2, 3); |
||||
H(3, 4) = r(6) - dot_j5q2 * H(3, 1) - dot_j5q4 * H(3, 3); |
||||
H(4, 4) = r(7) - dot_j5q2 * H(4, 1) - dot_j5q4 * H(4, 3); |
||||
H(5, 4) = r(8) - dot_j5q2 * H(5, 1) - dot_j5q4 * H(5, 3); |
||||
H(6, 4) = r(3) - dot_j5q3 * H(6, 2); H(7, 4) = r(4) - dot_j5q3 * H(7, 2); H(8, 4) = r(5) - dot_j5q3 * H(8, 2); |
||||
|
||||
Matx<double, 9, 1> q4 = H.col(4); |
||||
q4 /= cv::norm(q4); |
||||
set<double, 9, 1, 9, 6>(0, 4, H, q4); |
||||
|
||||
K(4, 0) = 0; |
||||
K(4, 1) = r(6) * H(3, 1) + r(7) * H(4, 1) + r(8) * H(5, 1); |
||||
K(4, 2) = r(3) * H(6, 2) + r(4) * H(7, 2) + r(5) * H(8, 2); |
||||
K(4, 3) = r(6) * H(3, 3) + r(7) * H(4, 3) + r(8) * H(5, 3); |
||||
K(4, 4) = r(6) * H(3, 4) + r(7) * H(4, 4) + r(8) * H(5, 4) + r(3) * H(6, 4) + r(4) * H(7, 4) + r(5) * H(8, 4); |
||||
|
||||
|
||||
// 4. q6
|
||||
double dot_j6q1 = r(6) * H(0, 0) + r(7) * H(1, 0) + r(8) * H(2, 0); |
||||
double dot_j6q3 = r(0) * H(6, 2) + r(1) * H(7, 2) + r(2) * H(8, 2); |
||||
double dot_j6q4 = r(6) * H(0, 3) + r(7) * H(1, 3) + r(8) * H(2, 3); |
||||
double dot_j6q5 = r(0) * H(6, 4) + r(1) * H(7, 4) + r(2) * H(8, 4) + r(6) * H(0, 4) + r(7) * H(1, 4) + r(8) * H(2, 4); |
||||
|
||||
H(0, 5) = r(6) - dot_j6q1 * H(0, 0) - dot_j6q4 * H(0, 3) - dot_j6q5 * H(0, 4); |
||||
H(1, 5) = r(7) - dot_j6q1 * H(1, 0) - dot_j6q4 * H(1, 3) - dot_j6q5 * H(1, 4); |
||||
H(2, 5) = r(8) - dot_j6q1 * H(2, 0) - dot_j6q4 * H(2, 3) - dot_j6q5 * H(2, 4); |
||||
|
||||
H(3, 5) = -dot_j6q5 * H(3, 4) - dot_j6q4 * H(3, 3); |
||||
H(4, 5) = -dot_j6q5 * H(4, 4) - dot_j6q4 * H(4, 3); |
||||
H(5, 5) = -dot_j6q5 * H(5, 4) - dot_j6q4 * H(5, 3); |
||||
|
||||
H(6, 5) = r(0) - dot_j6q3 * H(6, 2) - dot_j6q5 * H(6, 4); |
||||
H(7, 5) = r(1) - dot_j6q3 * H(7, 2) - dot_j6q5 * H(7, 4); |
||||
H(8, 5) = r(2) - dot_j6q3 * H(8, 2) - dot_j6q5 * H(8, 4); |
||||
|
||||
Matx<double, 9, 1> q5 = H.col(5); |
||||
q5 /= cv::norm(q5); |
||||
set<double, 9, 1, 9, 6>(0, 5, H, q5); |
||||
|
||||
K(5, 0) = r(6) * H(0, 0) + r(7) * H(1, 0) + r(8) * H(2, 0); |
||||
K(5, 1) = 0; K(5, 2) = r(0) * H(6, 2) + r(1) * H(7, 2) + r(2) * H(8, 2); |
||||
K(5, 3) = r(6) * H(0, 3) + r(7) * H(1, 3) + r(8) * H(2, 3); |
||||
K(5, 4) = r(6) * H(0, 4) + r(7) * H(1, 4) + r(8) * H(2, 4) + r(0) * H(6, 4) + r(1) * H(7, 4) + r(2) * H(8, 4); |
||||
K(5, 5) = r(6) * H(0, 5) + r(7) * H(1, 5) + r(8) * H(2, 5) + r(0) * H(6, 5) + r(1) * H(7, 5) + r(2) * H(8, 5); |
||||
|
||||
// Great! Now H is an orthogonalized, sparse basis of the Jacobian row space and K is filled.
|
||||
//
|
||||
// Now get a projector onto the null space H:
|
||||
const cv::Matx<double, 9, 9> Pn = cv::Matx<double, 9, 9>::eye() - (H * H.t()); |
||||
|
||||
// Now we need to pick 3 columns of P with non-zero norm (> 0.3) and some angle between them (> 0.3).
|
||||
//
|
||||
// Find the 3 columns of Pn with largest norms
|
||||
int index1 = 0, |
||||
index2 = 0, |
||||
index3 = 0; |
||||
double max_norm1 = std::numeric_limits<double>::min(); |
||||
double min_dot12 = std::numeric_limits<double>::max(); |
||||
double min_dot1323 = std::numeric_limits<double>::max(); |
||||
|
||||
|
||||
double col_norms[9]; |
||||
for (int i = 0; i < 9; i++) |
||||
{ |
||||
col_norms[i] = cv::norm(Pn.col(i)); |
||||
if (col_norms[i] >= norm_threshold) |
||||
{ |
||||
if (max_norm1 < col_norms[i]) |
||||
{ |
||||
max_norm1 = col_norms[i]; |
||||
index1 = i; |
||||
} |
||||
} |
||||
} |
||||
|
||||
Matx<double, 9, 1> v1 = Pn.col(index1); |
||||
v1 /= max_norm1; |
||||
set<double, 9, 1, 9, 3>(0, 0, N, v1); |
||||
|
||||
for (int i = 0; i < 9; i++) |
||||
{ |
||||
if (i == index1) continue; |
||||
if (col_norms[i] >= norm_threshold) |
||||
{ |
||||
double cos_v1_x_col = fabs(Pn.col(i).dot(v1) / col_norms[i]); |
||||
|
||||
if (cos_v1_x_col <= min_dot12) |
||||
{ |
||||
index2 = i; |
||||
min_dot12 = cos_v1_x_col; |
||||
} |
||||
} |
||||
} |
||||
|
||||
Matx<double, 9, 1> v2 = Pn.col(index2); |
||||
Matx<double, 9, 1> n0 = N.col(0); |
||||
v2 -= v2.dot(n0) * n0; |
||||
v2 /= cv::norm(v2); |
||||
set<double, 9, 1, 9, 3>(0, 1, N, v2); |
||||
|
||||
for (int i = 0; i < 9; i++) |
||||
{ |
||||
if (i == index2 || i == index1) continue; |
||||
if (col_norms[i] >= norm_threshold) |
||||
{ |
||||
double cos_v1_x_col = fabs(Pn.col(i).dot(v1) / col_norms[i]); |
||||
double cos_v2_x_col = fabs(Pn.col(i).dot(v2) / col_norms[i]); |
||||
|
||||
if (cos_v1_x_col + cos_v2_x_col <= min_dot1323) |
||||
{ |
||||
index3 = i; |
||||
min_dot1323 = cos_v2_x_col + cos_v2_x_col; |
||||
} |
||||
} |
||||
} |
||||
|
||||
Matx<double, 9, 1> v3 = Pn.col(index3); |
||||
Matx<double, 9, 1> n1 = N.col(1); |
||||
v3 -= (v3.dot(n1)) * n1 - (v3.dot(n0)) * n0; |
||||
v3 /= cv::norm(v3); |
||||
set<double, 9, 1, 9, 3>(0, 2, N, v3); |
||||
|
||||
} |
||||
|
||||
// faster nearest rotation computation based on FOAM (see: http://users.ics.forth.gr/~lourakis/publ/2018_iros.pdf )
|
||||
/* Solve the nearest orthogonal approximation problem
|
||||
* i.e., given e, find R minimizing ||R-e||_F |
||||
* |
||||
* The computation borrows from Markley's FOAM algorithm |
||||
* "Attitude Determination Using Vector Observations: A Fast Optimal Matrix Algorithm", J. Astronaut. Sci. |
||||
* |
||||
* See also M. Lourakis: "An Efficient Solution to Absolute Orientation", ICPR 2016 |
||||
* |
||||
* Copyright (C) 2019 Manolis Lourakis (lourakis **at** ics forth gr) |
||||
* Institute of Computer Science, Foundation for Research & Technology - Hellas |
||||
* Heraklion, Crete, Greece. |
||||
*/ |
||||
void PoseSolver::nearestRotationMatrix(const cv::Matx<double, 9, 1>& e, |
||||
cv::Matx<double, 9, 1>& r) |
||||
{ |
||||
int i; |
||||
double l, lprev, det_e, e_sq, adj_e_sq, adj_e[9]; |
||||
|
||||
// e's adjoint
|
||||
adj_e[0] = e(4) * e(8) - e(5) * e(7); adj_e[1] = e(2) * e(7) - e(1) * e(8); adj_e[2] = e(1) * e(5) - e(2) * e(4); |
||||
adj_e[3] = e(5) * e(6) - e(3) * e(8); adj_e[4] = e(0) * e(8) - e(2) * e(6); adj_e[5] = e(2) * e(3) - e(0) * e(5); |
||||
adj_e[6] = e(3) * e(7) - e(4) * e(6); adj_e[7] = e(1) * e(6) - e(0) * e(7); adj_e[8] = e(0) * e(4) - e(1) * e(3); |
||||
|
||||
// det(e), ||e||^2, ||adj(e)||^2
|
||||
det_e = e(0) * e(4) * e(8) - e(0) * e(5) * e(7) - e(1) * e(3) * e(8) + e(2) * e(3) * e(7) + e(1) * e(6) * e(5) - e(2) * e(6) * e(4); |
||||
e_sq = e(0) * e(0) + e(1) * e(1) + e(2) * e(2) + e(3) * e(3) + e(4) * e(4) + e(5) * e(5) + e(6) * e(6) + e(7) * e(7) + e(8) * e(8); |
||||
adj_e_sq = adj_e[0] * adj_e[0] + adj_e[1] * adj_e[1] + adj_e[2] * adj_e[2] + adj_e[3] * adj_e[3] + adj_e[4] * adj_e[4] + adj_e[5] * adj_e[5] + adj_e[6] * adj_e[6] + adj_e[7] * adj_e[7] + adj_e[8] * adj_e[8]; |
||||
|
||||
// compute l_max with Newton-Raphson from FOAM's characteristic polynomial, i.e. eq.(23) - (26)
|
||||
for (i = 200, l = 2.0, lprev = 0.0; fabs(l - lprev) > 1E-12 * fabs(lprev) && i > 0; --i) { |
||||
double tmp, p, pp; |
||||
|
||||
tmp = (l * l - e_sq); |
||||
p = (tmp * tmp - 8.0 * l * det_e - 4.0 * adj_e_sq); |
||||
pp = 8.0 * (0.5 * tmp * l - det_e); |
||||
|
||||
lprev = l; |
||||
l -= p / pp; |
||||
} |
||||
|
||||
// the rotation matrix equals ((l^2 + e_sq)*e + 2*l*adj(e') - 2*e*e'*e) / (l*(l*l-e_sq) - 2*det(e)), i.e. eq.(14) using (18), (19)
|
||||
{ |
||||
// compute (l^2 + e_sq)*e
|
||||
double tmp[9], e_et[9], denom; |
||||
const double a = l * l + e_sq; |
||||
|
||||
// e_et=e*e'
|
||||
e_et[0] = e(0) * e(0) + e(1) * e(1) + e(2) * e(2); |
||||
e_et[1] = e(0) * e(3) + e(1) * e(4) + e(2) * e(5); |
||||
e_et[2] = e(0) * e(6) + e(1) * e(7) + e(2) * e(8); |
||||
|
||||
e_et[3] = e_et[1]; |
||||
e_et[4] = e(3) * e(3) + e(4) * e(4) + e(5) * e(5); |
||||
e_et[5] = e(3) * e(6) + e(4) * e(7) + e(5) * e(8); |
||||
|
||||
e_et[6] = e_et[2]; |
||||
e_et[7] = e_et[5]; |
||||
e_et[8] = e(6) * e(6) + e(7) * e(7) + e(8) * e(8); |
||||
|
||||
// tmp=e_et*e
|
||||
tmp[0] = e_et[0] * e(0) + e_et[1] * e(3) + e_et[2] * e(6); |
||||
tmp[1] = e_et[0] * e(1) + e_et[1] * e(4) + e_et[2] * e(7); |
||||
tmp[2] = e_et[0] * e(2) + e_et[1] * e(5) + e_et[2] * e(8); |
||||
|
||||
tmp[3] = e_et[3] * e(0) + e_et[4] * e(3) + e_et[5] * e(6); |
||||
tmp[4] = e_et[3] * e(1) + e_et[4] * e(4) + e_et[5] * e(7); |
||||
tmp[5] = e_et[3] * e(2) + e_et[4] * e(5) + e_et[5] * e(8); |
||||
|
||||
tmp[6] = e_et[6] * e(0) + e_et[7] * e(3) + e_et[8] * e(6); |
||||
tmp[7] = e_et[6] * e(1) + e_et[7] * e(4) + e_et[8] * e(7); |
||||
tmp[8] = e_et[6] * e(2) + e_et[7] * e(5) + e_et[8] * e(8); |
||||
|
||||
// compute R as (a*e + 2*(l*adj(e)' - tmp))*denom; note that adj(e')=adj(e)'
|
||||
denom = l * (l * l - e_sq) - 2.0 * det_e; |
||||
denom = 1.0 / denom; |
||||
r(0) = (a * e(0) + 2.0 * (l * adj_e[0] - tmp[0])) * denom; |
||||
r(1) = (a * e(1) + 2.0 * (l * adj_e[3] - tmp[1])) * denom; |
||||
r(2) = (a * e(2) + 2.0 * (l * adj_e[6] - tmp[2])) * denom; |
||||
|
||||
r(3) = (a * e(3) + 2.0 * (l * adj_e[1] - tmp[3])) * denom; |
||||
r(4) = (a * e(4) + 2.0 * (l * adj_e[4] - tmp[4])) * denom; |
||||
r(5) = (a * e(5) + 2.0 * (l * adj_e[7] - tmp[5])) * denom; |
||||
|
||||
r(6) = (a * e(6) + 2.0 * (l * adj_e[2] - tmp[6])) * denom; |
||||
r(7) = (a * e(7) + 2.0 * (l * adj_e[5] - tmp[7])) * denom; |
||||
r(8) = (a * e(8) + 2.0 * (l * adj_e[8] - tmp[8])) * denom; |
||||
} |
||||
} |
||||
|
||||
double PoseSolver::det3x3(const cv::Matx<double, 9, 1>& e) |
||||
{ |
||||
return e(0) * e(4) * e(8) + e(1) * e(5) * e(6) + e(2) * e(3) * e(7) |
||||
- e(6) * e(4) * e(2) - e(7) * e(5) * e(0) - e(8) * e(3) * e(1); |
||||
} |
||||
|
||||
inline bool PoseSolver::positiveDepth(const SQPSolution& solution) const |
||||
{ |
||||
const cv::Matx<double, 9, 1>& r = solution.r_hat; |
||||
const cv::Matx<double, 3, 1>& t = solution.t; |
||||
const cv::Vec3d& mean = point_mean_; |
||||
return (r(6) * mean(0) + r(7) * mean(1) + r(8) * mean(2) + t(2) > 0); |
||||
} |
||||
|
||||
void PoseSolver::checkSolution(SQPSolution& solution, double& min_error) |
||||
{ |
||||
if (positiveDepth(solution)) |
||||
{ |
||||
solution.sq_error = (omega_ * solution.r_hat).ddot(solution.r_hat); |
||||
if (fabs(min_error - solution.sq_error) > EQUAL_SQUARED_ERRORS_DIFF) |
||||
{ |
||||
if (min_error > solution.sq_error) |
||||
{ |
||||
min_error = solution.sq_error; |
||||
solutions_[0] = solution; |
||||
num_solutions_ = 1; |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
bool found = false; |
||||
for (int i = 0; i < num_solutions_; i++) |
||||
{ |
||||
if (cv::norm(solutions_[i].r_hat - solution.r_hat, cv::NORM_L2SQR) < EQUAL_VECTORS_SQUARED_DIFF) |
||||
{ |
||||
if (solutions_[i].sq_error > solution.sq_error) |
||||
{ |
||||
solutions_[i] = solution; |
||||
} |
||||
found = true; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (!found) |
||||
{ |
||||
solutions_[num_solutions_++] = solution; |
||||
} |
||||
if (min_error > solution.sq_error) min_error = solution.sq_error; |
||||
} |
||||
} |
||||
} |
||||
|
||||
double PoseSolver::orthogonalityError(const cv::Matx<double, 9, 1>& e) |
||||
{ |
||||
double sq_norm_e1 = e(0) * e(0) + e(1) * e(1) + e(2) * e(2); |
||||
double sq_norm_e2 = e(3) * e(3) + e(4) * e(4) + e(5) * e(5); |
||||
double sq_norm_e3 = e(6) * e(6) + e(7) * e(7) + e(8) * e(8); |
||||
double dot_e1e2 = e(0) * e(3) + e(1) * e(4) + e(2) * e(5); |
||||
double dot_e1e3 = e(0) * e(6) + e(1) * e(7) + e(2) * e(8); |
||||
double dot_e2e3 = e(3) * e(6) + e(4) * e(7) + e(5) * e(8); |
||||
|
||||
return (sq_norm_e1 - 1) * (sq_norm_e1 - 1) + (sq_norm_e2 - 1) * (sq_norm_e2 - 1) + (sq_norm_e3 - 1) * (sq_norm_e3 - 1) + |
||||
2 * (dot_e1e2 * dot_e1e2 + dot_e1e3 * dot_e1e3 + dot_e2e3 * dot_e2e3); |
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,194 @@ |
||||
// 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:
|
||||
|
||||
/*
|
||||
BSD 3-Clause License |
||||
|
||||
Copyright (c) 2020, George Terzakis |
||||
All rights reserved. |
||||
|
||||
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_SQPNP_HPP |
||||
#define OPENCV_CALIB3D_SQPNP_HPP |
||||
|
||||
#include <opencv2/core.hpp> |
||||
|
||||
namespace cv { |
||||
namespace sqpnp { |
||||
|
||||
|
||||
class PoseSolver { |
||||
public: |
||||
/**
|
||||
* @brief PoseSolver constructor |
||||
*/ |
||||
PoseSolver(); |
||||
|
||||
/**
|
||||
* @brief Finds the possible poses of a camera given a set of 3D points |
||||
* and their corresponding 2D image projections. The poses are |
||||
* sorted by lowest squared error (which corresponds to lowest |
||||
* reprojection error). |
||||
* @param objectPoints Array or vector of 3 or more 3D points defined in object coordinates. |
||||
* 1xN/Nx1 3-channel (float or double) where N is the number of points. |
||||
* @param imagePoints Array or vector of corresponding 2D points, 1xN/Nx1 2-channel. |
||||
* @param rvec The output rotation solutions (up to 18 3x1 rotation vectors) |
||||
* @param tvec The output translation solutions (up to 18 3x1 vectors) |
||||
*/ |
||||
void solve(InputArray objectPoints, InputArray imagePoints, OutputArrayOfArrays rvec, |
||||
OutputArrayOfArrays tvec); |
||||
|
||||
private: |
||||
struct SQPSolution |
||||
{ |
||||
cv::Matx<double, 9, 1> r_hat; |
||||
cv::Matx<double, 3, 1> t; |
||||
double sq_error; |
||||
}; |
||||
|
||||
/*
|
||||
* @brief Computes the 9x9 PSD Omega matrix and supporting matrices. |
||||
* @param objectPoints Array or vector of 3 or more 3D points defined in object coordinates. |
||||
* 1xN/Nx1 3-channel (float or double) where N is the number of points. |
||||
* @param imagePoints Array or vector of corresponding 2D points, 1xN/Nx1 2-channel. |
||||
*/ |
||||
void computeOmega(InputArray objectPoints, InputArray imagePoints); |
||||
|
||||
/*
|
||||
* @brief Computes the 9x9 PSD Omega matrix and supporting matrices. |
||||
*/ |
||||
void solveInternal(); |
||||
|
||||
/*
|
||||
* @brief Produces the distance from being orthogonal for a given 3x3 matrix |
||||
* in row-major form. |
||||
* @param e The vector to test representing a 3x3 matrix in row major form. |
||||
* @return The distance the matrix is from being orthogonal. |
||||
*/ |
||||
static double orthogonalityError(const cv::Matx<double, 9, 1>& e); |
||||
|
||||
/*
|
||||
* @brief Processes a solution and sorts it by error. |
||||
* @param solution The solution to evaluate. |
||||
* @param min_error The current minimum error. |
||||
*/ |
||||
void checkSolution(SQPSolution& solution, double& min_error); |
||||
|
||||
/*
|
||||
* @brief Computes the determinant of a matrix stored in row-major format. |
||||
* @param e Vector representing a 3x3 matrix stored in row-major format. |
||||
* @return The determinant of the matrix. |
||||
*/ |
||||
static double det3x3(const cv::Matx<double, 9, 1>& e); |
||||
|
||||
/*
|
||||
* @brief Tests the cheirality for a given solution. |
||||
* @param solution The solution to evaluate. |
||||
*/ |
||||
inline bool positiveDepth(const SQPSolution& solution) const; |
||||
|
||||
/*
|
||||
* @brief Determines the nearest rotation matrix to a given rotaiton matrix. |
||||
* Input and output are 9x1 vector representing a vector stored in row-major |
||||
* form. |
||||
* @param e The input 3x3 matrix stored in a vector in row-major form. |
||||
* @param r The nearest rotation matrix to the input e (again in row-major form). |
||||
*/ |
||||
static void nearestRotationMatrix(const cv::Matx<double, 9, 1>& e, |
||||
cv::Matx<double, 9, 1>& r); |
||||
|
||||
/*
|
||||
* @brief Runs the sequential quadratic programming on orthogonal matrices. |
||||
* @param r0 The start point of the solver. |
||||
*/ |
||||
SQPSolution runSQP(const cv::Matx<double, 9, 1>& r0); |
||||
|
||||
/*
|
||||
* @brief Steps down the gradient for the given matrix r to solve the SQP system. |
||||
* @param r The current matrix step. |
||||
* @param delta The next step down the gradient. |
||||
*/ |
||||
void solveSQPSystem(const cv::Matx<double, 9, 1>& r, cv::Matx<double, 9, 1>& delta); |
||||
|
||||
/*
|
||||
* @brief Analytically computes the inverse of a symmetric 3x3 matrix using the |
||||
* lower triangle. |
||||
* @param Q The matrix to invert. |
||||
* @param Qinv The inverse of Q. |
||||
* @param threshold The threshold to determine if Q is singular and non-invertible. |
||||
*/ |
||||
bool analyticalInverse3x3Symm(const cv::Matx<double, 3, 3>& Q, |
||||
cv::Matx<double, 3, 3>& Qinv, |
||||
const double& threshold = 1e-8); |
||||
|
||||
/*
|
||||
* @brief Computes the 3D null space and 6D normal space of the constraint Jacobian |
||||
* at a 9D vector r (representing a rank-3 matrix). Note that K is lower |
||||
* triangular so upper triangle is undefined. |
||||
* @param r 9D vector representing a rank-3 matrix. |
||||
* @param H 6D row space of the constraint Jacobian at r. |
||||
* @param N 3D null space of the constraint Jacobian at r. |
||||
* @param K The constraint Jacobian at r. |
||||
* @param norm_threshold Threshold for column vector norm of Pn (the projection onto the null space |
||||
* of the constraint Jacobian). |
||||
*/ |
||||
void computeRowAndNullspace(const cv::Matx<double, 9, 1>& r, |
||||
cv::Matx<double, 9, 6>& H, |
||||
cv::Matx<double, 9, 3>& N, |
||||
cv::Matx<double, 6, 6>& K, |
||||
const double& norm_threshold = 0.1); |
||||
|
||||
static const double RANK_TOLERANCE; |
||||
static const double SQP_SQUARED_TOLERANCE; |
||||
static const double SQP_DET_THRESHOLD; |
||||
static const double ORTHOGONALITY_SQUARED_ERROR_THRESHOLD; |
||||
static const double EQUAL_VECTORS_SQUARED_DIFF; |
||||
static const double EQUAL_SQUARED_ERRORS_DIFF; |
||||
static const double POINT_VARIANCE_THRESHOLD; |
||||
static const int SQP_MAX_ITERATION; |
||||
static const double SQRT3; |
||||
|
||||
cv::Matx<double, 9, 9> omega_; |
||||
cv::Vec<double, 9> s_; |
||||
cv::Matx<double, 9, 9> u_; |
||||
cv::Matx<double, 3, 9> p_; |
||||
cv::Vec3d point_mean_; |
||||
int num_null_vectors_; |
||||
|
||||
SQPSolution solutions_[18]; |
||||
int num_solutions_; |
||||
|
||||
}; |
||||
|
||||
} |
||||
} |
||||
|
||||
#endif |
Loading…
Reference in new issue