LSST Applications g180d380827+6621f76652,g2079a07aa2+86d27d4dc4,g2305ad1205+f5a9e323a1,g2bbee38e9b+c6a8a0fb72,g337abbeb29+c6a8a0fb72,g33d1c0ed96+c6a8a0fb72,g3a166c0a6a+c6a8a0fb72,g3ddfee87b4+9a10e1fe7b,g48712c4677+c9a099281a,g487adcacf7+f2e03ea30b,g50ff169b8f+96c6868917,g52b1c1532d+585e252eca,g591dd9f2cf+aead732c78,g64a986408d+eddffb812c,g858d7b2824+eddffb812c,g864b0138d7+aa38e45daa,g974c55ee3d+f37bf00e57,g99cad8db69+119519a52d,g9c22b2923f+e2510deafe,g9ddcbc5298+9a081db1e4,ga1e77700b3+03d07e1c1f,gb0e22166c9+60f28cb32d,gb23b769143+eddffb812c,gba4ed39666+c2a2e4ac27,gbb8dafda3b+27317ec8e9,gbd998247f1+585e252eca,gc120e1dc64+5817c176a8,gc28159a63d+c6a8a0fb72,gc3e9b769f7+6707aea8b4,gcf0d15dbbd+9a10e1fe7b,gdaeeff99f8+f9a426f77a,ge6526c86ff+6a2e01d432,ge79ae78c31+c6a8a0fb72,gee10cc3b42+585e252eca,gff1a9f87cc+eddffb812c,v27.0.0.rc1
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 if (cos_shift < max_cos_theta_shift) {
72 continue;
73 }
74 // We can now append this one as a candidate.
75 candidate_pairs.push_back(std::make_pair(ref_id, 0));
76 ndarray::Array<double, 1, 1> ref_delta;
77 // Test to see which reference object to use in the pair.
78 if (ref_id == *tmp_ref_pair_list.begin()) {
79 candidate_pairs.push_back(std::make_pair(tmp_ref_pair_list[1], 1));
80 ref_delta = copy(reference_array[tmp_ref_pair_list[1]] - ref_center);
81 } else {
82 candidate_pairs.push_back(std::make_pair(tmp_ref_pair_list[0], 1));
83 ref_delta = copy(reference_array[tmp_ref_pair_list[0]] - ref_center);
84 }
85 // For dense fields it will be faster to compute the absolute rotation this pair suggests first
86 // rather than saving it after all the spokes are found. We then compute the cos^2 of the rotation
87 // and first part of the rotation matrix from source to reference frame.
88 RotationTestResult test_rot_result =
89 test_rotation(src_pattern_array[0], ref_center, src_delta_array[0], ref_delta, cos_shift,
90 max_cos_rot_sq);
91 if (!test_rot_result.success) {
92 continue;
93 }
94 // Now that we have a candidate first spoke and reference pattern center, we mask our future
95 // search to only those pairs that contain our candidate reference center.
96 SortedArrayResult sorted_array_struct = create_sorted_arrays(ref_center, reference_array);
97 // Now we feed this sub data to match the spokes of our pattern.
99 create_pattern_spokes(src_pattern_array[0], src_delta_array, src_dist_array, ref_center,
100 test_rot_result.proj_ref_ctr_delta, sorted_array_struct.dists,
101 sorted_array_struct.ids, reference_array, max_dist_rad, n_match);
102 // If we don't find enough candidates we can continue to the next reference center pair.
103 if (pattern_spokes.size() < n_match - 2) {
104 continue;
105 }
106 // If we have the right number of matched ids we store these.
107 candidate_pairs.reserve(candidate_pairs.size() + pattern_spokes.size());
108 candidate_pairs.insert(candidate_pairs.end(), pattern_spokes.begin(), pattern_spokes.end());
109 // We can now create our full matrix for both the shift and rotation. The shift aligns
110 // the pattern centers, while the rotation rotates the spokes on top of each other.
111 ShiftRotMatrixResult shift_rot_result =
112 create_shift_rot_matrix(test_rot_result.cos_rot_sq, test_rot_result.shift_matrix,
113 src_delta_array[0], ref_center, ref_delta);
114
115 // concatenate our final patterns.
116 Eigen::Matrix3d shift_rot_matrix = shift_rot_result.shift_rot_matrix;
119 ref_pattern.reserve(n_match);
120 src_pattern.reserve(n_match);
121 for (size_t idx = 0; idx < n_match; idx++) {
122 ref_pattern[idx] = ndarray::asEigenMatrix(reference_array[candidate_pairs[idx].first]);
123 src_pattern[idx] = ndarray::asEigenMatrix(src_pattern_array[candidate_pairs[idx].second]);
124 }
125 // TODO: DM-32985 Implement least squares linear fitting here. Look at python example linked in
126 // ticket for how to implement.
127 // Test that the all points in each pattern are within tolerance after shift/rotation.
128 bool passed =
129 intermediate_verify_comparison(src_pattern, ref_pattern, shift_rot_matrix, max_dist_rad);
130 if (passed) {
131 return PatternResult(candidate_pairs, shift_rot_matrix, cos_shift, shift_rot_result.sin_rot);
132 }
133 }
134 }
135 // failed fit
136 return PatternResult();
137}
138
139SortedArrayResult create_sorted_arrays(ndarray::Array<double, 1, 1> const& ref_center,
140 ndarray::Array<double, 2, 1> const& reference_array) {
142 // NOTE: this algorithm is quadratic in the length of reference_array. It might be worth using std::sort
143 // instead of this approach, if reference_array is long, but the length at which that matters should
144 // be tested because it would require caching of distances.
145 for (uint16_t idx = 0; idx < reference_array.getShape()[0]; idx++) {
146 Eigen::Vector3d diff = ndarray::asEigenMatrix(copy(reference_array[idx] - ref_center));
147 double dist = sqrt(diff.dot(diff));
148 auto dists_itr = std::lower_bound(result.dists.begin(), result.dists.end(), dist);
149 auto ids_itr = result.ids.begin() + (dists_itr - result.dists.begin());
150 result.ids.insert(ids_itr, idx);
151 result.dists.insert(dists_itr, dist);
152 }
153 return result;
154}
155
157 float src_dist, ndarray::Array<float, 1, 1> const& ref_dist_array, double max_dist_rad) {
158 auto itr =
159 std::lower_bound(ref_dist_array.begin(), ref_dist_array.end(), src_dist - max_dist_rad - 1e-16);
160 auto itrEnd =
161 std::upper_bound(ref_dist_array.begin(), ref_dist_array.end(), src_dist + max_dist_rad + 1e-16);
162
163 size_t startIdx = itr - ref_dist_array.begin();
164 size_t endIdx = itrEnd - ref_dist_array.begin();
165 return std::make_pair(startIdx, endIdx);
166}
167
169 std::vector<float> const& ref_dist_array,
170 double max_dist_rad) {
171 auto itr =
172 std::lower_bound(ref_dist_array.begin(), ref_dist_array.end(), src_dist - max_dist_rad - 1e-16);
173 auto itrEnd =
174 std::upper_bound(ref_dist_array.begin(), ref_dist_array.end(), src_dist + max_dist_rad + 1e-16);
175
176 size_t startIdx = itr - ref_dist_array.begin();
177 size_t endIdx = itrEnd - ref_dist_array.begin();
178 return std::make_pair(startIdx, endIdx);
179}
180
181RotationTestResult test_rotation(ndarray::Array<double, 1, 1> const& src_center,
182 ndarray::Array<double, 1, 1> const& ref_center,
183 ndarray::Array<double, 1, 1> const& src_delta,
184 ndarray::Array<double, 1, 1> const& ref_delta, double cos_shift,
185 double max_cos_rot_sq) {
186 // Make sure the sine is a real number.
187 if (cos_shift > 1.0) {
188 cos_shift = 1;
189 } else if (cos_shift < -1.0) {
190 cos_shift = -1;
191 }
192 double sin_shift = sqrt(1 - cos_shift * cos_shift);
193
194 // If the sine of our shift is zero we only need to use the identity matrix for the shift. Else we
195 // construct the rotation matrix for shift.
196 auto ref_center_eigen = ndarray::asEigenMatrix(ref_center).head<3>();
197 Eigen::Matrix3d shift_matrix;
198 if (sin_shift > 0) {
199 Eigen::Vector3d rot_axis = ndarray::asEigenMatrix(src_center).head<3>().cross(ref_center_eigen);
200 rot_axis /= sin_shift;
201 shift_matrix = create_spherical_rotation_matrix(rot_axis, cos_shift, sin_shift);
202 } else {
203 shift_matrix = Eigen::Matrix3d::Identity();
204 }
205
206 // Now that we have our shift we apply it to the src delta vector and check the rotation.
207 Eigen::Vector3d rot_src_delta = shift_matrix * ndarray::asEigenMatrix(src_delta);
208 Eigen::Vector3d proj_src_delta = rot_src_delta - rot_src_delta.dot(ref_center_eigen) * ref_center_eigen;
209 Eigen::Vector3d ref_delta_eigen = ndarray::asEigenMatrix(ref_delta);
210
211 Eigen::Vector3d proj_ref_delta =
212 ref_delta_eigen - ref_delta_eigen.dot(ref_center_eigen) * ref_center_eigen;
213 double proj_src_delta_sq = proj_src_delta.dot(proj_ref_delta);
214 double cos_rot_sq = proj_src_delta_sq * proj_src_delta_sq /
215 (proj_src_delta.dot(proj_src_delta) * proj_ref_delta.dot(proj_ref_delta));
216 if (cos_rot_sq < max_cos_rot_sq) {
217 // Return failure if the rotation isn't within tolerance.
218 return RotationTestResult();
219 }
220 return RotationTestResult(cos_rot_sq, proj_ref_delta, shift_matrix);
221}
222
223Eigen::Matrix3d create_spherical_rotation_matrix(Eigen::Vector3d const& rot_axis, double cos_rotation,
224 double sin_rotation) {
225 Eigen::Matrix3d rot_cross_matrix;
226 // clang-format off
227 rot_cross_matrix << 0., -rot_axis[2], rot_axis[1],
228 rot_axis[2], 0., -rot_axis[0],
229 -rot_axis[1], rot_axis[0], 0.;
230 // clang-format on
231 Eigen::Matrix3d shift_matrix = cos_rotation * Eigen::Matrix3d::Identity() +
232 sin_rotation * rot_cross_matrix +
233 (1 - cos_rotation) * rot_axis * rot_axis.transpose();
234 return shift_matrix;
235}
236
237ShiftRotMatrixResult create_shift_rot_matrix(double cos_rot_sq, Eigen::Matrix3d const& shift_matrix,
238 ndarray::Array<double, 1, 1> const& src_delta,
239 ndarray::Array<double, 1, 1> const& ref_ctr,
240 ndarray::Array<double, 1, 1> const& ref_delta) {
241 double cos_rot = sqrt(cos_rot_sq);
242 Eigen::Vector3d src_delta_eigen = ndarray::asEigenMatrix(src_delta);
243 Eigen::Vector3d rot_src_delta = shift_matrix * src_delta_eigen;
244 Eigen::Vector3d tmp_cross = rot_src_delta.cross(ndarray::asEigenMatrix(ref_delta).head<3>());
245 double delta_dot_cross = tmp_cross.dot(ndarray::asEigenMatrix(ref_ctr));
246
247 double sin_rot = sgn(delta_dot_cross) * sqrt(1 - cos_rot_sq);
248 Eigen::Matrix3d rot_matrix =
249 create_spherical_rotation_matrix(ndarray::asEigenMatrix(ref_ctr), cos_rot, sin_rot);
250
251 Eigen::Matrix3d shift_rot_matrix = rot_matrix * shift_matrix;
252
254 result.sin_rot = sin_rot;
255 result.shift_rot_matrix = shift_rot_matrix;
256 return result;
257}
258
260 std::vector<Eigen::Vector3d> const& ref_pattern,
261 Eigen::Matrix3d const& shift_rot_matrix, double max_dist_rad) {
262 double max_dist_sq = max_dist_rad * max_dist_rad;
263 bool passed = true;
264 auto iSrc = src_pattern.begin();
265 auto iRef = ref_pattern.begin();
266 for (auto iSrc = src_pattern.begin(), iRef = ref_pattern.begin();
267 iSrc != src_pattern.end() && iRef != ref_pattern.end(); iSrc++, iRef++) {
268 Eigen::Vector3d rot_src_vect = shift_rot_matrix * *iSrc;
269 Eigen::Vector3d diff_vect = rot_src_vect - *iRef;
270 if (max_dist_sq < diff_vect.dot(diff_vect)) {
271 passed = false;
272 break;
273 }
274 }
275 return passed;
276}
277
279 ndarray::Array<double, 1, 1> const& src_ctr, ndarray::Array<double, 2, 1> const& src_delta_array,
280 ndarray::Array<double, 1, 1> const& src_dist_array, ndarray::Array<double, 1, 1> const& ref_ctr,
281 Eigen::Vector3d const& proj_ref_ctr_delta, std::vector<float> const& ref_dist_array,
282 std::vector<uint16_t> const& ref_id_array, ndarray::Array<double, 2, 1> const& reference_array,
283 double max_dist_rad, size_t n_match) {
284 // Struct where we will be putting our results.
286
287 // Counter for number of spokes we failed to find a reference
288 // candidate for. We break the loop if we haven't found enough.
289 size_t n_fail = 0;
290
291 // Plane project the center/first spoke of the source pattern using
292 // the center vector of the pattern as normal.
293 auto src_ctr_eigen = ndarray::asEigenMatrix(src_ctr);
294 double src_delta_ctr_dot = ndarray::asEigenMatrix(src_delta_array[0]).dot(src_ctr_eigen);
295
296 ndarray::Array<double, 1, 1> proj_src_ctr_delta = copy(src_delta_array[0] - src_delta_ctr_dot * src_ctr);
297 auto proj_src_ctr_delta_eigen = ndarray::asEigenMatrix(proj_src_ctr_delta);
298 double proj_src_ctr_dist_sq = proj_src_ctr_delta_eigen.dot(proj_src_ctr_delta_eigen);
299
300 // Pre - compute the squared length of the projected reference vector.
301 double proj_ref_ctr_dist_sq = proj_ref_ctr_delta.dot(proj_ref_ctr_delta);
302
303 // Value of sin where sin(theta) ~= theta to within 0.1%. Used to make
304 // sure we are still in small angle approximation.
305 double max_sin_tol = 0.0447;
306 // Loop over the source pairs.
307 for (size_t src_idx = 1; src_idx < src_dist_array.size(); src_idx++) {
308 if (n_fail > src_dist_array.size() - (n_match - 1)) {
309 break;
310 }
311 // Find the reference pairs that include our candidate pattern center
312 // and sort them in increasing delta. Check this first so we don't
313 // compute anything else if no candidates exist.
314 std::pair<size_t, size_t> candidate_range =
315 find_candidate_reference_pair_range(src_dist_array[src_idx], ref_dist_array, max_dist_rad);
316 if (candidate_range.first == candidate_range.second) {
317 n_fail++;
318 continue;
319 }
320
321 // Given our length tolerance we can use it to compute a tolerance
322 // on the angle between our spoke.
323 double src_sin_tol = max_dist_rad / (src_dist_array[src_idx] + max_dist_rad);
324
325 // Test if the small angle approximation will still hold. This is
326 // defined as when sin(theta) ~= theta to within 0.1% of each
327 // other. If the implied opening angle is too large we set it to
328 // the 0.1% threshold.
329 if (src_sin_tol > max_sin_tol) {
330 src_sin_tol = max_sin_tol;
331 }
332
333 // Plane project the candidate source spoke and compute the cosine
334 // and sine of the opening angle.
335 double proj_src_delta_dot = ndarray::asEigenMatrix(src_delta_array[src_idx]).dot(src_ctr_eigen);
336
337 ndarray::Array<double, 1, 1> proj_src_delta =
338 copy(src_delta_array[src_idx] - proj_src_delta_dot * src_ctr);
339 auto proj_src_delta_eigen = ndarray::asEigenMatrix(proj_src_delta);
340 double geom_dist_src = sqrt(proj_src_delta_eigen.dot(proj_src_delta_eigen) * proj_src_ctr_dist_sq);
341
342 // Compute cosine and sine of the delta vector opening angle.
343 double cos_theta_src = proj_src_delta_eigen.dot(proj_src_ctr_delta_eigen) / geom_dist_src;
344 Eigen::Vector3d cross_src =
345 proj_src_delta_eigen.head<3>().cross(proj_src_ctr_delta_eigen.head<3>()) / geom_dist_src;
346 double sin_theta_src = cross_src.dot(src_ctr_eigen);
347
348 // Test the spokes and return the id of the reference object.
349 // Return -1 if no match is found.
350 int ref_id =
351 check_spoke(cos_theta_src, sin_theta_src, ref_ctr, proj_ref_ctr_delta, proj_ref_ctr_dist_sq,
352 candidate_range, ref_id_array, reference_array, src_sin_tol);
353 if (ref_id < 0) {
354 n_fail++;
355 continue;
356 }
357
360 output_spokes.push_back(std::make_pair(static_cast<size_t>(ref_id), src_idx + 1));
361 if (output_spokes.size() >= n_match - 2) {
362 break;
363 }
364 }
365 return output_spokes;
366}
367
368int check_spoke(double cos_theta_src, double sin_theta_src, ndarray::Array<double, 1, 1> const& ref_ctr,
369 Eigen::Vector3d const& proj_ref_ctr_delta, double proj_ref_ctr_dist_sq,
370 std::pair<size_t, size_t> const& candidate_range, std::vector<uint16_t> const& ref_id_array,
371 ndarray::Array<double, 2, 1> const& reference_array, double src_sin_tol) {
372 // Loop over our candidate reference objects. candidate_range is the min
373 // and max of for pair candidates and are view into ref_id_array. Here we
374 // start from the midpoint of min and max values and step outward.
375 size_t midpoint = (candidate_range.first + candidate_range.second) / 2;
376 for (size_t idx = 0; idx < candidate_range.second - candidate_range.first; idx++) {
377 // TODO DM-33514: cleanup this loop to use an iterator that handles the "inside-out" iteration.
378 if (idx % 2 == 0) {
379 midpoint = midpoint + idx;
380 } else {
381 midpoint = midpoint - idx;
382 }
383 // Compute the delta vector from the pattern center.
384 uint16_t ref_id = ref_id_array[midpoint];
385 ndarray::Array<double, 1, 1> ref_delta = copy(reference_array[ref_id] - ref_ctr);
386
387 double ref_dot = ndarray::asEigenMatrix(ref_delta).dot(ndarray::asEigenMatrix(ref_ctr));
388 ndarray::Array<double, 1, 1> proj_ref_delta = copy(ref_delta - ref_dot * ref_ctr);
389 // Compute the cos between our "center" reference vector and the
390 // current reference candidate.
391 auto proj_ref_delta_eigen = ndarray::asEigenMatrix(proj_ref_delta);
392 double proj_delta_dist_sq = proj_ref_delta_eigen.dot(proj_ref_delta_eigen);
393 double geom_dist_ref = sqrt(proj_ref_ctr_dist_sq * proj_delta_dist_sq);
394 double cos_theta_ref = proj_ref_delta_eigen.dot(proj_ref_ctr_delta) / geom_dist_ref;
395
396 // Make sure we can safely make the comparison in case
397 // our "center" and candidate vectors are mostly aligned.
398 double cos_sq_comparison;
399 if (cos_theta_ref * cos_theta_ref < (1 - src_sin_tol * src_sin_tol)) {
400 cos_sq_comparison = (cos_theta_src - cos_theta_ref) * (cos_theta_src - cos_theta_ref) /
401 (1 - cos_theta_ref * cos_theta_ref);
402 } else {
403 cos_sq_comparison = (cos_theta_src - cos_theta_ref) * (cos_theta_src - cos_theta_ref) /
404 (src_sin_tol * src_sin_tol);
405 }
406 // Test the difference of the cosine of the reference angle against
407 // the source angle. Assumes that the delta between the two is
408 // small.
409 if (cos_sq_comparison > src_sin_tol * src_sin_tol) {
410 continue;
411 }
412 // The cosine tests the magnitude of the angle but not
413 // its direction. To do that we need to know the sine as well.
414 // This cross product calculation does that.
415 Eigen::Vector3d cross_ref = proj_ref_delta_eigen.head<3>().cross(proj_ref_ctr_delta) / geom_dist_ref;
416 double sin_theta_ref = cross_ref.dot(ndarray::asEigenMatrix(ref_ctr));
417 // Check the value of the cos again to make sure that it is not
418 // near zero.
419 double sin_comparison;
420 if (abs(cos_theta_src) < src_sin_tol) {
421 sin_comparison = (sin_theta_src - sin_theta_ref) / src_sin_tol;
422 } else {
423 sin_comparison = (sin_theta_src - sin_theta_ref) / cos_theta_ref;
424 }
425
426 // Return the correct id of the candidate we found.
427 if (abs(sin_comparison) < src_sin_tol) {
428 return ref_id;
429 }
430 }
431 return -1;
432}
433
434} // namespace astrom
435} // namespace meas
436} // namespace lsst
py::object result
Definition _schema.cc:429
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)
ImageT val
Definition CR.cc:146
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)