mirror of
https://github.com/opencv/opencv.git
synced 2024-12-02 16:00:17 +08:00
426 lines
15 KiB
C++
426 lines
15 KiB
C++
#include "ap3p.h"
|
|
|
|
#include <cmath>
|
|
#include <complex>
|
|
#if defined (_MSC_VER) && (_MSC_VER <= 1700)
|
|
static inline double cbrt(double x) { return (double)cv::cubeRoot((float)x); };
|
|
#endif
|
|
|
|
using namespace std;
|
|
|
|
namespace {
|
|
void solveQuartic(const double *factors, double *realRoots) {
|
|
const double &a4 = factors[0];
|
|
const double &a3 = factors[1];
|
|
const double &a2 = factors[2];
|
|
const double &a1 = factors[3];
|
|
const double &a0 = factors[4];
|
|
|
|
double a4_2 = a4 * a4;
|
|
double a3_2 = a3 * a3;
|
|
double a4_3 = a4_2 * a4;
|
|
double a2a4 = a2 * a4;
|
|
|
|
double p4 = (8 * a2a4 - 3 * a3_2) / (8 * a4_2);
|
|
double q4 = (a3_2 * a3 - 4 * a2a4 * a3 + 8 * a1 * a4_2) / (8 * a4_3);
|
|
double r4 = (256 * a0 * a4_3 - 3 * (a3_2 * a3_2) - 64 * a1 * a3 * a4_2 + 16 * a2a4 * a3_2) / (256 * (a4_3 * a4));
|
|
|
|
double p3 = ((p4 * p4) / 12 + r4) / 3; // /=-3
|
|
double q3 = (72 * r4 * p4 - 2 * p4 * p4 * p4 - 27 * q4 * q4) / 432; // /=2
|
|
|
|
double t; // *=2
|
|
complex<double> w;
|
|
if (q3 >= 0)
|
|
w = -sqrt(static_cast<complex<double> >(q3 * q3 - p3 * p3 * p3)) - q3;
|
|
else
|
|
w = sqrt(static_cast<complex<double> >(q3 * q3 - p3 * p3 * p3)) - q3;
|
|
if (w.imag() == 0.0) {
|
|
w.real(cbrt(w.real()));
|
|
t = 2.0 * (w.real() + p3 / w.real());
|
|
} else {
|
|
w = pow(w, 1.0 / 3);
|
|
t = 4.0 * w.real();
|
|
}
|
|
|
|
complex<double> sqrt_2m = sqrt(static_cast<complex<double> >(-2 * p4 / 3 + t));
|
|
double B_4A = -a3 / (4 * a4);
|
|
double complex1 = 4 * p4 / 3 + t;
|
|
#if defined(__clang__) && defined(__arm__) && (__clang_major__ == 3 || __clang_minor__ == 4) && !defined(__ANDROID__)
|
|
// details: https://github.com/opencv/opencv/issues/11135
|
|
// details: https://github.com/opencv/opencv/issues/11056
|
|
complex<double> complex2 = 2 * q4;
|
|
complex2 = complex<double>(complex2.real() / sqrt_2m.real(), 0);
|
|
#else
|
|
complex<double> complex2 = 2 * q4 / sqrt_2m;
|
|
#endif
|
|
double sqrt_2m_rh = sqrt_2m.real() / 2;
|
|
double sqrt1 = sqrt(-(complex1 + complex2)).real() / 2;
|
|
realRoots[0] = B_4A + sqrt_2m_rh + sqrt1;
|
|
realRoots[1] = B_4A + sqrt_2m_rh - sqrt1;
|
|
double sqrt2 = sqrt(-(complex1 - complex2)).real() / 2;
|
|
realRoots[2] = B_4A - sqrt_2m_rh + sqrt2;
|
|
realRoots[3] = B_4A - sqrt_2m_rh - sqrt2;
|
|
}
|
|
|
|
void polishQuarticRoots(const double *coeffs, double *roots) {
|
|
const int iterations = 2;
|
|
for (int i = 0; i < iterations; ++i) {
|
|
for (int j = 0; j < 4; ++j) {
|
|
double error =
|
|
(((coeffs[0] * roots[j] + coeffs[1]) * roots[j] + coeffs[2]) * roots[j] + coeffs[3]) * roots[j] +
|
|
coeffs[4];
|
|
double
|
|
derivative =
|
|
((4 * coeffs[0] * roots[j] + 3 * coeffs[1]) * roots[j] + 2 * coeffs[2]) * roots[j] + coeffs[3];
|
|
roots[j] -= error / derivative;
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void vect_cross(const double *a, const double *b, double *result) {
|
|
result[0] = a[1] * b[2] - a[2] * b[1];
|
|
result[1] = -(a[0] * b[2] - a[2] * b[0]);
|
|
result[2] = a[0] * b[1] - a[1] * b[0];
|
|
}
|
|
|
|
inline double vect_dot(const double *a, const double *b) {
|
|
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
}
|
|
|
|
inline double vect_norm(const double *a) {
|
|
return sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
|
|
}
|
|
|
|
inline void vect_scale(const double s, const double *a, double *result) {
|
|
result[0] = a[0] * s;
|
|
result[1] = a[1] * s;
|
|
result[2] = a[2] * s;
|
|
}
|
|
|
|
inline void vect_sub(const double *a, const double *b, double *result) {
|
|
result[0] = a[0] - b[0];
|
|
result[1] = a[1] - b[1];
|
|
result[2] = a[2] - b[2];
|
|
}
|
|
|
|
inline void vect_divide(const double *a, const double d, double *result) {
|
|
result[0] = a[0] / d;
|
|
result[1] = a[1] / d;
|
|
result[2] = a[2] / d;
|
|
}
|
|
|
|
inline void mat_mult(const double a[3][3], const double b[3][3], double result[3][3]) {
|
|
result[0][0] = a[0][0] * b[0][0] + a[0][1] * b[1][0] + a[0][2] * b[2][0];
|
|
result[0][1] = a[0][0] * b[0][1] + a[0][1] * b[1][1] + a[0][2] * b[2][1];
|
|
result[0][2] = a[0][0] * b[0][2] + a[0][1] * b[1][2] + a[0][2] * b[2][2];
|
|
|
|
result[1][0] = a[1][0] * b[0][0] + a[1][1] * b[1][0] + a[1][2] * b[2][0];
|
|
result[1][1] = a[1][0] * b[0][1] + a[1][1] * b[1][1] + a[1][2] * b[2][1];
|
|
result[1][2] = a[1][0] * b[0][2] + a[1][1] * b[1][2] + a[1][2] * b[2][2];
|
|
|
|
result[2][0] = a[2][0] * b[0][0] + a[2][1] * b[1][0] + a[2][2] * b[2][0];
|
|
result[2][1] = a[2][0] * b[0][1] + a[2][1] * b[1][1] + a[2][2] * b[2][1];
|
|
result[2][2] = a[2][0] * b[0][2] + a[2][1] * b[1][2] + a[2][2] * b[2][2];
|
|
}
|
|
}
|
|
|
|
namespace cv {
|
|
void ap3p::init_inverse_parameters() {
|
|
inv_fx = 1. / fx;
|
|
inv_fy = 1. / fy;
|
|
cx_fx = cx / fx;
|
|
cy_fy = cy / fy;
|
|
}
|
|
|
|
ap3p::ap3p(cv::Mat cameraMatrix) {
|
|
if (cameraMatrix.depth() == CV_32F)
|
|
init_camera_parameters<float>(cameraMatrix);
|
|
else
|
|
init_camera_parameters<double>(cameraMatrix);
|
|
init_inverse_parameters();
|
|
}
|
|
|
|
ap3p::ap3p(double _fx, double _fy, double _cx, double _cy) {
|
|
fx = _fx;
|
|
fy = _fy;
|
|
cx = _cx;
|
|
cy = _cy;
|
|
init_inverse_parameters();
|
|
}
|
|
|
|
// This algorithm is from "Tong Ke, Stergios Roumeliotis, An Efficient Algebraic Solution to the Perspective-Three-Point Problem" (Accepted by CVPR 2017)
|
|
// See https://arxiv.org/pdf/1701.08237.pdf
|
|
// featureVectors: The 3 bearing measurements (normalized) stored as column vectors
|
|
// 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],
|
|
double solutionsR[4][3][3],
|
|
double solutionsT[4][3]) {
|
|
|
|
//world point vectors
|
|
double w1[3] = {worldPoints[0][0], worldPoints[1][0], worldPoints[2][0]};
|
|
double w2[3] = {worldPoints[0][1], worldPoints[1][1], worldPoints[2][1]};
|
|
double w3[3] = {worldPoints[0][2], worldPoints[1][2], worldPoints[2][2]};
|
|
// k1
|
|
double u0[3];
|
|
vect_sub(w1, w2, u0);
|
|
|
|
double nu0 = vect_norm(u0);
|
|
double k1[3];
|
|
vect_divide(u0, nu0, k1);
|
|
// bi
|
|
double b1[3] = {featureVectors[0][0], featureVectors[1][0], featureVectors[2][0]};
|
|
double b2[3] = {featureVectors[0][1], featureVectors[1][1], featureVectors[2][1]};
|
|
double b3[3] = {featureVectors[0][2], featureVectors[1][2], featureVectors[2][2]};
|
|
// k3,tz
|
|
double k3[3];
|
|
vect_cross(b1, b2, k3);
|
|
double nk3 = vect_norm(k3);
|
|
vect_divide(k3, nk3, k3);
|
|
|
|
double tz[3];
|
|
vect_cross(b1, k3, tz);
|
|
// ui,vi
|
|
double v1[3];
|
|
vect_cross(b1, b3, v1);
|
|
double v2[3];
|
|
vect_cross(b2, b3, v2);
|
|
|
|
double u1[3];
|
|
vect_sub(w1, w3, u1);
|
|
// coefficients related terms
|
|
double u1k1 = vect_dot(u1, k1);
|
|
double k3b3 = vect_dot(k3, b3);
|
|
// f1i
|
|
double f11 = k3b3;
|
|
double f13 = vect_dot(k3, v1);
|
|
double f15 = -u1k1 * f11;
|
|
//delta
|
|
double nl[3];
|
|
vect_cross(u1, k1, nl);
|
|
double delta = vect_norm(nl);
|
|
vect_divide(nl, delta, nl);
|
|
f11 *= delta;
|
|
f13 *= delta;
|
|
// f2i
|
|
double u2k1 = u1k1 - nu0;
|
|
double f21 = vect_dot(tz, v2);
|
|
double f22 = nk3 * k3b3;
|
|
double f23 = vect_dot(k3, v2);
|
|
double f24 = u2k1 * f22;
|
|
double f25 = -u2k1 * f21;
|
|
f21 *= delta;
|
|
f22 *= delta;
|
|
f23 *= delta;
|
|
double g1 = f13 * f22;
|
|
double g2 = f13 * f25 - f15 * f23;
|
|
double g3 = f11 * f23 - f13 * f21;
|
|
double g4 = -f13 * f24;
|
|
double g5 = f11 * f22;
|
|
double g6 = f11 * f25 - f15 * f21;
|
|
double g7 = -f15 * f24;
|
|
double coeffs[5] = {g5 * g5 + g1 * g1 + g3 * g3,
|
|
2 * (g5 * g6 + g1 * g2 + g3 * g4),
|
|
g6 * g6 + 2 * g5 * g7 + g2 * g2 + g4 * g4 - g1 * g1 - g3 * g3,
|
|
2 * (g6 * g7 - g1 * g2 - g3 * g4),
|
|
g7 * g7 - g2 * g2 - g4 * g4};
|
|
double s[4];
|
|
solveQuartic(coeffs, s);
|
|
polishQuarticRoots(coeffs, s);
|
|
|
|
double temp[3];
|
|
vect_cross(k1, nl, temp);
|
|
|
|
double Ck1nl[3][3] =
|
|
{{k1[0], nl[0], temp[0]},
|
|
{k1[1], nl[1], temp[1]},
|
|
{k1[2], nl[2], temp[2]}};
|
|
|
|
double Cb1k3tzT[3][3] =
|
|
{{b1[0], b1[1], b1[2]},
|
|
{k3[0], k3[1], k3[2]},
|
|
{tz[0], tz[1], tz[2]}};
|
|
|
|
double b3p[3];
|
|
vect_scale((delta / k3b3), b3, b3p);
|
|
|
|
int nb_solutions = 0;
|
|
for (int i = 0; i < 4; ++i) {
|
|
double ctheta1p = s[i];
|
|
if (abs(ctheta1p) > 1)
|
|
continue;
|
|
double stheta1p = sqrt(1 - ctheta1p * ctheta1p);
|
|
stheta1p = (k3b3 > 0) ? stheta1p : -stheta1p;
|
|
double ctheta3 = g1 * ctheta1p + g2;
|
|
double stheta3 = g3 * ctheta1p + g4;
|
|
double ntheta3 = stheta1p / ((g5 * ctheta1p + g6) * ctheta1p + g7);
|
|
ctheta3 *= ntheta3;
|
|
stheta3 *= ntheta3;
|
|
|
|
double C13[3][3] =
|
|
{{ctheta3, 0, -stheta3},
|
|
{stheta1p * stheta3, ctheta1p, stheta1p * ctheta3},
|
|
{ctheta1p * stheta3, -stheta1p, ctheta1p * ctheta3}};
|
|
|
|
double temp_matrix[3][3];
|
|
double R[3][3];
|
|
mat_mult(Ck1nl, C13, temp_matrix);
|
|
mat_mult(temp_matrix, Cb1k3tzT, R);
|
|
|
|
// R' * p3
|
|
double rp3[3] =
|
|
{w3[0] * R[0][0] + w3[1] * R[1][0] + w3[2] * R[2][0],
|
|
w3[0] * R[0][1] + w3[1] * R[1][1] + w3[2] * R[2][1],
|
|
w3[0] * R[0][2] + w3[1] * R[1][2] + w3[2] * R[2][2]};
|
|
|
|
double pxstheta1p[3];
|
|
vect_scale(stheta1p, b3p, pxstheta1p);
|
|
|
|
vect_sub(pxstheta1p, rp3, solutionsT[nb_solutions]);
|
|
|
|
solutionsR[nb_solutions][0][0] = R[0][0];
|
|
solutionsR[nb_solutions][1][0] = R[0][1];
|
|
solutionsR[nb_solutions][2][0] = R[0][2];
|
|
solutionsR[nb_solutions][0][1] = R[1][0];
|
|
solutionsR[nb_solutions][1][1] = R[1][1];
|
|
solutionsR[nb_solutions][2][1] = R[1][2];
|
|
solutionsR[nb_solutions][0][2] = R[2][0];
|
|
solutionsR[nb_solutions][1][2] = R[2][1];
|
|
solutionsR[nb_solutions][2][2] = R[2][2];
|
|
|
|
nb_solutions++;
|
|
}
|
|
|
|
return nb_solutions;
|
|
}
|
|
|
|
bool ap3p::solve(cv::Mat &R, cv::Mat &tvec, const cv::Mat &opoints, const cv::Mat &ipoints) {
|
|
CV_INSTRUMENT_REGION();
|
|
|
|
double rotation_matrix[3][3], translation[3];
|
|
std::vector<double> points;
|
|
if (opoints.depth() == ipoints.depth()) {
|
|
if (opoints.depth() == CV_32F)
|
|
extract_points<cv::Point3f, cv::Point2f>(opoints, ipoints, points);
|
|
else
|
|
extract_points<cv::Point3d, cv::Point2d>(opoints, ipoints, points);
|
|
} else if (opoints.depth() == CV_32F)
|
|
extract_points<cv::Point3f, cv::Point2d>(opoints, ipoints, points);
|
|
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]);
|
|
cv::Mat(3, 1, CV_64F, translation).copyTo(tvec);
|
|
cv::Mat(3, 3, CV_64F, rotation_matrix).copyTo(R);
|
|
return result;
|
|
}
|
|
|
|
int ap3p::solve(std::vector<cv::Mat> &Rs, std::vector<cv::Mat> &tvecs, const cv::Mat &opoints, const cv::Mat &ipoints) {
|
|
CV_INSTRUMENT_REGION();
|
|
|
|
double rotation_matrix[4][3][3], translation[4][3];
|
|
std::vector<double> points;
|
|
if (opoints.depth() == ipoints.depth()) {
|
|
if (opoints.depth() == CV_32F)
|
|
extract_points<cv::Point3f, cv::Point2f>(opoints, ipoints, points);
|
|
else
|
|
extract_points<cv::Point3d, cv::Point2d>(opoints, ipoints, points);
|
|
} else if (opoints.depth() == CV_32F)
|
|
extract_points<cv::Point3f, cv::Point2d>(opoints, ipoints, points);
|
|
else
|
|
extract_points<cv::Point3d, cv::Point2f>(opoints, ipoints, points);
|
|
|
|
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]);
|
|
|
|
for (int i = 0; i < solutions; i++) {
|
|
cv::Mat R, tvec;
|
|
cv::Mat(3, 1, CV_64F, translation[i]).copyTo(tvec);
|
|
cv::Mat(3, 3, CV_64F, rotation_matrix[i]).copyTo(R);
|
|
|
|
Rs.push_back(R);
|
|
tvecs.push_back(tvec);
|
|
}
|
|
|
|
return solutions;
|
|
}
|
|
|
|
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) {
|
|
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);
|
|
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];
|
|
}
|
|
|
|
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) {
|
|
double mk0, mk1, mk2;
|
|
double norm;
|
|
|
|
mu0 = inv_fx * mu0 - cx_fx;
|
|
mv0 = inv_fy * mv0 - cy_fy;
|
|
norm = sqrt(mu0 * mu0 + mv0 * mv0 + 1);
|
|
mk0 = 1. / norm;
|
|
mu0 *= mk0;
|
|
mv0 *= mk0;
|
|
|
|
mu1 = inv_fx * mu1 - cx_fx;
|
|
mv1 = inv_fy * mv1 - cy_fy;
|
|
norm = sqrt(mu1 * mu1 + mv1 * mv1 + 1);
|
|
mk1 = 1. / norm;
|
|
mu1 *= mk1;
|
|
mv1 *= mk1;
|
|
|
|
mu2 = inv_fx * mu2 - cx_fx;
|
|
mv2 = inv_fy * mv2 - cy_fy;
|
|
norm = sqrt(mu2 * mu2 + mv2 * mv2 + 1);
|
|
mk2 = 1. / norm;
|
|
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}};
|
|
|
|
return computePoses(featureVectors, worldPoints, R, t);
|
|
}
|
|
}
|