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,
73 EuclideanReconstruction* reconstruction,
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,
107 reconstruction,
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) {
117 EuclideanReconstruction& reconstruction =
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++) {
159 EuclideanReconstruction reconstruction;
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);
230 EuclideanReconstruction& reconstruction =
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);
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,
298 &reconstruction,
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);
326 EuclideanReconstruction& reconstruction =
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,
348 &reconstruction,
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,
358 &reconstruction,
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]) {
386 const EuclideanReconstruction* reconstruction =
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) {
400 const EuclideanReconstruction* reconstruction =
401 &libmv_reconstruction->reconstruction;
402 const CameraIntrinsics* intrinsics = libmv_reconstruction->intrinsics;
403 libmv::vector<Marker> markers =
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) {
435 const EuclideanReconstruction* reconstruction =
436 &libmv_reconstruction->reconstruction;
437 const CameraIntrinsics* intrinsics = libmv_reconstruction->intrinsics;
438 libmv::vector<Marker> markers =
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]) {
473 const EuclideanReconstruction* reconstruction =
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
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between camera
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
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
EuclideanPoint * PointForTrack(int track)
Returns a pointer to the point corresponding to track.
EuclideanCamera * CameraForImage(int image)
Returns a pointer to the camera corresponding to image.
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.
input_tx image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "preview_img") .compute_source("compositor_compute_preview.glsl") .do_static_compilation(true)
#define NULL
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)
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)
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 EuclideanReprojectionError(const Tracks &image_tracks, const EuclideanReconstruction &reconstruction, const CameraIntrinsics &intrinsics)
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
#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