#pragma once #include #include #include #include #include namespace cgv { namespace math { /// mean of point set template fvec mean(const std::vector >& P, T inv_n = T(1) / P.size()) { fvec mu(T(0)); for (unsigned i = 0; i < P.size(); ++i) mu += P[i]; mu *= inv_n; return mu; } /// svd wrapper template void svd(const fmat& A, fmat& U, fvec& D, fmat& V_t, bool ordering = true, int maxiter = 30) { mat _A(N, M, &A(0, 0)), _U, _V; diag_mat _D; svd(_A, _U, _D, _V, ordering, maxiter); U = fmat(&_U(0, 0)); cgv::type::uint32_type i, j; for (j = 0; j < M; ++j) { D(j) = _D(j); for (i = 0; i < M; ++i) V_t(j, i) = _V(i, j); } } //! compute rigid body transformation and optionally uniform scaling to align source point set to target point set in least squares sense /*! Given source points X and target points Y find orthogonal matrix O\in O(3), translation vector t\in R^3 and optionally scale factor s > 0 to minimize \sum_i ||y_i - (s*O*x_i + t)||^2 Optionally, one can enforce O to be a rotation. Algorithm taken from Umeyama, Shinji. "Least-squares estimation of transformation parameters between two point patterns." IEEE Transactions on pattern analysis and machine intelligence 13.4 (1991): 376-380. Second template argument allows to specify a different number type for svd computation */ template void align( const std::vector >& source_points, const std::vector >& target_points, // input point sets fmat& O, fvec& t, T* scale_ptr = nullptr, // output transformation bool allow_reflection = false) // configuration { // ensure that source_points and target_points have same number of points and that we have at least 3 point pairs assert(source_points.size() == target_points.size() && source_points.size() > 2); T inv_n = T(1) / source_points.size(); // compute mean points fvec mu_source = mean(source_points, inv_n); fvec mu_target = mean(target_points, inv_n); // compute covariance matrix and variances of point sets fmat Sigma(T(0)); T sigma_S = 0, sigma_T = 0; for (size_t i = 0; i < source_points.size(); ++i) { Sigma += fmat(target_points[i] - mu_target, source_points[i] - mu_source); sigma_S += sqr_length(source_points[i] - mu_source); sigma_T += sqr_length(target_points[i] - mu_target); } Sigma *= inv_n; sigma_S *= inv_n; sigma_T *= inv_n; // compute SVD of covariance matrix fvec D; fmat U, V_t; svd(fmat(Sigma), U, D, V_t); // account for reflections fmat S; S.identity(); if (!allow_reflection && det(mat(3,3,&Sigma(0,0))) < 0) S(2, 2) = T_SVD(-1); // compute results O = fmat(U*S*V_t); if (scale_ptr) { *scale_ptr = T(D(0) + D(1) + S(2, 2)*D(2)) / sigma_S; t = mu_target - *scale_ptr * O * mu_source; } else t = mu_target - O*mu_source; } } }