27#include "ndarray/eigen.h"
33int sgn(
double val) {
return (0.0 < val) - (val < 0.0); }
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) {
49 size_t ref_dist_idx = (candidate_range.first + candidate_range.second) / 2;
53 for (
size_t idx = 0; idx < candidate_range.second - candidate_range.first; idx++) {
56 ref_dist_idx = ref_dist_idx + idx;
58 ref_dist_idx = ref_dist_idx - idx;
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];
68 ndarray::Array<double, 1, 1> ref_center = reference_array[ref_id];
70 ndarray::asEigenMatrix(src_pattern_array[0]).dot(ndarray::asEigenMatrix(ref_center));
72 cos_shift = std::clamp(cos_shift, -1.0, 1.0);
73 if (cos_shift < max_cos_theta_shift) {
78 ndarray::Array<double, 1, 1> ref_delta;
80 if (ref_id == *tmp_ref_pair_list.begin()) {
82 ref_delta = copy(reference_array[tmp_ref_pair_list[1]] - ref_center);
85 ref_delta = copy(reference_array[tmp_ref_pair_list[0]] - ref_center);
91 test_rotation(src_pattern_array[0], ref_center, src_delta_array[0], ref_delta, cos_shift,
103 sorted_array_struct.
ids, reference_array, max_dist_rad, n_match);
105 if (pattern_spokes.
size() < n_match - 2) {
109 candidate_pairs.
reserve(candidate_pairs.
size() + pattern_spokes.
size());
110 candidate_pairs.
insert(candidate_pairs.
end(), pattern_spokes.
begin(), pattern_spokes.
end());
115 src_delta_array[0], ref_center, ref_delta);
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]);
142 ndarray::Array<double, 2, 1>
const& reference_array) {
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);
159 float src_dist, ndarray::Array<float, 1, 1>
const& ref_dist_array,
double max_dist_rad) {
161 std::lower_bound(ref_dist_array.begin(), ref_dist_array.end(), src_dist - max_dist_rad - 1e-16);
163 std::upper_bound(ref_dist_array.begin(), ref_dist_array.end(), src_dist + max_dist_rad + 1e-16);
165 size_t startIdx = itr - ref_dist_array.begin();
166 size_t endIdx = itrEnd - ref_dist_array.begin();
172 double max_dist_rad) {
178 size_t startIdx = itr - ref_dist_array.
begin();
179 size_t endIdx = itrEnd - ref_dist_array.
begin();
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) {
189 if (cos_shift > 1.0) {
191 }
else if (cos_shift < -1.0) {
194 double sin_shift = sqrt(1 - cos_shift * cos_shift);
198 auto ref_center_eigen = ndarray::asEigenMatrix(ref_center).head<3>();
199 Eigen::Matrix3d shift_matrix;
201 Eigen::Vector3d rot_axis = ndarray::asEigenMatrix(src_center).head<3>().cross(ref_center_eigen);
202 rot_axis /= sin_shift;
205 shift_matrix = Eigen::Matrix3d::Identity();
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);
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) {
226 double sin_rotation) {
227 Eigen::Matrix3d rot_cross_matrix;
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.;
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();
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));
249 double sin_rot = sgn(delta_dot_cross) * sqrt(1 - cos_rot_sq);
250 Eigen::Matrix3d rot_matrix =
253 Eigen::Matrix3d shift_rot_matrix = rot_matrix * shift_matrix;
256 result.sin_rot = sin_rot;
257 result.shift_rot_matrix = shift_rot_matrix;
263 Eigen::Matrix3d
const& shift_rot_matrix,
double max_dist_rad) {
264 double max_dist_sq = max_dist_rad * max_dist_rad;
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)) {
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,
285 double max_dist_rad,
size_t n_match) {
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);
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);
303 double proj_ref_ctr_dist_sq = proj_ref_ctr_delta.dot(proj_ref_ctr_delta);
307 double max_sin_tol = 0.0447;
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)) {
318 if (candidate_range.first == candidate_range.second) {
325 double src_sin_tol = max_dist_rad / (src_dist_array[src_idx] + max_dist_rad);
331 if (src_sin_tol > max_sin_tol) {
332 src_sin_tol = max_sin_tol;
337 double proj_src_delta_dot = ndarray::asEigenMatrix(src_delta_array[src_idx]).dot(src_ctr_eigen);
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);
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);
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);
363 if (output_spokes.
size() >= n_match - 2) {
367 return output_spokes;
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,
373 ndarray::Array<double, 2, 1>
const& reference_array,
double src_sin_tol) {
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++) {
381 midpoint = midpoint + idx;
383 midpoint = midpoint - idx;
386 uint16_t ref_id = ref_id_array[midpoint];
387 ndarray::Array<double, 1, 1> ref_delta = copy(reference_array[ref_id] - ref_ctr);
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);
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;
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);
405 cos_sq_comparison = (cos_theta_src - cos_theta_ref) * (cos_theta_src - cos_theta_ref) /
406 (src_sin_tol * src_sin_tol);
411 if (cos_sq_comparison > src_sin_tol * src_sin_tol) {
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));
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;
425 sin_comparison = (sin_theta_src - sin_theta_ref) / cos_theta_ref;
429 if (abs(sin_comparison) < src_sin_tol) {
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...
Result of construct_pattern_and_shift_rot_matrix(), containing the matched sources,...
Result of test_rotation(), containing the rotation matrix and success flag.
bool success
Was the algorithm successful?
Eigen::Vector3d proj_ref_ctr_delta
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.