LSST Applications g04a91732dc+146a938ab0,g07dc498a13+80b84b0d75,g0fba68d861+4c4f3dcb5c,g1409bbee79+80b84b0d75,g1a7e361dbc+80b84b0d75,g1fd858c14a+f6e422e056,g20f46db602+333b6c0f32,g35bb328faa+fcb1d3bbc8,g42c1b31a95+a1301e4c20,g4d2262a081+f1facf12e5,g4d39ba7253+9b833be27e,g4e0f332c67+5d362be553,g53246c7159+fcb1d3bbc8,g60b5630c4e+9b833be27e,g78460c75b0+2f9a1b4bcd,g786e29fd12+cf7ec2a62a,g7b71ed6315+fcb1d3bbc8,g8852436030+790117df0f,g89139ef638+80b84b0d75,g8d6b6b353c+9b833be27e,g9125e01d80+fcb1d3bbc8,g989de1cb63+80b84b0d75,g9f33ca652e+9c6b68d7f3,ga9baa6287d+9b833be27e,gaaedd4e678+80b84b0d75,gabe3b4be73+1e0a283bba,gb1101e3267+9f3571abad,gb58c049af0+f03b321e39,gb90eeb9370+691e4ab549,gc741bbaa4f+2bcd3860df,gcf25f946ba+790117df0f,gd315a588df+5b65d88fe4,gd6cbbdb0b4+c8606af20c,gd9a9a58781+fcb1d3bbc8,gde0f65d7ad+ee6a3faa19,ge278dab8ac+932305ba37,ge82c20c137+76d20ab76d,gee8db133a9+2a6ae0040b,w.2025.10
LSST Data Management Base Package
Loading...
Searching...
No Matches
pessimisticPatternMatcherUtils.cc
Go to the documentation of this file.
1// -*- LSST-C++ -*-
2
3/*
4 * This file is part of meas_astrom.
5 *
6 * Developed for the LSST Data Management System.
7 * This product includes software developed by the LSST Project
8 * (https://www.lsst.org).
9 * See the COPYRIGHT file at the top-level directory of this distribution
10 * for details of code ownership.
11 *
12 * This program is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <https://www.gnu.org/licenses/>.
24 */
25
26#include <cmath>
27#include "ndarray/eigen.h"
29
30namespace {
31
33int sgn(double val) { return (0.0 < val) - (val < 0.0); }
34} // namespace
35
36namespace lsst {
37namespace meas {
38namespace astrom {
39
41 ndarray::Array<double, 2, 1> src_pattern_array, ndarray::Array<double, 2, 1> src_delta_array,
42 ndarray::Array<double, 1, 1> src_dist_array, ndarray::Array<float, 1, 1> dist_array,
43 ndarray::Array<uint16_t, 2, 1> id_array, ndarray::Array<double, 2, 1> reference_array, size_t n_match,
44 double max_cos_theta_shift, double max_cos_rot_sq, double max_dist_rad) {
45 // Our first test. We search the reference dataset for pairs that have the same length as our first source
46 // pairs to within plus/minus the max_dist tolerance.
47 std::pair<size_t, size_t> candidate_range =
48 find_candidate_reference_pair_range(src_dist_array[0], dist_array, max_dist_rad);
49 size_t ref_dist_idx = (candidate_range.first + candidate_range.second) / 2;
50
51 // Start our loop over the candidate reference objects. Looping from the inside (minimum difference to our
52 // source dist) to the outside.
53 for (size_t idx = 0; idx < candidate_range.second - candidate_range.first; idx++) {
54 // TODO DM-33514: cleanup this loop to use an iterator that handles the "inside-out" iteration.
55 if (idx % 2 == 0) {
56 ref_dist_idx = ref_dist_idx + idx;
57 } else {
58 ref_dist_idx = ref_dist_idx - idx;
59 }
60 // We have two candidates for which reference object corresponds with the source at the center of our
61 // pattern. As such we loop over and test both possibilities.
62 ndarray::Array<uint16_t, 1, 1> tmp_ref_pair_list = id_array[ref_dist_idx];
63 for (uint16_t ref_pair_idx = 0; ref_pair_idx < 2; ref_pair_idx++) {
64 uint16_t ref_id = id_array[ref_dist_idx][ref_pair_idx];
66 // Test the angle between our candidate ref center and the source center of our pattern. This
67 // angular distance also defines the shift we will later use.
68 ndarray::Array<double, 1, 1> ref_center = reference_array[ref_id];
69 double cos_shift =
70 ndarray::asEigenMatrix(src_pattern_array[0]).dot(ndarray::asEigenMatrix(ref_center));
71 // DM-49033: ensure the returned cosine is in range [-1.0, 1.0]
72 cos_shift = std::clamp(cos_shift, -1.0, 1.0);
73 if (cos_shift < max_cos_theta_shift) {
74 continue;
75 }
76 // We can now append this one as a candidate.
77 candidate_pairs.push_back(std::make_pair(ref_id, 0));
78 ndarray::Array<double, 1, 1> ref_delta;
79 // Test to see which reference object to use in the pair.
80 if (ref_id == *tmp_ref_pair_list.begin()) {
81 candidate_pairs.push_back(std::make_pair(tmp_ref_pair_list[1], 1));
82 ref_delta = copy(reference_array[tmp_ref_pair_list[1]] - ref_center);
83 } else {
84 candidate_pairs.push_back(std::make_pair(tmp_ref_pair_list[0], 1));
85 ref_delta = copy(reference_array[tmp_ref_pair_list[0]] - ref_center);
86 }
87 // For dense fields it will be faster to compute the absolute rotation this pair suggests first
88 // rather than saving it after all the spokes are found. We then compute the cos^2 of the rotation
89 // and first part of the rotation matrix from source to reference frame.
90 RotationTestResult test_rot_result =
91 test_rotation(src_pattern_array[0], ref_center, src_delta_array[0], ref_delta, cos_shift,
92 max_cos_rot_sq);
93 if (!test_rot_result.success) {
94 continue;
95 }
96 // Now that we have a candidate first spoke and reference pattern center, we mask our future
97 // search to only those pairs that contain our candidate reference center.
98 SortedArrayResult sorted_array_struct = create_sorted_arrays(ref_center, reference_array);
99 // Now we feed this sub data to match the spokes of our pattern.
101 create_pattern_spokes(src_pattern_array[0], src_delta_array, src_dist_array, ref_center,
102 test_rot_result.proj_ref_ctr_delta, sorted_array_struct.dists,
103 sorted_array_struct.ids, reference_array, max_dist_rad, n_match);
104 // If we don't find enough candidates we can continue to the next reference center pair.
105 if (pattern_spokes.size() < n_match - 2) {
106 continue;
107 }
108 // If we have the right number of matched ids we store these.
109 candidate_pairs.reserve(candidate_pairs.size() + pattern_spokes.size());
110 candidate_pairs.insert(candidate_pairs.end(), pattern_spokes.begin(), pattern_spokes.end());
111 // We can now create our full matrix for both the shift and rotation. The shift aligns
112 // the pattern centers, while the rotation rotates the spokes on top of each other.
113 ShiftRotMatrixResult shift_rot_result =
114 create_shift_rot_matrix(test_rot_result.cos_rot_sq, test_rot_result.shift_matrix,
115 src_delta_array[0], ref_center, ref_delta);
116
117 // concatenate our final patterns.
118 Eigen::Matrix3d shift_rot_matrix = shift_rot_result.shift_rot_matrix;
121 ref_pattern.reserve(n_match);
122 src_pattern.reserve(n_match);
123 for (size_t idx = 0; idx < n_match; idx++) {
124 ref_pattern[idx] = ndarray::asEigenMatrix(reference_array[candidate_pairs[idx].first]);
125 src_pattern[idx] = ndarray::asEigenMatrix(src_pattern_array[candidate_pairs[idx].second]);
126 }
127 // TODO: DM-32985 Implement least squares linear fitting here. Look at python example linked in
128 // ticket for how to implement.
129 // Test that the all points in each pattern are within tolerance after shift/rotation.
130 bool passed =
131 intermediate_verify_comparison(src_pattern, ref_pattern, shift_rot_matrix, max_dist_rad);
132 if (passed) {
133 return PatternResult(candidate_pairs, shift_rot_matrix, cos_shift, shift_rot_result.sin_rot);
134 }
135 }
136 }
137 // failed fit
138 return PatternResult();
139}
140
141SortedArrayResult create_sorted_arrays(ndarray::Array<double, 1, 1> const& ref_center,
142 ndarray::Array<double, 2, 1> const& reference_array) {
143 SortedArrayResult result;
144 // NOTE: this algorithm is quadratic in the length of reference_array. It might be worth using std::sort
145 // instead of this approach, if reference_array is long, but the length at which that matters should
146 // be tested because it would require caching of distances.
147 for (uint16_t idx = 0; idx < reference_array.getShape()[0]; idx++) {
148 Eigen::Vector3d diff = ndarray::asEigenMatrix(copy(reference_array[idx] - ref_center));
149 double dist = sqrt(diff.dot(diff));
150 auto dists_itr = std::lower_bound(result.dists.begin(), result.dists.end(), dist);
151 auto ids_itr = result.ids.begin() + (dists_itr - result.dists.begin());
152 result.ids.insert(ids_itr, idx);
153 result.dists.insert(dists_itr, dist);
154 }
155 return result;
156}
157
159 float src_dist, ndarray::Array<float, 1, 1> const& ref_dist_array, double max_dist_rad) {
160 auto itr =
161 std::lower_bound(ref_dist_array.begin(), ref_dist_array.end(), src_dist - max_dist_rad - 1e-16);
162 auto itrEnd =
163 std::upper_bound(ref_dist_array.begin(), ref_dist_array.end(), src_dist + max_dist_rad + 1e-16);
164
165 size_t startIdx = itr - ref_dist_array.begin();
166 size_t endIdx = itrEnd - ref_dist_array.begin();
167 return std::make_pair(startIdx, endIdx);
168}
169
171 std::vector<float> const& ref_dist_array,
172 double max_dist_rad) {
173 auto itr =
174 std::lower_bound(ref_dist_array.begin(), ref_dist_array.end(), src_dist - max_dist_rad - 1e-16);
175 auto itrEnd =
176 std::upper_bound(ref_dist_array.begin(), ref_dist_array.end(), src_dist + max_dist_rad + 1e-16);
177
178 size_t startIdx = itr - ref_dist_array.begin();
179 size_t endIdx = itrEnd - ref_dist_array.begin();
180 return std::make_pair(startIdx, endIdx);
181}
182
183RotationTestResult test_rotation(ndarray::Array<double, 1, 1> const& src_center,
184 ndarray::Array<double, 1, 1> const& ref_center,
185 ndarray::Array<double, 1, 1> const& src_delta,
186 ndarray::Array<double, 1, 1> const& ref_delta, double cos_shift,
187 double max_cos_rot_sq) {
188 // Make sure the sine is a real number.
189 if (cos_shift > 1.0) {
190 cos_shift = 1;
191 } else if (cos_shift < -1.0) {
192 cos_shift = -1;
193 }
194 double sin_shift = sqrt(1 - cos_shift * cos_shift);
195
196 // If the sine of our shift is zero we only need to use the identity matrix for the shift. Else we
197 // construct the rotation matrix for shift.
198 auto ref_center_eigen = ndarray::asEigenMatrix(ref_center).head<3>();
199 Eigen::Matrix3d shift_matrix;
200 if (sin_shift > 0) {
201 Eigen::Vector3d rot_axis = ndarray::asEigenMatrix(src_center).head<3>().cross(ref_center_eigen);
202 rot_axis /= sin_shift;
203 shift_matrix = create_spherical_rotation_matrix(rot_axis, cos_shift, sin_shift);
204 } else {
205 shift_matrix = Eigen::Matrix3d::Identity();
206 }
207
208 // Now that we have our shift we apply it to the src delta vector and check the rotation.
209 Eigen::Vector3d rot_src_delta = shift_matrix * ndarray::asEigenMatrix(src_delta);
210 Eigen::Vector3d proj_src_delta = rot_src_delta - rot_src_delta.dot(ref_center_eigen) * ref_center_eigen;
211 Eigen::Vector3d ref_delta_eigen = ndarray::asEigenMatrix(ref_delta);
212
213 Eigen::Vector3d proj_ref_delta =
214 ref_delta_eigen - ref_delta_eigen.dot(ref_center_eigen) * ref_center_eigen;
215 double proj_src_delta_sq = proj_src_delta.dot(proj_ref_delta);
216 double cos_rot_sq = proj_src_delta_sq * proj_src_delta_sq /
217 (proj_src_delta.dot(proj_src_delta) * proj_ref_delta.dot(proj_ref_delta));
218 if (cos_rot_sq < max_cos_rot_sq) {
219 // Return failure if the rotation isn't within tolerance.
220 return RotationTestResult();
221 }
222 return RotationTestResult(cos_rot_sq, proj_ref_delta, shift_matrix);
223}
224
225Eigen::Matrix3d create_spherical_rotation_matrix(Eigen::Vector3d const& rot_axis, double cos_rotation,
226 double sin_rotation) {
227 Eigen::Matrix3d rot_cross_matrix;
228 // clang-format off
229 rot_cross_matrix << 0., -rot_axis[2], rot_axis[1],
230 rot_axis[2], 0., -rot_axis[0],
231 -rot_axis[1], rot_axis[0], 0.;
232 // clang-format on
233 Eigen::Matrix3d shift_matrix = cos_rotation * Eigen::Matrix3d::Identity() +
234 sin_rotation * rot_cross_matrix +
235 (1 - cos_rotation) * rot_axis * rot_axis.transpose();
236 return shift_matrix;
237}
238
239ShiftRotMatrixResult create_shift_rot_matrix(double cos_rot_sq, Eigen::Matrix3d const& shift_matrix,
240 ndarray::Array<double, 1, 1> const& src_delta,
241 ndarray::Array<double, 1, 1> const& ref_ctr,
242 ndarray::Array<double, 1, 1> const& ref_delta) {
243 double cos_rot = sqrt(cos_rot_sq);
244 Eigen::Vector3d src_delta_eigen = ndarray::asEigenMatrix(src_delta);
245 Eigen::Vector3d rot_src_delta = shift_matrix * src_delta_eigen;
246 Eigen::Vector3d tmp_cross = rot_src_delta.cross(ndarray::asEigenMatrix(ref_delta).head<3>());
247 double delta_dot_cross = tmp_cross.dot(ndarray::asEigenMatrix(ref_ctr));
248
249 double sin_rot = sgn(delta_dot_cross) * sqrt(1 - cos_rot_sq);
250 Eigen::Matrix3d rot_matrix =
251 create_spherical_rotation_matrix(ndarray::asEigenMatrix(ref_ctr), cos_rot, sin_rot);
252
253 Eigen::Matrix3d shift_rot_matrix = rot_matrix * shift_matrix;
254
256 result.sin_rot = sin_rot;
257 result.shift_rot_matrix = shift_rot_matrix;
258 return result;
259}
260
262 std::vector<Eigen::Vector3d> const& ref_pattern,
263 Eigen::Matrix3d const& shift_rot_matrix, double max_dist_rad) {
264 double max_dist_sq = max_dist_rad * max_dist_rad;
265 bool passed = true;
266 auto iSrc = src_pattern.begin();
267 auto iRef = ref_pattern.begin();
268 for (auto iSrc = src_pattern.begin(), iRef = ref_pattern.begin();
269 iSrc != src_pattern.end() && iRef != ref_pattern.end(); iSrc++, iRef++) {
270 Eigen::Vector3d rot_src_vect = shift_rot_matrix * *iSrc;
271 Eigen::Vector3d diff_vect = rot_src_vect - *iRef;
272 if (max_dist_sq < diff_vect.dot(diff_vect)) {
273 passed = false;
274 break;
275 }
276 }
277 return passed;
278}
279
281 ndarray::Array<double, 1, 1> const& src_ctr, ndarray::Array<double, 2, 1> const& src_delta_array,
282 ndarray::Array<double, 1, 1> const& src_dist_array, ndarray::Array<double, 1, 1> const& ref_ctr,
283 Eigen::Vector3d const& proj_ref_ctr_delta, std::vector<float> const& ref_dist_array,
284 std::vector<uint16_t> const& ref_id_array, ndarray::Array<double, 2, 1> const& reference_array,
285 double max_dist_rad, size_t n_match) {
286 // Struct where we will be putting our results.
288
289 // Counter for number of spokes we failed to find a reference
290 // candidate for. We break the loop if we haven't found enough.
291 size_t n_fail = 0;
292
293 // Plane project the center/first spoke of the source pattern using
294 // the center vector of the pattern as normal.
295 auto src_ctr_eigen = ndarray::asEigenMatrix(src_ctr);
296 double src_delta_ctr_dot = ndarray::asEigenMatrix(src_delta_array[0]).dot(src_ctr_eigen);
297
298 ndarray::Array<double, 1, 1> proj_src_ctr_delta = copy(src_delta_array[0] - src_delta_ctr_dot * src_ctr);
299 auto proj_src_ctr_delta_eigen = ndarray::asEigenMatrix(proj_src_ctr_delta);
300 double proj_src_ctr_dist_sq = proj_src_ctr_delta_eigen.dot(proj_src_ctr_delta_eigen);
301
302 // Pre - compute the squared length of the projected reference vector.
303 double proj_ref_ctr_dist_sq = proj_ref_ctr_delta.dot(proj_ref_ctr_delta);
304
305 // Value of sin where sin(theta) ~= theta to within 0.1%. Used to make
306 // sure we are still in small angle approximation.
307 double max_sin_tol = 0.0447;
308 // Loop over the source pairs.
309 for (size_t src_idx = 1; src_idx < src_dist_array.size(); src_idx++) {
310 if (n_fail > src_dist_array.size() - (n_match - 1)) {
311 break;
312 }
313 // Find the reference pairs that include our candidate pattern center
314 // and sort them in increasing delta. Check this first so we don't
315 // compute anything else if no candidates exist.
316 std::pair<size_t, size_t> candidate_range =
317 find_candidate_reference_pair_range(src_dist_array[src_idx], ref_dist_array, max_dist_rad);
318 if (candidate_range.first == candidate_range.second) {
319 n_fail++;
320 continue;
321 }
322
323 // Given our length tolerance we can use it to compute a tolerance
324 // on the angle between our spoke.
325 double src_sin_tol = max_dist_rad / (src_dist_array[src_idx] + max_dist_rad);
326
327 // Test if the small angle approximation will still hold. This is
328 // defined as when sin(theta) ~= theta to within 0.1% of each
329 // other. If the implied opening angle is too large we set it to
330 // the 0.1% threshold.
331 if (src_sin_tol > max_sin_tol) {
332 src_sin_tol = max_sin_tol;
333 }
334
335 // Plane project the candidate source spoke and compute the cosine
336 // and sine of the opening angle.
337 double proj_src_delta_dot = ndarray::asEigenMatrix(src_delta_array[src_idx]).dot(src_ctr_eigen);
338
339 ndarray::Array<double, 1, 1> proj_src_delta =
340 copy(src_delta_array[src_idx] - proj_src_delta_dot * src_ctr);
341 auto proj_src_delta_eigen = ndarray::asEigenMatrix(proj_src_delta);
342 double geom_dist_src = sqrt(proj_src_delta_eigen.dot(proj_src_delta_eigen) * proj_src_ctr_dist_sq);
343
344 // Compute cosine and sine of the delta vector opening angle.
345 double cos_theta_src = proj_src_delta_eigen.dot(proj_src_ctr_delta_eigen) / geom_dist_src;
346 Eigen::Vector3d cross_src =
347 proj_src_delta_eigen.head<3>().cross(proj_src_ctr_delta_eigen.head<3>()) / geom_dist_src;
348 double sin_theta_src = cross_src.dot(src_ctr_eigen);
349
350 // Test the spokes and return the id of the reference object.
351 // Return -1 if no match is found.
352 int ref_id =
353 check_spoke(cos_theta_src, sin_theta_src, ref_ctr, proj_ref_ctr_delta, proj_ref_ctr_dist_sq,
354 candidate_range, ref_id_array, reference_array, src_sin_tol);
355 if (ref_id < 0) {
356 n_fail++;
357 continue;
358 }
359
362 output_spokes.push_back(std::make_pair(static_cast<size_t>(ref_id), src_idx + 1));
363 if (output_spokes.size() >= n_match - 2) {
364 break;
365 }
366 }
367 return output_spokes;
368}
369
370int check_spoke(double cos_theta_src, double sin_theta_src, ndarray::Array<double, 1, 1> const& ref_ctr,
371 Eigen::Vector3d const& proj_ref_ctr_delta, double proj_ref_ctr_dist_sq,
372 std::pair<size_t, size_t> const& candidate_range, std::vector<uint16_t> const& ref_id_array,
373 ndarray::Array<double, 2, 1> const& reference_array, double src_sin_tol) {
374 // Loop over our candidate reference objects. candidate_range is the min
375 // and max of for pair candidates and are view into ref_id_array. Here we
376 // start from the midpoint of min and max values and step outward.
377 size_t midpoint = (candidate_range.first + candidate_range.second) / 2;
378 for (size_t idx = 0; idx < candidate_range.second - candidate_range.first; idx++) {
379 // TODO DM-33514: cleanup this loop to use an iterator that handles the "inside-out" iteration.
380 if (idx % 2 == 0) {
381 midpoint = midpoint + idx;
382 } else {
383 midpoint = midpoint - idx;
384 }
385 // Compute the delta vector from the pattern center.
386 uint16_t ref_id = ref_id_array[midpoint];
387 ndarray::Array<double, 1, 1> ref_delta = copy(reference_array[ref_id] - ref_ctr);
388
389 double ref_dot = ndarray::asEigenMatrix(ref_delta).dot(ndarray::asEigenMatrix(ref_ctr));
390 ndarray::Array<double, 1, 1> proj_ref_delta = copy(ref_delta - ref_dot * ref_ctr);
391 // Compute the cos between our "center" reference vector and the
392 // current reference candidate.
393 auto proj_ref_delta_eigen = ndarray::asEigenMatrix(proj_ref_delta);
394 double proj_delta_dist_sq = proj_ref_delta_eigen.dot(proj_ref_delta_eigen);
395 double geom_dist_ref = sqrt(proj_ref_ctr_dist_sq * proj_delta_dist_sq);
396 double cos_theta_ref = proj_ref_delta_eigen.dot(proj_ref_ctr_delta) / geom_dist_ref;
397
398 // Make sure we can safely make the comparison in case
399 // our "center" and candidate vectors are mostly aligned.
400 double cos_sq_comparison;
401 if (cos_theta_ref * cos_theta_ref < (1 - src_sin_tol * src_sin_tol)) {
402 cos_sq_comparison = (cos_theta_src - cos_theta_ref) * (cos_theta_src - cos_theta_ref) /
403 (1 - cos_theta_ref * cos_theta_ref);
404 } else {
405 cos_sq_comparison = (cos_theta_src - cos_theta_ref) * (cos_theta_src - cos_theta_ref) /
406 (src_sin_tol * src_sin_tol);
407 }
408 // Test the difference of the cosine of the reference angle against
409 // the source angle. Assumes that the delta between the two is
410 // small.
411 if (cos_sq_comparison > src_sin_tol * src_sin_tol) {
412 continue;
413 }
414 // The cosine tests the magnitude of the angle but not
415 // its direction. To do that we need to know the sine as well.
416 // This cross product calculation does that.
417 Eigen::Vector3d cross_ref = proj_ref_delta_eigen.head<3>().cross(proj_ref_ctr_delta) / geom_dist_ref;
418 double sin_theta_ref = cross_ref.dot(ndarray::asEigenMatrix(ref_ctr));
419 // Check the value of the cos again to make sure that it is not
420 // near zero.
421 double sin_comparison;
422 if (abs(cos_theta_src) < src_sin_tol) {
423 sin_comparison = (sin_theta_src - sin_theta_ref) / src_sin_tol;
424 } else {
425 sin_comparison = (sin_theta_src - sin_theta_ref) / cos_theta_ref;
426 }
427
428 // Return the correct id of the candidate we found.
429 if (abs(sin_comparison) < src_sin_tol) {
430 return ref_id;
431 }
432 }
433 return -1;
434}
435
436} // namespace astrom
437} // namespace meas
438} // namespace lsst
T begin(T... args)
T end(T... args)
T insert(T... args)
T lower_bound(T... args)
T make_pair(T... args)
RotationTestResult test_rotation(ndarray::Array< double, 1, 1 > const &src_center, ndarray::Array< double, 1, 1 > const &ref_center, ndarray::Array< double, 1, 1 > const &src_delta, ndarray::Array< double, 1, 1 > const &ref_delta, double cos_shift, double max_cos_rot_sq)
Test if the rotation implied between the source pattern and reference pattern is within tolerance.
bool intermediate_verify_comparison(std::vector< Eigen::Vector3d > const &src_pattern, std::vector< Eigen::Vector3d > const &ref_pattern, Eigen::Matrix3d const &shift_rot_matrix, double max_dist_rad)
Test the input rotation matrix by comparing the rotated src pattern to the ref pattern.
int check_spoke(double cos_theta_src, double sin_theta_src, ndarray::Array< double, 1, 1 > const &ref_ctr, Eigen::Vector3d const &proj_ref_ctr_delta, double proj_ref_ctr_dist_sq, std::pair< size_t, size_t > const &candidate_range, std::vector< uint16_t > const &ref_id_array, ndarray::Array< double, 2, 1 > const &reference_array, double src_sin_tol)
Check the opening angle between the first spoke of our pattern for the source object against the refe...
PatternResult construct_pattern_and_shift_rot_matrix(ndarray::Array< double, 2, 1 > src_pattern_array, ndarray::Array< double, 2, 1 > src_delta_array, ndarray::Array< double, 1, 1 > src_dist_array, ndarray::Array< float, 1, 1 > dist_array, ndarray::Array< uint16_t, 2, 1 > id_array, ndarray::Array< double, 2, 1 > reference_array, size_t n_match, double max_cos_theta_shift, double max_cos_rot_sq, double max_dist_rad)
Test an input source pattern against the reference catalog.
Eigen::Matrix3d create_spherical_rotation_matrix(Eigen::Vector3d const &rot_axis, double cos_rotation, double sin_rotion)
Construct a generalized 3D rotation matrix about a given axis.
SortedArrayResult create_sorted_arrays(ndarray::Array< double, 1, 1 > const &ref_center, ndarray::Array< double, 2, 1 > const &reference_array)
Create arrays sorted on the distance between the center of this candidate reference object and all th...
std::pair< size_t, size_t > find_candidate_reference_pair_range(float src_dist, ndarray::Array< float, 1, 1 > const &ref_dist_array, double max_dist_rad)
Find the range of reference pairs within the distance tolerance of our source pair spoke.
ShiftRotMatrixResult create_shift_rot_matrix(double cos_rot_sq, Eigen::Matrix3d const &shift_matrix, ndarray::Array< double, 1, 1 > const &src_delta, ndarray::Array< double, 1, 1 > const &ref_ctr, ndarray::Array< double, 1, 1 > const &ref_delta)
Create the complete spherical rotation matrix.
std::vector< std::pair< size_t, size_t > > create_pattern_spokes(ndarray::Array< double, 1, 1 > const &src_ctr, ndarray::Array< double, 2, 1 > const &src_delta_array, ndarray::Array< double, 1, 1 > const &src_dist_array, ndarray::Array< double, 1, 1 > const &ref_ctr, Eigen::Vector3d const &proj_ref_ctr_delta, std::vector< float > const &ref_dist_array, std::vector< uint16_t > const &ref_id_array, ndarray::Array< double, 2, 1 > const &reference_array, double max_dist_rad, size_t n_match)
Create the individual spokes that make up the pattern now that the shift and rotation are within tole...
T push_back(T... args)
T reserve(T... args)
T size(T... args)
Result of construct_pattern_and_shift_rot_matrix(), containing the matched sources,...
Result of test_rotation(), containing the rotation matrix and success flag.
double cos_rot_sq
Magnitude of the rotation needed to align the two patterns after their centers are shifted on top of ...
Eigen::Matrix3d shift_matrix
Rotation matrix describing the shift needed to align the source and candidate reference center.
Result of create_shift_rot_matrix() containing the rotation matrix and rotation angle.
Eigen::Matrix3d shift_rot_matrix
Spherical rotation matrix.
double sin_rot
Rotation that makes up the matrix.
Result of create_sorted_arrays(), containing the sorted distances and array indexes.
std::vector< uint16_t > ids
Index locations of the pair with the given distance in the reference array.
std::vector< float > dists
Sorted distances between center of a candidate and all reference objects.
T upper_bound(T... args)