Blender V4.3
libmv/multiview/homography.cc
Go to the documentation of this file.
1// Copyright (c) 2008, 2009 libmv authors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to
5// deal in the Software without restriction, including without limitation the
6// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7// sell copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19// IN THE SOFTWARE.
20
22
23#include "ceres/ceres.h"
27
28namespace libmv {
47 const Mat& x1, const Mat& x2, Mat3* H, double expected_precision) {
48 assert(2 == x1.rows());
49 assert(4 <= x1.cols());
50 assert(x1.rows() == x2.rows());
51 assert(x1.cols() == x2.cols());
52
53 int n = x1.cols();
54 MatX8 L = Mat::Zero(n * 3, 8);
55 Mat b = Mat::Zero(n * 3, 1);
56 for (int i = 0; i < n; ++i) {
57 int j = 3 * i;
58 L(j, 0) = x1(0, i); // a
59 L(j, 1) = x1(1, i); // b
60 L(j, 2) = 1.0; // c
61 L(j, 6) = -x2(0, i) * x1(0, i); // g
62 L(j, 7) = -x2(0, i) * x1(1, i); // h
63 b(j, 0) = x2(0, i); // i
64
65 ++j;
66 L(j, 3) = x1(0, i); // d
67 L(j, 4) = x1(1, i); // e
68 L(j, 5) = 1.0; // f
69 L(j, 6) = -x2(1, i) * x1(0, i); // g
70 L(j, 7) = -x2(1, i) * x1(1, i); // h
71 b(j, 0) = x2(1, i); // i
72
73 // This ensures better stability
74 // TODO(julien) make a lite version without this 3rd set
75 ++j;
76 L(j, 0) = x2(1, i) * x1(0, i); // a
77 L(j, 1) = x2(1, i) * x1(1, i); // b
78 L(j, 2) = x2(1, i); // c
79 L(j, 3) = -x2(0, i) * x1(0, i); // d
80 L(j, 4) = -x2(0, i) * x1(1, i); // e
81 L(j, 5) = -x2(0, i); // f
82 }
83 // Solve Lx=B
84 Vec h = L.fullPivLu().solve(b);
86 if ((L * h).isApprox(b, expected_precision)) {
87 return true;
88 } else {
89 return false;
90 }
91}
92
93// clang-format off
102// clang-format on
104 const Mat& x2,
105 Mat3* H,
106 double expected_precision) {
107 if (x1.rows() == 2) {
109 x1, x2, H, expected_precision);
110 }
111 assert(3 == x1.rows());
112 assert(4 <= x1.cols());
113 assert(x1.rows() == x2.rows());
114 assert(x1.cols() == x2.cols());
115
116 const int x = 0;
117 const int y = 1;
118 const int w = 2;
119 int n = x1.cols();
120 MatX8 L = Mat::Zero(n * 3, 8);
121 Mat b = Mat::Zero(n * 3, 1);
122 for (int i = 0; i < n; ++i) {
123 int j = 3 * i;
124 L(j, 0) = x2(w, i) * x1(x, i); // a
125 L(j, 1) = x2(w, i) * x1(y, i); // b
126 L(j, 2) = x2(w, i) * x1(w, i); // c
127 L(j, 6) = -x2(x, i) * x1(x, i); // g
128 L(j, 7) = -x2(x, i) * x1(y, i); // h
129 b(j, 0) = x2(x, i) * x1(w, i);
130
131 ++j;
132 L(j, 3) = x2(w, i) * x1(x, i); // d
133 L(j, 4) = x2(w, i) * x1(y, i); // e
134 L(j, 5) = x2(w, i) * x1(w, i); // f
135 L(j, 6) = -x2(y, i) * x1(x, i); // g
136 L(j, 7) = -x2(y, i) * x1(y, i); // h
137 b(j, 0) = x2(y, i) * x1(w, i);
138
139 // This ensures better stability
140 ++j;
141 L(j, 0) = x2(y, i) * x1(x, i); // a
142 L(j, 1) = x2(y, i) * x1(y, i); // b
143 L(j, 2) = x2(y, i) * x1(w, i); // c
144 L(j, 3) = -x2(x, i) * x1(x, i); // d
145 L(j, 4) = -x2(x, i) * x1(y, i); // e
146 L(j, 5) = -x2(x, i) * x1(w, i); // f
147 }
148 // Solve Lx=B
149 Vec h = L.fullPivLu().solve(b);
150 if ((L * h).isApprox(b, expected_precision)) {
152 return true;
153 } else {
154 return false;
155 }
156}
157
158// Default settings for homography estimation which should be suitable
159// for a wide range of use cases.
161 : use_normalization(true),
162 max_num_iterations(50),
163 expected_average_symmetric_distance(1e-16) {
164}
165
166namespace {
167void GetNormalizedPoints(const Mat& original_points,
168 Mat* normalized_points,
169 Mat3* normalization_matrix) {
170 IsotropicPreconditionerFromPoints(original_points, normalization_matrix);
172 original_points, *normalization_matrix, normalized_points);
173}
174
175// Cost functor which computes symmetric geometric distance
176// used for homography matrix refinement.
177class HomographySymmetricGeometricCostFunctor {
178 public:
179 HomographySymmetricGeometricCostFunctor(const Vec2& x, const Vec2& y)
180 : x_(x), y_(y) {}
181
182 template <typename T>
183 bool operator()(const T* homography_parameters, T* residuals) const {
184 typedef Eigen::Matrix<T, 3, 3> Mat3;
185 typedef Eigen::Matrix<T, 3, 1> Vec3;
186
187 Mat3 H(homography_parameters);
188
189 Vec3 x(T(x_(0)), T(x_(1)), T(1.0));
190 Vec3 y(T(y_(0)), T(y_(1)), T(1.0));
191
192 Vec3 H_x = H * x;
193 Vec3 Hinv_y = H.inverse() * y;
194
195 H_x /= H_x(2);
196 Hinv_y /= Hinv_y(2);
197
198 // This is a forward error.
199 residuals[0] = H_x(0) - T(y_(0));
200 residuals[1] = H_x(1) - T(y_(1));
201
202 // This is a backward error.
203 residuals[2] = Hinv_y(0) - T(x_(0));
204 residuals[3] = Hinv_y(1) - T(x_(1));
205
206 return true;
207 }
208
209 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
210
211 const Vec2 x_;
212 const Vec2 y_;
213};
214
215// Termination checking callback used for homography estimation.
216// It finished the minimization as soon as actual average of
217// symmetric geometric distance is less or equal to the expected
218// average value.
219class TerminationCheckingCallback : public ceres::IterationCallback {
220 public:
221 TerminationCheckingCallback(const Mat& x1,
222 const Mat& x2,
223 const EstimateHomographyOptions& options,
224 Mat3* H)
225 : options_(options), x1_(x1), x2_(x2), H_(H) {}
226
227 virtual ceres::CallbackReturnType operator()(
228 const ceres::IterationSummary& summary) {
229 // If the step wasn't successful, there's nothing to do.
230 if (!summary.step_is_successful) {
231 return ceres::SOLVER_CONTINUE;
232 }
233
234 // Calculate average of symmetric geometric distance.
235 double average_distance = 0.0;
236 for (int i = 0; i < x1_.cols(); i++) {
237 average_distance =
238 SymmetricGeometricDistance(*H_, x1_.col(i), x2_.col(i));
239 }
240 average_distance /= x1_.cols();
241
242 if (average_distance <= options_.expected_average_symmetric_distance) {
243 return ceres::SOLVER_TERMINATE_SUCCESSFULLY;
244 }
245
246 return ceres::SOLVER_CONTINUE;
247 }
248
249 private:
250 const EstimateHomographyOptions& options_;
251 const Mat& x1_;
252 const Mat& x2_;
253 Mat3* H_;
254};
255} // namespace
256
261 const Mat& x1,
262 const Mat& x2,
264 Mat3* H) {
265 // TODO(sergey): Support homogenous coordinates, not just euclidean.
266
267 assert(2 == x1.rows());
268 assert(4 <= x1.cols());
269 assert(x1.rows() == x2.rows());
270 assert(x1.cols() == x2.cols());
271
272 Mat3 T1 = Mat3::Identity(), T2 = Mat3::Identity();
273
274 // Step 1: Algebraic homography estimation.
275 Mat x1_normalized, x2_normalized;
276
277 if (options.use_normalization) {
278 LG << "Estimating homography using normalization.";
279 GetNormalizedPoints(x1, &x1_normalized, &T1);
280 GetNormalizedPoints(x2, &x2_normalized, &T2);
281 } else {
282 x1_normalized = x1;
283 x2_normalized = x2;
284 }
285
286 // Assume algebraic estiation always suceeds,
287 Homography2DFromCorrespondencesLinear(x1_normalized, x2_normalized, H);
288
289 // Denormalize the homography matrix.
290 if (options.use_normalization) {
291 *H = T2.inverse() * (*H) * T1;
292 }
293
294 LG << "Estimated matrix after algebraic estimation:\n" << *H;
295
296 // Step 2: Refine matrix using Ceres minimizer.
297 ceres::Problem problem;
298 for (int i = 0; i < x1.cols(); i++) {
299 HomographySymmetricGeometricCostFunctor*
300 homography_symmetric_geometric_cost_function =
301 new HomographySymmetricGeometricCostFunctor(x1.col(i), x2.col(i));
302
303 problem.AddResidualBlock(
304 new ceres::AutoDiffCostFunction<HomographySymmetricGeometricCostFunctor,
305 4, // num_residuals
306 9>(
307 homography_symmetric_geometric_cost_function),
308 NULL,
309 H->data());
310 }
311
312 // Configure the solve.
313 ceres::Solver::Options solver_options;
314 solver_options.linear_solver_type = ceres::DENSE_QR;
315 solver_options.max_num_iterations = options.max_num_iterations;
316 solver_options.update_state_every_iteration = true;
317
318 // Terminate if the average symmetric distance is good enough.
319 TerminationCheckingCallback callback(x1, x2, options, H);
320 solver_options.callbacks.push_back(&callback);
321
322 // Run the solve.
323 ceres::Solver::Summary summary;
324 ceres::Solve(solver_options, &problem, &summary);
325
326 VLOG(1) << "Summary:\n" << summary.FullReport();
327
328 LG << "Final refined matrix:\n" << *H;
329
330 return summary.IsSolutionUsable();
331}
332
333// clang-format off
361// clang-format on
363 const Mat& x2,
364 Mat4* H,
365 double expected_precision) {
366 assert(4 == x1.rows());
367 assert(5 <= x1.cols());
368 assert(x1.rows() == x2.rows());
369 assert(x1.cols() == x2.cols());
370 const int x = 0;
371 const int y = 1;
372 const int z = 2;
373 const int w = 3;
374 int n = x1.cols();
375 MatX15 L = Mat::Zero(n * 6, 15);
376 Mat b = Mat::Zero(n * 6, 1);
377 for (int i = 0; i < n; ++i) {
378 int j = 6 * i;
379 L(j, 0) = -x2(w, i) * x1(x, i); // a
380 L(j, 1) = -x2(w, i) * x1(y, i); // b
381 L(j, 2) = -x2(w, i) * x1(z, i); // c
382 L(j, 3) = -x2(w, i) * x1(w, i); // d
383 L(j, 12) = x2(x, i) * x1(x, i); // m
384 L(j, 13) = x2(x, i) * x1(y, i); // n
385 L(j, 14) = x2(x, i) * x1(z, i); // o
386 b(j, 0) = -x2(x, i) * x1(w, i);
387
388 ++j;
389 L(j, 4) = -x2(z, i) * x1(x, i); // e
390 L(j, 5) = -x2(z, i) * x1(y, i); // f
391 L(j, 6) = -x2(z, i) * x1(z, i); // g
392 L(j, 7) = -x2(z, i) * x1(w, i); // h
393 L(j, 8) = x2(y, i) * x1(x, i); // i
394 L(j, 9) = x2(y, i) * x1(y, i); // j
395 L(j, 10) = x2(y, i) * x1(z, i); // k
396 L(j, 11) = x2(y, i) * x1(w, i); // l
397
398 ++j;
399 L(j, 0) = -x2(z, i) * x1(x, i); // a
400 L(j, 1) = -x2(z, i) * x1(y, i); // b
401 L(j, 2) = -x2(z, i) * x1(z, i); // c
402 L(j, 3) = -x2(z, i) * x1(w, i); // d
403 L(j, 8) = x2(x, i) * x1(x, i); // i
404 L(j, 9) = x2(x, i) * x1(y, i); // j
405 L(j, 10) = x2(x, i) * x1(z, i); // k
406 L(j, 11) = x2(x, i) * x1(w, i); // l
407
408 ++j;
409 L(j, 4) = -x2(w, i) * x1(x, i); // e
410 L(j, 5) = -x2(w, i) * x1(y, i); // f
411 L(j, 6) = -x2(w, i) * x1(z, i); // g
412 L(j, 7) = -x2(w, i) * x1(w, i); // h
413 L(j, 12) = x2(y, i) * x1(x, i); // m
414 L(j, 13) = x2(y, i) * x1(y, i); // n
415 L(j, 14) = x2(y, i) * x1(z, i); // o
416 b(j, 0) = -x2(y, i) * x1(w, i);
417
418 ++j;
419 L(j, 0) = -x2(y, i) * x1(x, i); // a
420 L(j, 1) = -x2(y, i) * x1(y, i); // b
421 L(j, 2) = -x2(y, i) * x1(z, i); // c
422 L(j, 3) = -x2(y, i) * x1(w, i); // d
423 L(j, 4) = x2(x, i) * x1(x, i); // e
424 L(j, 5) = x2(x, i) * x1(y, i); // f
425 L(j, 6) = x2(x, i) * x1(z, i); // g
426 L(j, 7) = x2(x, i) * x1(w, i); // h
427
428 ++j;
429 L(j, 8) = -x2(w, i) * x1(x, i); // i
430 L(j, 9) = -x2(w, i) * x1(y, i); // j
431 L(j, 10) = -x2(w, i) * x1(z, i); // k
432 L(j, 11) = -x2(w, i) * x1(w, i); // l
433 L(j, 12) = x2(z, i) * x1(x, i); // m
434 L(j, 13) = x2(z, i) * x1(y, i); // n
435 L(j, 14) = x2(z, i) * x1(z, i); // o
436 b(j, 0) = -x2(z, i) * x1(w, i);
437 }
438 // Solve Lx=B
439 Vec h = L.fullPivLu().solve(b);
440 if ((L * h).isApprox(b, expected_precision)) {
442 return true;
443 } else {
444 return false;
445 }
446}
447
449 const Vec2& x1,
450 const Vec2& x2) {
451 Vec3 x(x1(0), x1(1), 1.0);
452 Vec3 y(x2(0), x2(1), 1.0);
453
454 Vec3 H_x = H * x;
455 Vec3 Hinv_y = H.inverse() * y;
456
457 H_x /= H_x(2);
458 Hinv_y /= Hinv_y(2);
459
460 return (H_x.head<2>() - y.head<2>()).squaredNorm() +
461 (Hinv_y.head<2>() - x.head<2>()).squaredNorm();
462}
463
464} // namespace libmv
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
SIMD_FORCE_INLINE btVector3 operator()(const btVector3 &x) const
Return the transform of the vector.
Definition btTransform.h:90
static void To(const Parameters &p, Parameterized *h)
Convert from the 8 parameters to a H matrix.
static void To(const Parameters &p, Parameterized *h)
Convert from the 15 parameters to a H matrix.
local_group_size(16, 16) .push_constant(Type b
CCL_NAMESPACE_BEGIN struct Options options
DEGForeachIDComponentCallback callback
#define NULL
const Mat y_
const Mat x_
float[3][3] Mat3
Definition gpu_matrix.cc:28
#define LG
#define VLOG(severity)
Definition log.h:34
#define T
#define L
#define T2
Definition md5.cpp:19
#define T1
Definition md5.cpp:18
#define H(x, y, z)
Eigen::VectorXd Vec
Definition numeric.h:61
Eigen::Matrix< double, Eigen::Dynamic, 15 > MatX15
Definition numeric.h:102
Eigen::Matrix< double, 3, 3 > Mat3
Definition numeric.h:72
Eigen::Vector2d Vec2
Definition numeric.h:105
Eigen::MatrixXd Mat
Definition numeric.h:60
Eigen::Matrix< double, Eigen::Dynamic, 8 > MatX8
Definition numeric.h:100
void ApplyTransformationToPoints(const Mat &points, const Mat3 &T, Mat *transformed_points)
bool Homography2DFromCorrespondencesLinear(const Mat &x1, const Mat &x2, Mat3 *H, double expected_precision)
bool EstimateHomography2DFromCorrespondences(const Mat &x1, const Mat &x2, const EstimateHomographyOptions &options, Mat3 *H)
Eigen::Vector3d Vec3
Definition numeric.h:106
bool Homography3DFromCorrespondencesLinear(const Mat &x1, const Mat &x2, Mat4 *H, double expected_precision)
double SymmetricGeometricDistance(const Mat3 &H, const Vec2 &x1, const Vec2 &x2)
static bool Homography2DFromCorrespondencesLinearEuc(const Mat &x1, const Mat &x2, Mat3 *H, double expected_precision)
void IsotropicPreconditionerFromPoints(const Mat &points, Mat3 *T)