Blender V4.3
modal_solver.cc
Go to the documentation of this file.
1// Copyright (c) 2015 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 <cstdio>
24
25#include "ceres/ceres.h"
26#include "ceres/rotation.h"
29
30#ifdef _MSC_VER
31# define snprintf _snprintf
32#endif
33
34namespace libmv {
35
36namespace {
37void ProjectMarkerOnSphere(const Marker& marker, Vec3& X) {
38 X(0) = marker.x;
39 X(1) = marker.y;
40 X(2) = 1.0;
41
42 X *= 5.0 / X.norm();
43}
44
45void ModalSolverLogProgress(ProgressUpdateCallback* update_callback,
46 double progress) {
47 if (update_callback) {
48 char message[256];
49
50 snprintf(message,
51 sizeof(message),
52 "Solving progress %d%%",
53 (int)(progress * 100));
54
55 update_callback->invoke(progress, message);
56 }
57}
58
59struct ModalReprojectionError {
60 ModalReprojectionError(double observed_x,
61 double observed_y,
62 const double weight,
63 const Vec3& bundle)
64 : observed_x_(observed_x),
65 observed_y_(observed_y),
66 weight_(weight),
67 bundle_(bundle) {}
68
69 // TODO(keir): This should support bundling focal length as well.
70 template <typename T>
71 bool operator()(const T* quaternion, T* residuals) const {
72 // Convert bundle position from double to T.
73 T X[3] = {T(bundle_(0)), T(bundle_(1)), T(bundle_(2))};
74
75 // Compute the point position in camera coordinates: x = RX.
76 T x[3];
77
78 // This flips the sense of the quaternion, to adhere to Blender conventions.
79 T quaternion_inverse[4] = {
80 quaternion[0],
81 -quaternion[1],
82 -quaternion[2],
83 -quaternion[3],
84 };
85 ceres::QuaternionRotatePoint(quaternion_inverse, X, x);
86
87 // Compute normalized coordinates by dividing out the depth.
88 T xn = x[0] / x[2];
89 T yn = x[1] / x[2];
90
91 // The error is the difference between reprojected and observed marker
92 // positions, weighted by the passed in weight.
93 residuals[0] = T(weight_) * (xn - T(observed_x_));
94 residuals[1] = T(weight_) * (yn - T(observed_y_));
95
96 return true;
97 }
98
101 double weight_;
103};
104} // namespace
105
106void ModalSolver(const Tracks& tracks,
108 ProgressUpdateCallback* update_callback) {
109 int max_image = tracks.MaxImage();
110 int max_track = tracks.MaxTrack();
111
112 LG << "Max image: " << max_image;
113 LG << "Max track: " << max_track;
114
115 // For minimization we're using quaternions.
116 Vec3 zero_rotation = Vec3::Zero();
117 Vec4 quaternion;
118 ceres::AngleAxisToQuaternion(&zero_rotation(0), &quaternion(0));
119
120 for (int image = 0; image <= max_image; ++image) {
121 vector<Marker> all_markers = tracks.MarkersInImage(image);
122
123 ModalSolverLogProgress(update_callback, (float)image / max_image);
124
125 // Skip empty images without doing anything.
126 if (all_markers.size() == 0) {
127 LG << "Skipping image: " << image;
128 continue;
129 }
130
131 // STEP 1: Estimate rotation analytically.
132 Mat3 current_R;
133 ceres::QuaternionToRotation(&quaternion(0), &current_R(0, 0));
134
135 // Construct point cloud for current and previous images,
136 // using markers appear at current image for which we know
137 // 3D positions.
138 Mat x1, x2;
139 for (int i = 0; i < all_markers.size(); ++i) {
140 Marker& marker = all_markers[i];
142 if (point) {
143 Vec3 X;
144 ProjectMarkerOnSphere(marker, X);
145
146 int last_column = x1.cols();
147 x1.conservativeResize(3, last_column + 1);
148 x2.conservativeResize(3, last_column + 1);
149
150 x1.col(last_column) = current_R * point->X;
151 x2.col(last_column) = X;
152 }
153 }
154
155 if (x1.cols() >= 2) {
156 Mat3 delta_R;
157
158 // Compute delta rotation matrix for two point clouds.
159 // Could be a bit confusing at first glance, but order
160 // of clouds is indeed so.
161 GetR_FixedCameraCenter(x2, x1, 1.0, &delta_R);
162
163 // Convert delta rotation form matrix to final image
164 // rotation stored in a quaternion
165 Vec3 delta_angle_axis;
166 ceres::RotationMatrixToAngleAxis(&delta_R(0, 0), &delta_angle_axis(0));
167
168 Vec3 current_angle_axis;
169 ceres::QuaternionToAngleAxis(&quaternion(0), &current_angle_axis(0));
170
171 Vec3 angle_axis = current_angle_axis + delta_angle_axis;
172
173 ceres::AngleAxisToQuaternion(&angle_axis(0), &quaternion(0));
174
175 LG << "Analytically computed quaternion " << quaternion.transpose();
176 }
177
178 // STEP 2: Refine rotation with Ceres.
179 ceres::Problem problem;
180
181 // NOTE: Parameterization is lazily initialized when it is really needed,
182 // and is re-used by all parameters block.
183 ceres::Manifold* quaternion_manifold = NULL;
184
185 int num_residuals = 0;
186 for (int i = 0; i < all_markers.size(); ++i) {
187 Marker& marker = all_markers[i];
189
190 if (point && marker.weight != 0.0) {
191 problem.AddResidualBlock(
192 new ceres::AutoDiffCostFunction<ModalReprojectionError,
193 2, /* num_residuals */
194 4>(new ModalReprojectionError(
195 marker.x, marker.y, marker.weight, point->X)),
196 NULL,
197 &quaternion(0));
198 num_residuals++;
199
200 if (quaternion_manifold == NULL) {
201 quaternion_manifold = new ceres::QuaternionManifold();
202 }
203
204 problem.SetManifold(&quaternion(0), quaternion_manifold);
205 }
206 }
207
208 LG << "Number of residuals: " << num_residuals;
209
210 if (num_residuals) {
211 // Configure the solve.
212 ceres::Solver::Options solver_options;
213 solver_options.linear_solver_type = ceres::DENSE_QR;
214 solver_options.max_num_iterations = 50;
215 solver_options.update_state_every_iteration = true;
216 solver_options.gradient_tolerance = 1e-36;
217 solver_options.parameter_tolerance = 1e-36;
218 solver_options.function_tolerance = 1e-36;
219
220 // Run the solve.
221 ceres::Solver::Summary summary;
222 ceres::Solve(solver_options, &problem, &summary);
223
224 LG << "Summary:\n" << summary.FullReport();
225 LG << "Refined quaternion " << quaternion.transpose();
226 }
227
228 // Convert quaternion to rotation matrix.
229 Mat3 R;
230 ceres::QuaternionToRotation(&quaternion(0), &R(0, 0));
231 reconstruction->InsertCamera(image, R, Vec3::Zero());
232
233 // STEP 3: reproject all new markers appeared at image
234
235 // Check if there're new markers appeared on current image
236 // and reproject them on sphere to obtain 3D position/
237 for (int track = 0; track <= max_track; ++track) {
238 if (!reconstruction->PointForTrack(track)) {
239 Marker marker = tracks.MarkerInImageForTrack(image, track);
240
241 if (marker.image == image) {
242 // New track appeared on this image,
243 // project its position onto sphere.
244
245 LG << "Projecting track " << track << " at image " << image;
246
247 Vec3 X;
248 ProjectMarkerOnSphere(marker, X);
249 reconstruction->InsertPoint(track, R.inverse() * X);
250 }
251 }
252 }
253 }
254}
255
256} // namespace libmv
#define X
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
SIMD_FORCE_INLINE btVector3 operator()(const btVector3 &x) const
Return the transform of the vector.
Definition btTransform.h:90
const double weight_
Definition bundle.cc:289
ProjectivePoint * PointForTrack(int track)
Returns a pointer to the point corresponding to 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
const ProjectiveReconstruction & reconstruction
Definition intersect.cc:198
#define LG
#define T
#define R
double observed_y_
double observed_x_
Vec3 bundle_
Eigen::Vector4d Vec4
Definition numeric.h:107
void ModalSolver(const Tracks &tracks, EuclideanReconstruction *reconstruction, ProgressUpdateCallback *update_callback)
Eigen::Matrix< double, 3, 3 > Mat3
Definition numeric.h:72
Eigen::MatrixXd Mat
Definition numeric.h:60
void GetR_FixedCameraCenter(const Mat &x1, const Mat &x2, const double focal, Mat3 *R)
Definition panography.cc:99
Eigen::Vector3d Vec3
Definition numeric.h:106