Blender V4.3
intern/reconstruction.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
7#include "intern/tracks.h"
9
18
24using libmv::Marker;
26
32using libmv::Tracks;
33
36
37 /* Used for per-track average error calculation after reconstruction */
40
41 double error;
43};
44
45namespace {
46
47class ReconstructUpdateCallback : public ProgressUpdateCallback {
48 public:
49 ReconstructUpdateCallback(
50 reconstruct_progress_update_cb progress_update_callback,
51 void* callback_customdata) {
52 progress_update_callback_ = progress_update_callback;
53 callback_customdata_ = callback_customdata;
54 }
55
56 void invoke(double progress, const char* message) {
57 if (progress_update_callback_) {
58 progress_update_callback_(callback_customdata_, progress, message);
59 }
60 }
61
62 protected:
63 reconstruct_progress_update_cb progress_update_callback_;
64 void* callback_customdata_;
65};
66
67void libmv_solveRefineIntrinsics(
68 const Tracks& tracks,
69 const int refine_intrinsics,
70 const int bundle_constraints,
71 reconstruct_progress_update_cb progress_update_callback,
72 void* callback_customdata,
74 CameraIntrinsics* intrinsics) {
75 /* only a few combinations are supported but trust the caller/ */
76 int bundle_intrinsics = 0;
77
78 if (refine_intrinsics & LIBMV_REFINE_FOCAL_LENGTH) {
79 bundle_intrinsics |= libmv::BUNDLE_FOCAL_LENGTH;
80 }
81 if (refine_intrinsics & LIBMV_REFINE_PRINCIPAL_POINT) {
82 bundle_intrinsics |= libmv::BUNDLE_PRINCIPAL_POINT;
83 }
84
85#define SET_DISTORTION_FLAG_CHECKED(type, coefficient) \
86 do { \
87 if (refine_intrinsics & LIBMV_REFINE_##type##_DISTORTION_##coefficient) { \
88 bundle_intrinsics |= libmv::BUNDLE_##type##_##coefficient; \
89 } \
90 } while (0)
91
96
99
100#undef SET_DISTORTION_FLAG_CHECKED
101
102 progress_update_callback(callback_customdata, 1.0, "Refining solution");
103
105 bundle_intrinsics,
106 bundle_constraints,
108 intrinsics);
109}
110
111void finishReconstruction(
112 const Tracks& tracks,
113 const CameraIntrinsics& camera_intrinsics,
114 libmv_Reconstruction* libmv_reconstruction,
115 reconstruct_progress_update_cb progress_update_callback,
116 void* callback_customdata) {
118 libmv_reconstruction->reconstruction;
119
120 /* Reprojection error calculation. */
121 progress_update_callback(callback_customdata, 1.0, "Finishing solution");
122 libmv_reconstruction->tracks = tracks;
123 libmv_reconstruction->error =
124 EuclideanReprojectionError(tracks, reconstruction, camera_intrinsics);
125}
126
127bool selectTwoKeyframesBasedOnGRICAndVariance(
128 Tracks& tracks,
129 Tracks& normalized_tracks,
130 CameraIntrinsics& camera_intrinsics,
131 int& keyframe1,
132 int& keyframe2) {
133 libmv::vector<int> keyframes;
134
135 /* Get list of all keyframe candidates first. */
137 normalized_tracks, camera_intrinsics, keyframes);
138
139 if (keyframes.size() < 2) {
140 LG << "Not enough keyframes detected by GRIC";
141 return false;
142 } else if (keyframes.size() == 2) {
143 keyframe1 = keyframes[0];
144 keyframe2 = keyframes[1];
145 return true;
146 }
147
148 /* Now choose two keyframes with minimal reprojection error after initial
149 * reconstruction choose keyframes with the least reprojection error after
150 * solving from two candidate keyframes.
151 *
152 * In fact, currently libmv returns single pair only, so this code will
153 * not actually run. But in the future this could change, so let's stay
154 * prepared.
155 */
156 int previous_keyframe = keyframes[0];
157 double best_error = std::numeric_limits<double>::max();
158 for (int i = 1; i < keyframes.size(); i++) {
160 int current_keyframe = keyframes[i];
161 libmv::vector<Marker> keyframe_markers =
162 normalized_tracks.MarkersForTracksInBothImages(previous_keyframe,
163 current_keyframe);
164
165 Tracks keyframe_tracks(keyframe_markers);
166
167 /* get a solution from two keyframes only */
168 EuclideanReconstructTwoFrames(keyframe_markers, &reconstruction);
169 EuclideanBundle(keyframe_tracks, &reconstruction);
170 EuclideanCompleteReconstruction(keyframe_tracks, &reconstruction, NULL);
171
172 double current_error =
173 EuclideanReprojectionError(tracks, reconstruction, camera_intrinsics);
174
175 LG << "Error between " << previous_keyframe << " and " << current_keyframe
176 << ": " << current_error;
177
178 if (current_error < best_error) {
179 best_error = current_error;
180 keyframe1 = previous_keyframe;
181 keyframe2 = current_keyframe;
182 }
183
184 previous_keyframe = current_keyframe;
185 }
186
187 return true;
188}
189
190Marker libmv_projectMarker(const EuclideanPoint& point,
191 const EuclideanCamera& camera,
192 const CameraIntrinsics& intrinsics) {
193 libmv::Vec3 projected = camera.R * point.X + camera.t;
194 projected /= projected(2);
195
196 libmv::Marker reprojected_marker;
197 intrinsics.ApplyIntrinsics(
198 projected(0), projected(1), &reprojected_marker.x, &reprojected_marker.y);
199
200 reprojected_marker.image = camera.image;
201 reprojected_marker.track = point.track;
202 return reprojected_marker;
203}
204
205void libmv_getNormalizedTracks(const Tracks& tracks,
206 const CameraIntrinsics& camera_intrinsics,
207 Tracks* normalized_tracks) {
208 libmv::vector<Marker> markers = tracks.AllMarkers();
209 for (int i = 0; i < markers.size(); ++i) {
210 Marker& marker = markers[i];
211 camera_intrinsics.InvertIntrinsics(
212 marker.x, marker.y, &marker.x, &marker.y);
213 normalized_tracks->Insert(
214 marker.image, marker.track, marker.x, marker.y, marker.weight);
215 }
216}
217
218} // namespace
219
221 const libmv_Tracks* libmv_tracks,
222 const libmv_CameraIntrinsicsOptions* libmv_camera_intrinsics_options,
223 libmv_ReconstructionOptions* libmv_reconstruction_options,
224 reconstruct_progress_update_cb progress_update_callback,
225 void* callback_customdata) {
226 libmv_Reconstruction* libmv_reconstruction =
228
229 Tracks& tracks = *((Tracks*)libmv_tracks);
231 libmv_reconstruction->reconstruction;
232
233 ReconstructUpdateCallback update_callback =
234 ReconstructUpdateCallback(progress_update_callback, callback_customdata);
235
236 /* Retrieve reconstruction options from C-API to libmv API. */
237 CameraIntrinsics* camera_intrinsics;
238 camera_intrinsics = libmv_reconstruction->intrinsics =
239 libmv_cameraIntrinsicsCreateFromOptions(libmv_camera_intrinsics_options);
240
241 /* Invert the camera intrinsics/ */
242 Tracks normalized_tracks;
243 libmv_getNormalizedTracks(tracks, *camera_intrinsics, &normalized_tracks);
244
245 /* keyframe selection. */
246 int keyframe1 = libmv_reconstruction_options->keyframe1,
247 keyframe2 = libmv_reconstruction_options->keyframe2;
248
249 if (libmv_reconstruction_options->select_keyframes) {
250 LG << "Using automatic keyframe selection";
251
252 update_callback.invoke(0, "Selecting keyframes");
253
254 if (selectTwoKeyframesBasedOnGRICAndVariance(tracks,
255 normalized_tracks,
256 *camera_intrinsics,
257 keyframe1,
258 keyframe2)) {
259 /* so keyframes in the interface would be updated */
260 libmv_reconstruction_options->keyframe1 = keyframe1;
261 libmv_reconstruction_options->keyframe2 = keyframe2;
262 }
263 }
264
265 /* Actual reconstruction. */
266 LG << "frames to init from: " << keyframe1 << " " << keyframe2;
267
268 libmv::vector<Marker> keyframe_markers =
269 normalized_tracks.MarkersForTracksInBothImages(keyframe1, keyframe2);
270
271 LG << "number of markers for init: " << keyframe_markers.size();
272
273 if (keyframe_markers.size() < 16) {
274 LG << "No enough markers to initialize from";
275 libmv_reconstruction->is_valid = false;
276 return libmv_reconstruction;
277 }
278
279 update_callback.invoke(0, "Initial reconstruction");
280
281 if (!EuclideanReconstructTwoFrames(keyframe_markers, &reconstruction)) {
282 LG << "Failed to initialize reconstruction";
283 libmv_reconstruction->is_valid = false;
284 return libmv_reconstruction;
285 }
286
287 EuclideanBundle(normalized_tracks, &reconstruction);
288 EuclideanCompleteReconstruction(
289 normalized_tracks, &reconstruction, &update_callback);
290
291 /* Refinement. */
292 if (libmv_reconstruction_options->refine_intrinsics) {
293 libmv_solveRefineIntrinsics(tracks,
294 libmv_reconstruction_options->refine_intrinsics,
296 progress_update_callback,
297 callback_customdata,
299 camera_intrinsics);
300 }
301
302 /* Set reconstruction scale to unity. */
303 EuclideanScaleToUnity(&reconstruction);
304
305 /* Finish reconstruction. */
306 finishReconstruction(tracks,
307 *camera_intrinsics,
308 libmv_reconstruction,
309 progress_update_callback,
310 callback_customdata);
311
312 libmv_reconstruction->is_valid = true;
313 return (libmv_Reconstruction*)libmv_reconstruction;
314}
315
317 const libmv_Tracks* libmv_tracks,
318 const libmv_CameraIntrinsicsOptions* libmv_camera_intrinsics_options,
319 const libmv_ReconstructionOptions* libmv_reconstruction_options,
320 reconstruct_progress_update_cb progress_update_callback,
321 void* callback_customdata) {
322 libmv_Reconstruction* libmv_reconstruction =
324
325 Tracks& tracks = *((Tracks*)libmv_tracks);
327 libmv_reconstruction->reconstruction;
328
329 ReconstructUpdateCallback update_callback =
330 ReconstructUpdateCallback(progress_update_callback, callback_customdata);
331
332 /* Retrieve reconstruction options from C-API to libmv API. */
333 CameraIntrinsics* camera_intrinsics;
334 camera_intrinsics = libmv_reconstruction->intrinsics =
335 libmv_cameraIntrinsicsCreateFromOptions(libmv_camera_intrinsics_options);
336
337 /* Invert the camera intrinsics. */
338 Tracks normalized_tracks;
339 libmv_getNormalizedTracks(tracks, *camera_intrinsics, &normalized_tracks);
340
341 /* Actual reconstruction. */
342 ModalSolver(normalized_tracks, &reconstruction, &update_callback);
343
344 PolynomialCameraIntrinsics empty_intrinsics;
345 EuclideanBundleCommonIntrinsics(normalized_tracks,
349 &empty_intrinsics);
350
351 /* Refinement. */
352 if (libmv_reconstruction_options->refine_intrinsics) {
353 libmv_solveRefineIntrinsics(tracks,
354 libmv_reconstruction_options->refine_intrinsics,
356 progress_update_callback,
357 callback_customdata,
359 camera_intrinsics);
360 }
361
362 /* Finish reconstruction. */
363 finishReconstruction(tracks,
364 *camera_intrinsics,
365 libmv_reconstruction,
366 progress_update_callback,
367 callback_customdata);
368
369 libmv_reconstruction->is_valid = true;
370 return (libmv_Reconstruction*)libmv_reconstruction;
371}
372
374 return libmv_reconstruction->is_valid;
375}
376
378 LIBMV_OBJECT_DELETE(libmv_reconstruction->intrinsics, CameraIntrinsics);
379 LIBMV_OBJECT_DELETE(libmv_reconstruction, libmv_Reconstruction);
380}
381
383 const libmv_Reconstruction* libmv_reconstruction,
384 int track,
385 double pos[3]) {
387 &libmv_reconstruction->reconstruction;
388 const EuclideanPoint* point = reconstruction->PointForTrack(track);
389 if (point) {
390 pos[0] = point->X[0];
391 pos[1] = point->X[2];
392 pos[2] = point->X[1];
393 return 1;
394 }
395 return 0;
396}
397
399 const libmv_Reconstruction* libmv_reconstruction, int track) {
401 &libmv_reconstruction->reconstruction;
402 const CameraIntrinsics* intrinsics = libmv_reconstruction->intrinsics;
404 libmv_reconstruction->tracks.MarkersForTrack(track);
405
406 int num_reprojected = 0;
407 double total_error = 0.0;
408
409 for (int i = 0; i < markers.size(); ++i) {
410 double weight = markers[i].weight;
411 const EuclideanCamera* camera =
412 reconstruction->CameraForImage(markers[i].image);
413 const EuclideanPoint* point =
414 reconstruction->PointForTrack(markers[i].track);
415
416 if (!camera || !point || weight == 0.0) {
417 continue;
418 }
419
420 num_reprojected++;
421
422 Marker reprojected_marker =
423 libmv_projectMarker(*point, *camera, *intrinsics);
424 double ex = (reprojected_marker.x - markers[i].x) * weight;
425 double ey = (reprojected_marker.y - markers[i].y) * weight;
426
427 total_error += sqrt(ex * ex + ey * ey);
428 }
429
430 return total_error / num_reprojected;
431}
432
434 const libmv_Reconstruction* libmv_reconstruction, int image) {
436 &libmv_reconstruction->reconstruction;
437 const CameraIntrinsics* intrinsics = libmv_reconstruction->intrinsics;
439 libmv_reconstruction->tracks.MarkersInImage(image);
440 const EuclideanCamera* camera = reconstruction->CameraForImage(image);
441 int num_reprojected = 0;
442 double total_error = 0.0;
443
444 if (!camera) {
445 return 0.0;
446 }
447
448 for (int i = 0; i < markers.size(); ++i) {
449 const EuclideanPoint* point =
450 reconstruction->PointForTrack(markers[i].track);
451
452 if (!point) {
453 continue;
454 }
455
456 num_reprojected++;
457
458 Marker reprojected_marker =
459 libmv_projectMarker(*point, *camera, *intrinsics);
460 double ex = (reprojected_marker.x - markers[i].x) * markers[i].weight;
461 double ey = (reprojected_marker.y - markers[i].y) * markers[i].weight;
462
463 total_error += sqrt(ex * ex + ey * ey);
464 }
465
466 return total_error / num_reprojected;
467}
468
470 const libmv_Reconstruction* libmv_reconstruction,
471 int image,
472 double mat[4][4]) {
474 &libmv_reconstruction->reconstruction;
475 const EuclideanCamera* camera = reconstruction->CameraForImage(image);
476
477 if (camera) {
478 for (int j = 0; j < 3; ++j) {
479 for (int k = 0; k < 3; ++k) {
480 int l = k;
481
482 if (k == 1) {
483 l = 2;
484 } else if (k == 2) {
485 l = 1;
486 }
487
488 if (j == 2) {
489 mat[j][l] = -camera->R(j, k);
490 } else {
491 mat[j][l] = camera->R(j, k);
492 }
493 }
494 mat[j][3] = 0.0;
495 }
496
497 libmv::Vec3 optical_center = -camera->R.transpose() * camera->t;
498
499 mat[3][0] = optical_center(0);
500 mat[3][1] = optical_center(2);
501 mat[3][2] = optical_center(1);
502
503 mat[3][3] = 1.0;
504
505 return 1;
506 }
507
508 return 0;
509}
510
512 const libmv_Reconstruction* libmv_reconstruction) {
513 return libmv_reconstruction->error;
514}
515
517 libmv_Reconstruction* libmv_reconstruction) {
518 return (libmv_CameraIntrinsics*)libmv_reconstruction->intrinsics;
519}
sqrt(x)+1/max(0
ATTR_WARN_UNUSED_RESULT const BMLoop * l
virtual void ApplyIntrinsics(double normalized_x, double normalized_y, double *image_x, double *image_y) const =0
virtual void InvertIntrinsics(double image_x, double image_y, double *normalized_x, double *normalized_y) const =0
vector< Marker > MarkersInImage(int image) const
Returns all the markers visible in image.
vector< Marker > MarkersForTracksInBothImages(int image1, int image2) const
void Insert(int image, int track, double x, double y, double weight=1.0)
vector< Marker > MarkersForTrack(int track) const
Returns all the markers belonging to a track.
#define NULL
const vector< Marker > & markers
CameraIntrinsics * libmv_cameraIntrinsicsCreateFromOptions(const libmv_CameraIntrinsicsOptions *camera_intrinsics_options)
struct libmv_CameraIntrinsics libmv_CameraIntrinsics
libmv_Reconstruction * libmv_solveModal(const libmv_Tracks *libmv_tracks, const libmv_CameraIntrinsicsOptions *libmv_camera_intrinsics_options, const libmv_ReconstructionOptions *libmv_reconstruction_options, reconstruct_progress_update_cb progress_update_callback, void *callback_customdata)
double libmv_reprojectionErrorForImage(const libmv_Reconstruction *libmv_reconstruction, int image)
double libmv_reprojectionError(const libmv_Reconstruction *libmv_reconstruction)
void libmv_reconstructionDestroy(libmv_Reconstruction *libmv_reconstruction)
int libmv_reconstructionIsValid(libmv_Reconstruction *libmv_reconstruction)
int libmv_reprojectionCameraForImage(const libmv_Reconstruction *libmv_reconstruction, int image, double mat[4][4])
libmv_CameraIntrinsics * libmv_reconstructionExtractIntrinsics(libmv_Reconstruction *libmv_reconstruction)
int libmv_reprojectionPointForTrack(const libmv_Reconstruction *libmv_reconstruction, int track, double pos[3])
libmv_Reconstruction * libmv_solveReconstruction(const libmv_Tracks *libmv_tracks, const libmv_CameraIntrinsicsOptions *libmv_camera_intrinsics_options, libmv_ReconstructionOptions *libmv_reconstruction_options, reconstruct_progress_update_cb progress_update_callback, void *callback_customdata)
#define SET_DISTORTION_FLAG_CHECKED(type, coefficient)
double libmv_reprojectionErrorForTrack(const libmv_Reconstruction *libmv_reconstruction, int track)
void(* reconstruct_progress_update_cb)(void *customdata, double progress, const char *message)
@ LIBMV_REFINE_FOCAL_LENGTH
@ LIBMV_REFINE_PRINCIPAL_POINT
struct libmv_Tracks libmv_Tracks
const ProjectiveReconstruction & reconstruction
Definition intersect.cc:198
#define LG
@ BUNDLE_NO_TRANSLATION
Definition bundle.h:118
@ BUNDLE_NO_CONSTRAINTS
Definition bundle.h:117
@ BUNDLE_FOCAL_LENGTH
Definition bundle.h:102
@ BUNDLE_NO_INTRINSICS
Definition bundle.h:100
@ BUNDLE_PRINCIPAL_POINT
Definition bundle.h:103
void SelectKeyframesBasedOnGRICAndVariance(const Tracks &_tracks, const CameraIntrinsics &intrinsics, vector< int > &keyframes)
bool EuclideanReconstructTwoFrames(const vector< Marker > &markers, EuclideanReconstruction *reconstruction)
void EuclideanScaleToUnity(EuclideanReconstruction *reconstruction)
void EuclideanBundle(const Tracks &tracks, EuclideanReconstruction *reconstruction)
Definition bundle.cc:650
void EuclideanCompleteReconstruction(const Tracks &tracks, EuclideanReconstruction *reconstruction, ProgressUpdateCallback *update_callback)
std::vector< ElementType, Eigen::aligned_allocator< ElementType > > vector
Eigen::Vector3d Vec3
Definition numeric.h:106
void EuclideanBundleCommonIntrinsics(const Tracks &tracks, const int bundle_intrinsics, const int bundle_constraints, EuclideanReconstruction *reconstruction, CameraIntrinsics *intrinsics, BundleEvaluation *evaluation)
Definition bundle.cc:661
double EuclideanReprojectionError(const Tracks &image_tracks, const EuclideanReconstruction &reconstruction, const CameraIntrinsics &intrinsics)
@ TANGENTIAL
CameraIntrinsics * intrinsics
EuclideanReconstruction reconstruction
ListBase tracks
Definition tracking.cc:70
#define LIBMV_OBJECT_NEW(type,...)
Definition utildefines.h:39
#define LIBMV_OBJECT_DELETE(what, type)
Definition utildefines.h:42