Files
2026-07-13 13:33:03 +08:00

722 lines
26 KiB
C++

//
// calib3d.cpp
// MNN
//
// Created by MNN on 2021/08/26.
// Copyright © 2018][Alibaba Group Holding Limited
//
#include <math/Matrix.hpp>
#include "cv/calib3d.hpp"
#include "cv/imgproc/geometric.hpp"
#include <MNN/expr/NeuralNetWorkOp.hpp>
#include <MNN/expr/MathOp.hpp>
#include <cmath>
#include <limits>
#define DUMP(x)\
{\
printf(#x "\n");\
Math::Matrix::print(x.get());\
}
namespace MNN {
namespace CV {
// helper functions
inline static float det3x3(const float* ptr) {
#define M(r, c) ptr[r * 3 + c]
return M(0, 0) * (M(1, 1) * M(2, 2) - M(1, 2) * M(2, 1)) -
M(0, 1) * (M(1, 0) * M(2, 2) - M(1, 2) * M(2, 0)) +
M(0, 2) * (M(1, 0) * M(2, 1) - M(1, 1) * M(2, 0));
}
inline static float det9x1(const float* r) {
return r[0]*r[4]*r[8] + r[1]*r[5]*r[6] + r[2]*r[3]*r[7] - r[6]*r[4]*r[2] - r[7]*r[5]*r[0] - r[8]*r[3]*r[1];
}
inline static float orthogonalityError(const float a[9]) {
float sq_norm_a1 = a[0] * a[0] + a[1] * a[1] + a[2] * a[2],
sq_norm_a2 = a[3] * a[3] + a[4] * a[4] + a[5] * a[5],
sq_norm_a3 = a[6] * a[6] + a[7] * a[7] + a[8] * a[8];
float dot_a1a2 = a[0] * a[3] + a[1] * a[4] + a[2] * a[5],
dot_a1a3 = a[0] * a[6] + a[1] * a[7] + a[2] * a[8],
dot_a2a3 = a[3] * a[6] + a[4] * a[7] + a[5] * a[8];
return (sq_norm_a1 - 1) * (sq_norm_a1 - 1) + (sq_norm_a2 - 1) * (sq_norm_a2 - 1) + (sq_norm_a3 - 1) * (sq_norm_a3 - 1) +
2 * (dot_a1a2*dot_a1a2 + dot_a1a3*dot_a1a3 + dot_a2a3*dot_a2a3);
}
static void orthogonal(float* at, float* vt, int i, int j, int row, int col, bool& pass) {
auto ai = at + i * row;
auto aj = at + j * row;
auto vi = vt + i * col;
auto vj = vt + j * col;
float norm = 0.f, normi = 0.f, normj = 0.f;
for (int i = 0; i < col; i++) {
norm += ai[i] * aj[i];
normi += ai[i] * ai[i];
normj += aj[i] * aj[i];
}
constexpr float eps = std::numeric_limits<float>::epsilon() * 2;
if (std::abs(norm) < eps * std::sqrt(normi * normj)) {
return;
}
pass = false;
float tao = (normi - normj) / (2.0 * norm);
float tan = (tao < 0 ? -1 : 1) / (fabs(tao) + sqrt(1 + pow(tao, 2)));
float cos = 1 / sqrt(1 + pow(tan, 2));
float sin = cos * tan;
bool swap = normi < normj;
for (int i = 0; i < col; i++) {
float nai = ai[i];
float naj = aj[i];
float nvi = vi[i];
float nvj = vj[i];
if (swap) {
std::swap(nai, naj);
std::swap(nvi, nvj);
}
ai[i] = nai * cos + naj * sin;
aj[i] = naj * cos - nai * sin;
vi[i] = nvi * cos + nvj * sin;
vj[i] = nvj * cos - nvi * sin;
}
}
inline static void svdMatrix(float* w, float* u, float* vt, float* a, int M, int N) {
int size = M * N;
std::vector<float> AT_(size);
float* at = AT_.data();
// init at
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
at[i * M + j] = a[j * N + i];
}
}
// init vt
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
vt[i * N + j] = (i == j);
}
}
constexpr int max_iteration = 30;
for (int iter = 0; iter < max_iteration; iter++) {
bool pass = true;
for (int i = 0; i < N; i++) {
for (int j = i + 1; j < N; j++) {
orthogonal(at, vt, i, j, M, N, pass);
}
}
if (pass) break;
}
for (int i = 0; i < N; i++) {
float norm = 0.f;
for (int j = 0; j < N; j++) {
auto tmp = at[i * N + j];
norm += tmp * tmp;
}
norm = sqrt(norm);
w[i] = norm;
}
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
u[i * N + j] = at[j * N + i] / w[j];
}
}
}
void Rodrigues(float* dst, float* src) {
float w_[9], u_[9], vt_[9];
svdMatrix(w_, u_, vt_, src, 3, 3);
float R[9];
Math::Matrix::multi(R, u_, vt_, 3, 3, 3);
float x = R[7] - R[5], y = R[2] - R[6], z = R[3] - R[1];
float s = sqrt((x * x + y * y + z * z) * 0.25);
float c = (R[0] + R[4] + R[8] - 1) * 0.5;
c = c > 1. ? 1. : c < -1. ? -1. : c;
float theta = acos(c);
if (s < 1e-5) {
if (c > 0) {
x = y = z = 0;
} else {
x = sqrt(fmax((R[0] + 1) * 0.5, 0));
y = sqrt(fmax((R[4] + 1) * 0.5, 0)) * (R[1] < 0 ? -1. : 1.);
z = sqrt(fmax((R[8] + 1) * 0.5, 0)) * (R[2] < 0 ? -1. : 1.);
if (fabs(x) < fabs(y) && fabs(x) < fabs(z) && (R[5] > 0) != (y * z > 0)) {
z = -z;
}
theta /= sqrt(x * x + y * y + z * z);
x *= theta;
y *= theta;
z *= theta;
}
} else {
float vth = 1 / (2 * s);
vth *= theta;
x *= vth;
y *= vth;
z *= vth;
}
dst[0] = x;
dst[1] = y;
dst[2] = z;
}
void nearestRotationMatrix(float* r, float* e) {
// VARP e = Express::Variable::create(Express::Expr::create(e_, false));
float w[9] = {0};
float u[9] = {0};
float vt[9] = {0};
float v[9] = {0};
float e_t[9] = {0};
for (int i = 0; i < 3; ++i) {
for (int j = 0;j < 3; ++j) {
e_t[i * 3 + j] = e[j * 3 + i];
}
}
svdMatrix(w, u, vt, e_t, 3, 3);
for(int i = 0; i < 3; ++i) {
for(int j = 0; j < 3; ++j) {
v[i * 3 + j] = vt[j * 3 + i];
}
}
float detuv[9] = {1, 0, 0, 0, 1, 0, 0, 0, det3x3(u) * det3x3(v)};
float udetuv_[9] = {0};
float R_[9] = {0};
Math::Matrix::multi(udetuv_, u, detuv, 3, 3, 3);
Math::Matrix::multi(R_, udetuv_, vt, 3, 3, 3);
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
r[i * 3 + j] = R_[j * 3 + i];
}
}
}
void solveSQPSystem(float* delta_, float* r, float* omega) {
float 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];
float 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];
std::vector<float> h(54), k(36), n(27), h_t(54);
#define H(r, c) h[r * 6 + c]
#define K(r, c) k[r * 6 + c]
#define N(r, c) n[r * 3 + c]
// RowAndNullSpace start
// 1. q1
float norm_r1 = sqrt(sqnorm_r1);
float 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
float norm_r2 = sqrt(sqnorm_r2);
float 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)
float norm_r3 = sqrt(sqnorm_r3);
float 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
float 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);
float 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
float dot_j5q2 = r[6] * H(3, 1) + r[7] * H(4, 1) + r[8] * H(5, 1),
dot_j5q3 = r[3] * H(6, 2) + r[4] * H(7, 2) + r[5] * H(8, 2),
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);
auto norm_4 = 0.f;
for (int i = 0; i < 9; i++) {
norm_4 += H(i, 4) * H(i, 4);
}
norm_4 = sqrt(norm_4);
for (int i = 0; i < 9; i++) {
H(i, 4) = H(i, 4) / norm_4;
}
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
float dot_j6q1 = r[6] * H(0, 0) + r[7] * H(1, 0) + r[8] * H(2, 0),
dot_j6q3 = r[0] * H(6, 2) + r[1] * H(7, 2) + r[2] * H(8, 2),
dot_j6q4 = r[6] * H(0, 3) + r[7] * H(1, 3) + r[8] * H(2, 3),
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);
auto norm_5 = 0.f;
for (int i = 0; i < 9; i++) {
norm_5 += H(i, 5) * H(i, 5);
}
norm_5 = sqrt(norm_5);
for (int i = 0; i < 9; i++) {
H(i, 5) = H(i, 5) / norm_5;
}
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);
std::vector<float> pn(81, 0);
#define Pn(r, c) pn[r + 9 * c]
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
Pn(i, j) = (i == j);
}
}
for (int i = 0; i < 6; ++i) {
for (int j = 0;j < 9; ++j) {
h_t[i * 9 + j] = h[j * 6 + i];
}
}
std::vector<float> HHT(81); // h:(9,6),h_t(6,9),HHT(9,9)
Math::Matrix::multi(HHT.data(), h.data(), h_t.data(), 9, 6, 9);
for (int i = 0; i < 9; ++i) {
for (int j = 0; j < 9; ++j) {
pn[j + 9 * i] = pn[j + 9 * i] - HHT[j + 9 * i];
}
}
float norm_threshold = 0.1;
int index1 = -1,
index2 = -1,
index3 = -1;
float max_norm1 = std::numeric_limits<float>::min(),
min_dot12 = std::numeric_limits<float>::max(),
min_dot1323 = std::numeric_limits<float>::max();
float col_norms[9] = { 0 };
for (int c = 0; c < 9; c++) {
for (int r = 0; r < 9; r++) {
col_norms[c] += pn[9 * c + r] * pn[9 * c + r];
}
col_norms[c] = sqrt(col_norms[c]);
}
for (int i = 0; i < 9; i++) {
if (col_norms[i] >= norm_threshold) {
if (max_norm1 < col_norms[i]) {
max_norm1 = col_norms[i];
index1 = i;
}
}
}
for (int i = 0; i < 9; i++) {
N(i, 0) = Pn(i, index1) / max_norm1;
}
for (int i = 0; i < 9; i++) {
if (i == index1) continue;
if (col_norms[i] >= norm_threshold) {
float cos_v1_x_col = 0.f;
for (int j = 0; j < 9; j++) {
cos_v1_x_col += Pn(j, i) * Pn(j, index1);
}
cos_v1_x_col = fabs(cos_v1_x_col / col_norms[i]);
if (cos_v1_x_col <= min_dot12) {
index2 = i;
min_dot12 = cos_v1_x_col;
}
}
}
float v2dotN0 = 0.f;
for (int i = 0; i < 9; i++) {
v2dotN0 += Pn(i, index2) * N(i, 0);
}
float norm_N1 = 0.f;
for (int i = 0; i < 9; i++) {
N(i, 1) = Pn(i, index2) - v2dotN0 * N(i, 0);
norm_N1 += N(i, 1) * N(i, 1);
}
norm_N1 = sqrt(norm_N1);
for (int i = 0; i < 9; i++) {
N(i, 1) /= norm_N1;
}
for (int i = 0; i < 9; i++) {
if (i == index2 || i == index1) continue;
if (col_norms[i] >= norm_threshold) {
float cos_v1_x_col = 0.f, cos_v2_x_col = 0.f;
for (int j = 0; j < 9; j++) {
cos_v1_x_col += Pn(j, i) * Pn(j, index1);
cos_v2_x_col += Pn(j, i) * Pn(j, index2);
}
cos_v1_x_col = fabs(cos_v1_x_col / col_norms[i]);
cos_v2_x_col = fabs(cos_v2_x_col / 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;
}
}
}
float v3dotN1 = 0.f, v3dotN0 = 0.f;
for (int i = 0; i < 9; i++) {
v3dotN0 += Pn(i, index3) * N(i, 0);
v3dotN1 += Pn(i, index3) * N(i, 1);
}
float norm_N2 = 0.f;
for (int i = 0; i < 9; i++) {
N(i, 2) = Pn(i, index3) - v3dotN1 * N(i, 1) - v3dotN0 * N(i, 0);
norm_N2 += N(i, 2) * N(i, 2);
}
norm_N2 = sqrt(norm_N2);
for (int i = 0; i < 9; i++) {
N(i, 2) /= norm_N2;
}
// RowAndNullSpace end
float g[6];
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;
float x[6];
x[0] = g[0] / K(0, 0);
x[1] = g[1] / K(1, 1);
x[2] = g[2] / K(2, 2);
x[3] = (g[3] - K(3, 0) * x[0] - K(3, 1) * x[1]) / K(3, 3);
x[4] = (g[4] - K(4, 1) * x[1] - K(4, 2) * x[2] - K(4, 3) * x[3]) / K(4, 4);
x[5] = (g[5] - K(5, 0) * x[0] - K(5, 2) * x[2] - K(5, 3) * x[3] - K(5, 4) * x[4]) / K(5, 5);
std::vector<float> NtOmega_(27); // (3,9)
float W_[9] = {0}; // (3,3)
float WInverse_[9] = {0}; // (3,3)
std::vector<float> WInverseOmega(27);
float delta_r_[9] = {0}; // (9,1)
float y_[3] = {0}; // (3,1)
float ny_[9] = {0}; // (9,1)
Matrix winv;
Math::Matrix::multi(delta_, h.data(), x, 9, 6, 1);
// n:(9,3), n_t:(3,9)
std::vector<float> n_t(27);
for (int i = 0; i < 3; ++i) {
for (int j = 0;j < 9; ++j) {
n_t[i * 9 + j] = n[j * 3 + i];
}
}
Math::Matrix::multi(NtOmega_.data(), n_t.data(), omega, 3, 9, 9); // n_t * omega
Math::Matrix::multi(W_, NtOmega_.data(), n.data(), 3, 9, 3);
winv.set9(W_);
winv.invert(&winv);
winv.get9(WInverse_);
for (int i = 0; i < 9; ++i) {
WInverse_[i] = -1.0f * WInverse_[i];
}
Math::Matrix::multi(WInverseOmega.data(), WInverse_, NtOmega_.data(), 3, 3, 9);
Math::Matrix::add(delta_r_, delta_, r, 9);
Math::Matrix::multi(y_, WInverseOmega.data(), delta_r_, 3, 9, 1);
Math::Matrix::multi(ny_, n.data(), y_, 9, 3, 1);
Math::Matrix::add(delta_, delta_, ny_, 9);
}
void runSQP(float* solution_r_hat_, float* r_, float* omega_) {
float delta_squared_norm = std::numeric_limits<float>::max();
int step = 0;
while (delta_squared_norm > 1e-10 && step++ < 15) {
float delta[9] = {0};
solveSQPSystem(delta, r_, omega_);
for (int i = 0; i < 9; i++) {
auto d = delta[i];
delta_squared_norm += d * d;
r_[i] += d;
}
}
float solution_r_[9] = {0}; // (9,1)
// std::unique_ptr<Tensor> solution_r_(Math::Matrix::create(1, 9));
::memcpy(solution_r_, r_, 36);
float det_r = det9x1(r_);
if (det_r < 0) {
for (int i = 0; i < 9; ++i) {
r_[i] = (-1.f) * r_[i];
}
det_r = -det_r;
}
if (det_r > 1.001) {
nearestRotationMatrix(solution_r_hat_, solution_r_);
} else {
::memcpy(solution_r_hat_, solution_r_, 36);
}
}
void handleSolution(float* solution_r_hat_, float* solution_t, float* omega_,
float mean_x, float mean_y, float mean_z, const float* optr, int n,
float* rvec, float* tvec, float& min_sq_error) {
auto r = solution_r_hat_;
auto t = solution_t;
bool cheirok = (r[6] * mean_x + r[7] * mean_y + r[8] * mean_z + t[2]) > 0;
if (!cheirok) {
int npos = 0, nneg = 0;
for (size_t i = 0; i < n; i++) {
if (r[6] * optr[0] + r[7] * optr[1] + r[8] * optr[2] + t[2] > 0) {
++npos;
} else {
++nneg;
}
}
cheirok = (npos >= nneg);
}
if (cheirok) {
float sq_error = 0.f;
float omega_r_[9] = {0}; // (9,1)
Math::Matrix::multi(omega_r_, omega_, solution_r_hat_, 9, 9, 1);
for (int i = 0; i < 9; i++) {
sq_error += omega_r_[i] * solution_r_hat_[i];
}
if (min_sq_error - sq_error > 1e-6) {
min_sq_error = sq_error;
memcpy(rvec, r, 36);
memcpy(tvec, t, 12);
}
}
}
// helper functions
std::pair<VARP, VARP> solvePnP(VARP objectPoints, VARP imagePoints, VARP cameraMatrix, VARP distCoeffs, bool useExtrinsicGuess) {
imagePoints = undistortPoints(imagePoints, cameraMatrix, distCoeffs);
int n = objectPoints->getInfo()->dim[0];
auto optr = objectPoints->readMap<float>();
auto iptr = imagePoints->readMap<float>();
// computeOmega start
std::vector<float> omega(81); // (9,9)
std::vector<float> qa_sum(27); // (3,9)
#define omega(i,j) omega[i * 9 + j]
#define qa_sum(i,j) qa_sum[i * 9 + j]
float sq_norm_sum = 0, sum_img_x = 0, sum_img_y = 0,
sum_obj_x = 0, sum_obj_y = 0, sum_obj_z = 0;
for (int i = 0; i < n; i++) {
auto X = optr[i * 3], Y = optr[i * 3 + 1], Z = optr[i * 3 + 2];
auto x = iptr[i * 2], y = iptr[i * 2 + 1];
float sq_norm = x * x + y * y;
sq_norm_sum += sq_norm;
sum_img_x += x;
sum_img_y += y;
sum_obj_x += X;
sum_obj_y += Y;
sum_obj_z += Z;
float X2 = X * X;
float XY = X * Y;
float XZ = X * Z;
float Y2 = Y * Y;
float YZ = Y * Z;
float 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;
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;
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);
for (int r = 0; r < 9; r++) {
for (int c = 0; c < r; c++) {
omega(r,c) = omega(c,r);
}
}
float qinv[9]; // (3,3)
std::vector<float> p(27); // (3,9)
CV::Matrix q;
q.setAll(n, 0, -sum_img_x, 0, n, -sum_img_y, -sum_img_x, -sum_img_y, sq_norm_sum);
q.invert(&q);
q.get9(qinv);
std::vector<float> qa_sum_t(27); // (9,3)
std::vector<float> omega_add_(81); // (9,9)
for (int i = 0; i < 9; ++i) {
qinv[i] = qinv[i] * (-1.f);
}
Math::Matrix::multi(p.data(), qinv, qa_sum.data(), 3, 3, 9);
for (int i = 0; i < 9; ++i) {
for (int j = 0; j < 3; ++j) {
qa_sum_t[i * 3 + j] = qa_sum[j * 9 + i];
}
}
Math::Matrix::multi(omega_add_.data(), qa_sum_t.data(), p.data(), 9, 3, 9);
Math::Matrix::add(omega.data(), omega.data(), omega_add_.data(), 81);
std::vector<float> s_(81), u(81), vt_(81);
svdMatrix(s_.data(), u.data(), vt_.data(), omega.data(), 9, 9);
int num_null_vectors_ = -1;
while (s_[7 - num_null_vectors_] < 1e-7) num_null_vectors_++;
float mean_x = sum_obj_x / n, mean_y = sum_obj_y / n, mean_z = sum_obj_z / n;
// computeOmega end
// solveInternal start
int num_eigen_points = num_null_vectors_ > 0 ? num_null_vectors_ : 1;
float min_sq_error = std::numeric_limits<float>::max();
float e[9]; // (9,1)
float solution_r_hat_[9] = {0}; // (9,1)
float solution_t_[3] = {0}; // (3,1)
float rvec[9] = {0}, tvec[3] = {0};
for (int i = 9 - num_eigen_points; i < 9; i++) {
for (int j = 0; j < 9; j++) {
e[j] = vt_[i * 9 + j] * sqrt(3);
}
float orthogonality_sq_error = orthogonalityError(e);
if (orthogonality_sq_error < 1e-8) {
float det9x1e = det9x1(e);
Math::Matrix::multi(solution_r_hat_, e, &det9x1e, 9, 1, 1);
Math::Matrix::multi(solution_t_, p.data(), solution_r_hat_, 3, 9, 1);
handleSolution(solution_r_hat_, solution_t_, omega.data(), mean_x, mean_y, mean_z, optr, n, rvec, tvec, min_sq_error);
} else {
float r0[9] = {0};
nearestRotationMatrix(r0, e);
runSQP(solution_r_hat_, r0, omega.data());
Math::Matrix::multi(solution_t_, p.data(), solution_r_hat_, 3, 9, 1);
handleSolution(solution_r_hat_, solution_t_, omega.data(), mean_x, mean_y, mean_z, optr, n, rvec, tvec, min_sq_error);
for (int ix = 0; ix < 9; ++ix) {
e[ix] = (-1.0f) * e[ix];
}
float r1_[9] = {0};
nearestRotationMatrix(r1_, e);
runSQP(solution_r_hat_, r1_, omega.data());
Math::Matrix::multi(solution_t_, p.data(), solution_r_hat_, 3, 9, 1);
handleSolution(solution_r_hat_, solution_t_, omega.data(), mean_x, mean_y, mean_z, optr, n, rvec, tvec, min_sq_error);
}
}
int index, c = 1;
while ((index = 9 - num_eigen_points - c) > 0 && min_sq_error > 3 * s_[index]) {
for (int j = 0; j < 9; j++) {
e[j] = vt_[index * 9 + j];
}
float r0_[9] = {0};
nearestRotationMatrix(r0_, e);
runSQP(solution_r_hat_, r0_, omega.data());
Math::Matrix::multi(solution_t_, p.data(), solution_r_hat_, 3, 9, 1);
handleSolution(solution_r_hat_, solution_t_, omega.data(), mean_x, mean_y, mean_z, optr, n, rvec, tvec, min_sq_error);
for (int ix = 0; ix < 9; ++ix) {
e[ix] = (-1.0f) * e[ix];
}
float r1_[9] = {0};
nearestRotationMatrix(r1_, e);
runSQP(solution_r_hat_, r1_, omega.data());
Math::Matrix::multi(solution_t_, p.data(), solution_r_hat_, 3, 9, 1);
handleSolution(solution_r_hat_, solution_t_, omega.data(), mean_x, mean_y, mean_z, optr, n, rvec, tvec, min_sq_error);
c++;
}
// solveInternal end
float res[3];
Rodrigues(res, rvec);
VARP tvecvarp = _Input({3, 1}, NCHW);
VARP rvec_ = _Const(res, {3, 1}, NCHW);
memcpy(tvecvarp->writeMap<float>(), tvec, 12);
return std::make_pair(rvec_, tvecvarp);
}
VARP Rodrigues(VARP src) {
auto res = _Svd(src);
auto w_ = res[0];
auto u_ = res[1];
auto vt_ = res[2];
auto R_ = _MatMul(u_, vt_);
R_.fix(Express::VARP::CONSTANT);
auto R = R_->readMap<float>();
float x = R[7] - R[5], y = R[2] - R[6], z = R[3] - R[1];
float s = sqrt((x * x + y * y + z * z) * 0.25);
float c = (R[0] + R[4] + R[8] - 1) * 0.5;
c = c > 1. ? 1. : c < -1. ? -1. : c;
float theta = acos(c);
if (s < 1e-5) {
if (c > 0) {
x = y = z = 0;
} else {
x = sqrt(fmax((R[0] + 1) * 0.5, 0));
y = sqrt(fmax((R[4] + 1) * 0.5, 0)) * (R[1] < 0 ? -1. : 1.);
z = sqrt(fmax((R[8] + 1) * 0.5, 0)) * (R[2] < 0 ? -1. : 1.);
if (fabs(x) < fabs(y) && fabs(x) < fabs(z) && (R[5] > 0) != (y * z > 0)) {
z = -z;
}
theta /= sqrt(x * x + y * y + z * z);
x *= theta;
y *= theta;
z *= theta;
}
} else {
float vth = 1 / (2 * s);
vth *= theta;
x *= vth;
y *= vth;
z *= vth;
}
float data[3] = { x, y, z };
return _Const(data, {3, 1}, NCHW);
}
} // CV
} // MNN