Monado OpenXR Runtime
Loading...
Searching...
No Matches
lambdatwist_p3p.h
Go to the documentation of this file.
1// Copyright 2019, Philipp Zabel
2// Copyright 2026, Beyley Cardellio
3// SPDX-License-Identifier: BSL-1.0
4/*!
5 * @file
6 * @brief Lambda Twist P3P solver.
7 * @author Philipp Zabel <philipp.zabel@gmail.com>
8 * @author Beyley Cardellio <ep1cm1n10n123@gmail.com>
9 * @ingroup xrt_iface
10 */
11
12#pragma once
13
14#include "cubic.h"
15#include "quadratic.h"
16#include "eig3x3known0.h"
17#include "refine_lambda.h"
18#include "mat.h"
19#include "vec.h"
20
21#include <math.h>
22#include <string.h>
23
24
25// @todo remove when clang-format is updated in CI
26// clang-format off
27/*
28 * This implements algorithm 1 "Lambda Twist P3P" described in the paper
29 * "Lambda Twist: An Accurate Fast Robust Perspective Three Point (P3P)
30 * Solver." by Mikael Persson and Klas Nordberg. See the paper for details.
31 *
32 * 1: MIX(n, m) = (n, m, n ⨯ m)
33 */
34#define MIX(n, m) \
35 {n[0], m[0], n[1] * m[2] - n[2] * m[1], n[1], m[1], n[2] * m[0] - n[0] * m[2], \
36 n[2], m[2], n[0] * m[1] - n[1] * m[0]}
37
38#define VEC3_DOT(x, y) (x[0] * y[0] + x[1] * y[1] + x[2] * y[2])
39#define VEC3_SCALE(a, x) {a * x[0], a * x[1], a * x[2]}
40#define VEC3_SUB(x, y) {x[0] - y[0], x[1] - y[1], x[2] - y[2]}
41// clang-format on
42
43/*
44 * 2: function P3P(y₁, y₂, y₃, x₁, x₂, x₃)
45 */
46static inline int
47lambdatwist_p3p(const double *iny1,
48 const double *iny2,
49 const double *iny3, /* vec3 */
50 const double *x1,
51 const double *x2,
52 const double *x3, /* vec3 */
53 double (*R)[9],
54 double (*t)[3]) /* mat3[4], vec3[4] */
55{
56 double y1[3], y2[3], y3[3];
57
58 memcpy(y1, iny1, sizeof(y1));
59 memcpy(y2, iny2, sizeof(y2));
60 memcpy(y3, iny3, sizeof(y3));
61
62 /* 3: Normalize yᵢ */
63 vec3_normalise(y1);
64 vec3_normalise(y2);
65 vec3_normalise(y3);
66
67 /* 4: Compute aᵢⱼ and bᵢⱼ according to (3) */
68
69 /* reusable intermediate values: Δxᵢⱼ = xᵢ - xⱼ */
70 const double dx12[3] = VEC3_SUB(x1, x2);
71 const double dx13[3] = VEC3_SUB(x1, x3);
72 const double dx23[3] = VEC3_SUB(x2, x3);
73
74 /* aᵢⱼ = |xᵢ - xⱼ|² (2) */
75 const double a12 = VEC3_DOT(dx12, dx12);
76 const double a13 = VEC3_DOT(dx13, dx13);
77 const double a23 = VEC3_DOT(dx23, dx23);
78
79 /* bᵢⱼ = yᵢ^T yⱼ (3) */
80 const double b12 = VEC3_DOT(y1, y2);
81 const double b13 = VEC3_DOT(y1, y3);
82 const double b23 = VEC3_DOT(y2, y3);
83
84 /*
85 * 5: Construct D₁ and D₂ from (5) and (6)
86 *
87 * D₁ = M₁₂a₂₃ - M₂₃a₁₂ = ( d₁₁ d₁₂ d₁₃ ) (5)
88 * D₂ = M₁₃a₂₃ - M₂₃a₁₃ = ( d₂₁ d₂₂ d₂₃ ) (6)
89 *
90 * with
91 *
92 * ⎛ 1 -b₁₂ 0 ⎞ ⎛ 1 0 -b₁₃⎞ ⎛ 0 0 0 ⎞
93 * M₁₂ = ⎜-b₁₂ 1 0 ⎟, M₁₃ = ⎜ 0 0 0 ⎟, M₂₃ = ⎜ 0 1 -b₂₃⎟
94 * ⎝ 0 0 0 ⎠ ⎝-b₁₃ 0 1 ⎠ ⎝ 0 -b₂₃ 1 ⎠
95 *
96 * thus
97 *
98 * ⎛ a₂₃ -a₂₃b₁₂ 0 ⎞ ⎛ a₂₃ 0 -a₂₃b₁₃ ⎞
99 * D₁ = ⎜-a₂₃b₁₂ a₂₃-a₁₂ a₁₂b₂₃⎟, D₂ = ⎜ 0 -a₁₃ a₁₃b₂₃ ⎟
100 * ⎝ 0 a₁₂b₂₃ -a₁₂ ⎠ ⎝-a₂₃b₁₃ a₁₃b₂₃ a₂₃-a₁₃ ⎠
101 */
102
103 /*
104 * 6: Compute a real root γ to (8)-(10) of the cubic equation
105 *
106 * to find D₀ = D₁ + γD₂, by solving 0 = det(D₁ + λD₂) (8)
107 *
108 * ⎛ a₂₃ + γa₂₃ -a₂₃b₁₂ -γa₂₃b₁₃ ⎞
109 * = det ⎜-a₂₃b₁₂ a₂₃ - a₁₂ - γa₁₃ a₁₂b₂₃ + γa₁₃b₂₃ ⎟
110 * ⎝-γa₂₃b₁₃ a₁₂b₂₃+γa₁₃b₂₃ -a₁₂ + γ(a₂₃ - a₁₃)⎠
111 *
112 * which is equivalent to
113 *
114 * c₃γ³ + c₂γ² + c₁γ + c₀ (9)
115 *
116 * with
117 *
118 * c₃ = det(D₂)
119 * c₂ = d₂₁^T(d₁₂ ⨯ d₁₃) + d₂₂^T(d₁₃ ⨯ d₁₁) + d₂₃^T(d₁₁ ⨯ d₁₂)
120 * c₃ = d₁₁^T(d₂₂ ⨯ d₂₃) + d₁₂^T(d₂₃ ⨯ d₂₁) + d₁₃^T(d₂₁ ⨯ d₂₂)
121 * c₀ = det(D₁)
122 */
123 const double a23b123 = 2.0 * a23 * (b12 * b13 * b23 - 1.0);
124 const double s12_sq = 1.0 - b12 * b12;
125 const double s13_sq = 1.0 - b13 * b13;
126 const double s23_sq = 1.0 - b23 * b23;
127
128 const double c3 = a13 * (a13 * s23_sq - a23 * s13_sq);
129 const double c2 = a23 * (a23 - a12) * s13_sq + a13 * (a23b123 + (2.0 * a12 + a13) * s23_sq);
130 const double c1 = a23 * (a23 - a13) * s12_sq + a12 * (a23b123 + (2.0 * a13 + a12) * s23_sq);
131 const double c0 = a12 * (a12 * s23_sq - a23 * s12_sq);
132
133 /*
134 * Since det(D₂) > 0 we can reduce the cubic equation to:
135 * γ³ + (c₂/c₃)γ² + (c₁/c₃)γ + c₀/c₃
136 */
137 const double gamma = reduced_cubic_real_root(c2 / c3, c1 / c3, c0 / c3);
138
139 /*
140 * 7: D₀ = D₁ + γ D₂
141 *
142 * ⎛ a₂₃ + γa₂₃ -a₂₃b₁₂ -γa₂₃b₁₃ ⎞
143 * = ⎜-a₂₃b₁₂ a₂₃ - a₁₂ - γa₁₃ a₁₂b₂₃ + γa₁₃b₂₃ ⎟
144 * ⎝-γa₂₃b₁₃ a₁₂b₂₃ + γa₁₃b₂₃ -a₁₂ + γ(a₂₃ - a₁₃)⎠
145 *
146 * D₀ is symmetric, we only use its upper triangular part.
147 */
148 const double D0_00 = a23 + gamma * a23;
149 const double D0_01 = -a23 * b12;
150 const double D0_02 = -gamma * a23 * b13;
151 const double D0_11 = a23 - a12 - gamma * a13;
152 const double D0_12 = (a12 + gamma * a13) * b23;
153 const double D0_22 = -a12 + gamma * (a23 - a13);
154 const double D0[6] = {
155 D0_00, D0_01, D0_02, D0_11, D0_12, D0_22,
156 };
157
158 /* 8: [E, σ₁, σ₂] = EIG3X3KNOWN0(D₀). */
159 double E[9];
160 double sigma[2];
161 /* this function does not write E[2], E[5], nor E[8]*/
162 eig3x3known0(D0, E, sigma);
163
164 /* 9: s = ±√(-σ₂ / σ₁) */
165 const double v = sqrt(-sigma[1] / sigma[0]);
166
167 int k = 0;
168 double L[4][3];
169
170 /*
171 * 10: Compute the τₖ > 0 for each s using Eqn (14) with
172 * coefficients in Eqn (15)
173 */
174 double s, tmp, w0, w1, a, b, c, tau1, tau2;
175 /* s = +√(-σ₂ / σ₁) */
176 s = +v;
177 tmp = 1.0 / (s * E[1] - E[0]);
178 w0 = (E[3] - s * E[4]) * tmp; /* ω₀ = (e₃ - se₄) / (se₁ - e₀) */
179 w1 = (E[6] - s * E[7]) * tmp; /* ω₁ = (e₆ - se₇) / (se₁ - e₀) */
180 /*
181 * a = (a₁₃ - a₁₂)ω₁² + 2a₁₂b₁₃ω₁ - a₁₂
182 * b = 2a₁₂b₁₃ω₀ - 2a₁₃b₁₂ω₁ - 2ω₀ω₁(a₁₂ - a₁₃) (15)
183 * c = a₁₃ + (a₁₃ - a₁₂)ω₀² - 2a₁₃b₁₂ω₀
184 * ^ this parenthesis was misplaced in the paper
185 */
186 a = (a13 - a12) * w1 * w1 + 2.0 * a12 * b13 * w1 - a12;
187 b = -2.0 * (a13 * b12 * w1 - a12 * b13 * w0 + w0 * w1 * (a12 - a13));
188 c = a13 + (a13 - a12) * w0 * w0 - 2.0 * a13 * b12 * w0;
189
190 // @todo remove when clang-format is updated in CI
191 // clang-format off
192 /* calculate real roots τ₁, τ₂ of aτ² + bτ + c = 0 */
193 if (quadratic_real_roots(a, b, c, &tau1, &tau2)) {
194 /*
195 * 11: Compute Λₖ according to Eqn (16), λ₃ₖ = τₖ λ₂ₖ
196 * and Eqn (13), λ₁ₖ > 0
197 */
198/* Macro instead of inline function, gcc produces faster code this way */
199#define COMPUTE_LAMBDA(tau) \
200 if ((tau) > 0.0) \
201 do { \
202 /* \
203 * The solution to equation (16) for λ₂ₖ is: \
204 * λ₂ₖ = √(a₂₃ / (τₖ(-2b₂₃ + τₖ) + 1)) \
205 * ^^ missing in the paper \
206 */ \
207 const double tmp = (tau) * (-2.0 * b23 + (tau)) + 1.0; \
208 const double l2 = sqrt(a23 / tmp); \
209 /* λ₃ₖ = τₖλ₂ₖ */ \
210 const double l3 = (tau) * l2; \
211 /* λ₁ₖ = ω₀λ₂ₖ + ω₁λ₃ₖ (13) */ \
212 const double l1 = w0 * l2 + w1 * l3; \
213 if (l1 > 0.0) { \
214 L[k][0] = l1; \
215 L[k][1] = l2; \
216 L[k][2] = l3; \
217 k++; \
218 } \
219 } while (0)
220 COMPUTE_LAMBDA(tau1);
221 COMPUTE_LAMBDA(tau2);
222 }
223 // clang-format on
224
225 /* s = -√(-σ₂ / σ₁) */
226 s = -v;
227 tmp = 1.0 / (s * E[1] - E[0]);
228 w0 = (E[3] - s * E[4]) * tmp;
229 w1 = (E[6] - s * E[7]) * tmp;
230 a = (a13 - a12) * w1 * w1 + 2.0 * a12 * b13 * w1 - a12;
231 b = -2.0 * (a13 * b12 * w1 - a12 * b13 * w0 + w0 * w1 * (a12 - a13));
232 c = a13 + (a13 - a12) * w0 * w0 - 2.0 * a13 * b12 * w0;
233 if (quadratic_real_roots(a, b, c, &tau1, &tau2)) {
234 COMPUTE_LAMBDA(tau1);
235 COMPUTE_LAMBDA(tau2);
236 }
237
238 /* 12: X⁻¹ = (mix(x₁ - x₂, x₁ - x₃))⁻¹ */
239 double X[9] = MIX(dx12, dx13);
240 mat3_invert(X);
241
242 /* 13: for each valid Λₖ do */
243 const int valid = k;
244 for (k = 0; k < valid; k++) {
245 /* 14: Gauss-Newton-Refine(Λₖ), see Section 3.8 */
246 gauss_newton_refineL(L[k], a12, a13, a23, b12, b13, b23);
247
248 const double l1 = L[k][0], l2 = L[k][1], l3 = L[k][2];
249 const double l1y1[3] = VEC3_SCALE(l1, y1); /* λ₁ₖ y₁ */
250 const double dly12[3] = VEC3_SUB(l1y1, l2 * y2);
251 const double dly13[3] = VEC3_SUB(l1y1, l3 * y3);
252
253 /* 15: Yₖ = MIX(λ₁ₖ y₁ - λ₂ₖ y₂, λ₁ₖ y₁ - λ₃ₖ y₃) */
254 const double Y[9] = MIX(dly12, dly13);
255
256 /* 16: Rₖ = Yₖ X⁻¹ */
257 mat3_mul(R[k], Y, X);
258
259 /* 17: tₖ = λ₁ₖ y₁ - Rₖ x₁ */
260 double Rx1[3];
261 mat3vec3_mul(Rx1, R[k], x1);
262 vec3_sub(t[k], l1y1, Rx1);
263 }
264
265 /* 18: Return all Rₖ, tₖ */
266 return valid;
267}
Cubic root finder, specialized for P3P solving.
Eigenvalue solver for 3x3 symmetric matrices with known zero eigenvalue, specialized for P3P solving.
Matrix operations, specialized for P3P solving.
Quadratic root finder, specialized for P3P solving.
Gauss-Newton refinement of Λ, specialized for P3P solving.
Definition comp_scratch.c:130
Vector subtraction and normalization, specialized for P3P solving.