27#include "ndarray/eigen.h"
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));
71 if (cos_shift < max_cos_theta_shift) {
76 ndarray::Array<double, 1, 1> ref_delta;
78 if (ref_id == *tmp_ref_pair_list.begin()) {
80 ref_delta = copy(reference_array[tmp_ref_pair_list[1]] - ref_center);
83 ref_delta = copy(reference_array[tmp_ref_pair_list[0]] - ref_center);
89 test_rotation(src_pattern_array[0], ref_center, src_delta_array[0], ref_delta, cos_shift,
101 sorted_array_struct.
ids, reference_array, max_dist_rad, n_match);
103 if (pattern_spokes.
size() < n_match - 2) {
107 candidate_pairs.
reserve(candidate_pairs.
size() + pattern_spokes.
size());
108 candidate_pairs.
insert(candidate_pairs.
end(), pattern_spokes.
begin(), pattern_spokes.
end());
113 src_delta_array[0], ref_center, ref_delta);
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]);
140 ndarray::Array<double, 2, 1>
const& reference_array) {
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));
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);
157 float src_dist, ndarray::Array<float, 1, 1>
const& ref_dist_array,
double max_dist_rad) {
159 std::lower_bound(ref_dist_array.begin(), ref_dist_array.end(), src_dist - max_dist_rad - 1e-16);
161 std::upper_bound(ref_dist_array.begin(), ref_dist_array.end(), src_dist + max_dist_rad + 1e-16);
163 size_t startIdx = itr - ref_dist_array.begin();
164 size_t endIdx = itrEnd - ref_dist_array.begin();
170 double max_dist_rad) {
176 size_t startIdx = itr - ref_dist_array.
begin();
177 size_t endIdx = itrEnd - ref_dist_array.
begin();
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) {
187 if (cos_shift > 1.0) {
189 }
else if (cos_shift < -1.0) {
192 double sin_shift = sqrt(1 - cos_shift * cos_shift);
196 auto ref_center_eigen = ndarray::asEigenMatrix(ref_center).head<3>();
197 Eigen::Matrix3d shift_matrix;
199 Eigen::Vector3d rot_axis = ndarray::asEigenMatrix(src_center).head<3>().cross(ref_center_eigen);
200 rot_axis /= sin_shift;
203 shift_matrix = Eigen::Matrix3d::Identity();
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);
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) {
224 double sin_rotation) {
225 Eigen::Matrix3d rot_cross_matrix;
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.;
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();
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));
247 double sin_rot = sgn(delta_dot_cross) * sqrt(1 - cos_rot_sq);
248 Eigen::Matrix3d rot_matrix =
251 Eigen::Matrix3d shift_rot_matrix = rot_matrix * shift_matrix;
255 result.shift_rot_matrix = shift_rot_matrix;
261 Eigen::Matrix3d
const& shift_rot_matrix,
double max_dist_rad) {
262 double max_dist_sq = max_dist_rad * max_dist_rad;
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)) {
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,
283 double max_dist_rad,
size_t n_match) {
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);
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);
301 double proj_ref_ctr_dist_sq = proj_ref_ctr_delta.dot(proj_ref_ctr_delta);
305 double max_sin_tol = 0.0447;
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)) {
316 if (candidate_range.first == candidate_range.second) {
323 double src_sin_tol = max_dist_rad / (src_dist_array[src_idx] + max_dist_rad);
329 if (src_sin_tol > max_sin_tol) {
330 src_sin_tol = max_sin_tol;
335 double proj_src_delta_dot = ndarray::asEigenMatrix(src_delta_array[src_idx]).dot(src_ctr_eigen);
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);
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);
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);
361 if (output_spokes.
size() >= n_match - 2) {
365 return output_spokes;
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,
371 ndarray::Array<double, 2, 1>
const& reference_array,
double src_sin_tol) {
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++) {
379 midpoint = midpoint + idx;
381 midpoint = midpoint - idx;
384 uint16_t ref_id = ref_id_array[midpoint];
385 ndarray::Array<double, 1, 1> ref_delta = copy(reference_array[ref_id] - ref_ctr);
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);
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;
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);
403 cos_sq_comparison = (cos_theta_src - cos_theta_ref) * (cos_theta_src - cos_theta_ref) /
404 (src_sin_tol * src_sin_tol);
409 if (cos_sq_comparison > src_sin_tol * src_sin_tol) {
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));
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;
423 sin_comparison = (sin_theta_src - sin_theta_ref) / cos_theta_ref;
427 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.