Blender V4.3
least_squares_relocator.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include "slim.h"
12
13#include <Eigen/Dense>
14
15#include "BLI_assert.h"
16
17namespace slim {
18
19using namespace Eigen;
20
21static void apply_transformation(SLIMData &slim_data, Matrix2d &transformation_matrix)
22{
23 BLI_assert(slim_data.valid);
24
25 for (int i = 0; i < slim_data.V_o.rows(); i++) {
26 slim_data.V_o.row(i) = transformation_matrix * slim_data.V_o.row(i).transpose();
27 }
28}
29
30static void apply_translation(SLIMData &slim_data, Vector2d &translation_vector)
31{
32 BLI_assert(slim_data.valid);
33
34 for (int i = 0; i < slim_data.V_o.rows(); i++) {
35 slim_data.V_o.row(i) = translation_vector.transpose() + slim_data.V_o.row(i);
36 }
37}
38
40 const MatrixXd &all_uv_positions_in_initialization,
41 const VectorXi &indices_of_pinned_vertices,
42 MatrixXd &position_of_pinned_vertices_in_initialization)
43{
44 int i = 0;
45 for (VectorXi::InnerIterator it(indices_of_pinned_vertices, 0); it; ++it, i++) {
46 int vertex_index = it.value();
47 position_of_pinned_vertices_in_initialization.row(i) = all_uv_positions_in_initialization.row(
48 vertex_index);
49 }
50}
51
52static void flip_input_geometry(SLIMData &slim_data)
53{
54 BLI_assert(slim_data.valid);
55
56 VectorXi temp = slim_data.F.col(0);
57 slim_data.F.col(0) = slim_data.F.col(2);
58 slim_data.F.col(2) = temp;
59}
60
61static void compute_centroid(const MatrixXd &point_cloud, Vector2d &centroid)
62{
63 centroid << point_cloud.col(0).sum(), point_cloud.col(1).sum();
64 centroid /= point_cloud.rows();
65}
66
67/* Finds scaling matrix:
68 *
69 * T = |a 0|
70 * |0 a|
71 *
72 * s.t. if to each point p in the inizialized map the following is applied
73 *
74 * T*p
75 *
76 * We get the closest scaling of the positions of the vertices in the initialized map to the pinned
77 * vertices in a least squares sense. We find them by solving
78 *
79 * argmin_{t} At = p
80 *
81 * i.e.:
82 *
83 * | x_1 | |u_1|
84 * | . | | . |
85 * | . | | . |
86 * | x_n | |u_n|
87 * | y_1 | * | a | = |v_1|
88 * | . | | . |
89 * | . | | . |
90 * | y_n | |v_n|
91 *
92 * `t` is of dimension `1 x 1` and `p` of dimension `2*numberOfPinnedVertices x 1`
93 * is the vector holding the uv positions of the pinned vertices. */
94static void compute_least_squares_scaling(MatrixXd centered_pins,
95 MatrixXd centered_initialized_pins,
96 Matrix2d &transformation_matrix)
97{
98 int number_of_pinned_vertices = centered_pins.rows();
99
100 MatrixXd a = MatrixXd::Zero(number_of_pinned_vertices * 2, 1);
101 a << centered_initialized_pins.col(0), centered_initialized_pins.col(1);
102
103 VectorXd p(2 * number_of_pinned_vertices);
104 p << centered_pins.col(0), centered_pins.col(1);
105
106 VectorXd t = a.colPivHouseholderQr().solve(p);
107 t(0) = abs(t(0));
108 transformation_matrix << t(0), 0, 0, t(0);
109}
110
112 Vector2d &translation_vector,
113 Matrix2d &transformation_matrix,
114 bool is_flip_allowed)
115{
116 BLI_assert(slim_data.valid);
117
118 MatrixXd position_of_initialized_pins(slim_data.b.rows(), 2);
120 slim_data.V_o, slim_data.b, position_of_initialized_pins);
121
122 Vector2d centroid_of_initialized;
123 compute_centroid(position_of_initialized_pins, centroid_of_initialized);
124
125 Vector2d centroid_of_pins;
126 compute_centroid(slim_data.bc, centroid_of_pins);
127
128 MatrixXd centered_initialized_pins = position_of_initialized_pins.rowwise().operator-(
129 centroid_of_initialized.transpose());
130 MatrixXd centeredpins = slim_data.bc.rowwise().operator-(centroid_of_pins.transpose());
131
132 MatrixXd s = centered_initialized_pins.transpose() * centeredpins;
133
134 JacobiSVD<MatrixXd> svd(s, ComputeFullU | ComputeFullV);
135
136 Matrix2d vu_t = svd.matrixV() * svd.matrixU().transpose();
137
138 Matrix2d singular_values = Matrix2d::Identity();
139
140 bool contains_reflection = vu_t.determinant() < 0;
141 if (contains_reflection) {
142 if (!is_flip_allowed) {
143 singular_values(1, 1) = vu_t.determinant();
144 }
145 else {
146 flip_input_geometry(slim_data);
147 }
148 }
149
150 compute_least_squares_scaling(centeredpins, centered_initialized_pins, transformation_matrix);
151
152 transformation_matrix = transformation_matrix * svd.matrixV() * singular_values *
153 svd.matrixU().transpose();
154
155 translation_vector = centroid_of_pins - transformation_matrix * centroid_of_initialized;
156}
157
159 Matrix2d &transformation_matrix)
160{
161 BLI_assert(slim_data.valid);
162
163 Vector2d pinned_position_difference_vector = slim_data.bc.row(0) - slim_data.bc.row(1);
164 Vector2d initialized_position_difference_vector = slim_data.V_o.row(slim_data.b(0)) -
165 slim_data.V_o.row(slim_data.b(1));
166
167 double scale = pinned_position_difference_vector.norm() /
168 initialized_position_difference_vector.norm();
169
170 pinned_position_difference_vector.normalize();
171 initialized_position_difference_vector.normalize();
172
173 /* TODO: sometimes rotates in wrong direction. */
174 double cos_angle = pinned_position_difference_vector.dot(initialized_position_difference_vector);
175 double sin_angle = sqrt(1 - pow(cos_angle, 2));
176
177 transformation_matrix << cos_angle, -sin_angle, sin_angle, cos_angle;
178 transformation_matrix = (Matrix2d::Identity() * scale) * transformation_matrix;
179}
180
181static void compute_translation1_pin(const SLIMData &slim_data, Vector2d &translation_vector)
182{
183 BLI_assert(slim_data.valid);
184 translation_vector = slim_data.bc.row(0) - slim_data.V_o.row(slim_data.b(0));
185}
186
187static void transform_initialized_map(SLIMData &slim_data)
188{
189 BLI_assert(slim_data.valid);
190 Matrix2d transformation_matrix;
191 Vector2d translation_vector;
192
193 int number_of_pinned_vertices = slim_data.b.rows();
194
195 switch (number_of_pinned_vertices) {
196 case 0:
197 return;
198 case 1: /* Only translation is needed with one pin. */
199 compute_translation1_pin(slim_data, translation_vector);
200 apply_translation(slim_data, translation_vector);
201 break;
202 case 2:
203 compute_transformation_matrix2_pins(slim_data, transformation_matrix);
204 apply_transformation(slim_data, transformation_matrix);
205 compute_translation1_pin(slim_data, translation_vector);
206 apply_translation(slim_data, translation_vector);
207 break;
208 default:
209
210 bool flip_allowed = slim_data.reflection_mode == 0;
211
213 slim_data, translation_vector, transformation_matrix, flip_allowed);
214
215 apply_transformation(slim_data, transformation_matrix);
216 apply_translation(slim_data, translation_vector);
217
218 break;
219 }
220}
221
222static bool is_translation_needed(const SLIMData &slim_data)
223{
224 BLI_assert(slim_data.valid);
225 bool pinned_vertices_exist = (slim_data.b.rows() > 0);
226 bool was_initialized = !slim_data.skipInitialization;
227 return was_initialized && pinned_vertices_exist;
228}
229
231{
232 BLI_assert(slim_data.valid);
233
234 if (!is_translation_needed(slim_data)) {
235 return;
236 }
237
238 transform_initialized_map(slim_data);
239}
240} // namespace slim
#define BLI_assert(a)
Definition BLI_assert.h:50
sqrt(x)+1/max(0
pow(value.r - subtrahend, 2.0)") .do_static_compilation(true)
static void apply_translation(SLIMData &slim_data, Vector2d &translation_vector)
static void compute_centroid(const MatrixXd &point_cloud, Vector2d &centroid)
static void compute_translation1_pin(const SLIMData &slim_data, Vector2d &translation_vector)
void transform_initialization_if_necessary(SLIMData &slim_data)
static void compute_transformation_matrix2_pins(const SLIMData &slim_data, Matrix2d &transformation_matrix)
static void retrieve_positions_of_pinned_vertices_in_initialization(const MatrixXd &all_uv_positions_in_initialization, const VectorXi &indices_of_pinned_vertices, MatrixXd &position_of_pinned_vertices_in_initialization)
static void apply_transformation(SLIMData &slim_data, Matrix2d &transformation_matrix)
static bool is_translation_needed(const SLIMData &slim_data)
static void comput_least_squares_rotation_scale_only(SLIMData &slim_data, Vector2d &translation_vector, Matrix2d &transformation_matrix, bool is_flip_allowed)
static void compute_least_squares_scaling(MatrixXd centered_pins, MatrixXd centered_initialized_pins, Matrix2d &transformation_matrix)
static void flip_input_geometry(SLIMData &slim_data)
static void transform_initialized_map(SLIMData &slim_data)
Eigen::MatrixXi F
Definition slim.h:30
Eigen::MatrixXd bc
Definition slim.h:44
bool skipInitialization
Definition slim.h:51
Eigen::VectorXi b
Definition slim.h:43
bool valid
Definition slim.h:26
int reflection_mode
Definition slim.h:50
Eigen::MatrixXd V_o
Definition slim.h:56
ccl_device_inline int abs(int x)
Definition util/math.h:120