1 // This file is part of Eigen, a lightweight C++ template library 2 // for linear algebra. 3 // 4 // Copyright (C) 2009 Hauke Heibel <hauke.heibel@gmail.com> 5 // 6 // This Source Code Form is subject to the terms of the Mozilla 7 // Public License v. 2.0. If a copy of the MPL was not distributed 8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 10 #ifndef EIGEN_UMEYAMA_H 11 #define EIGEN_UMEYAMA_H 12 13 // This file requires the user to include 14 // * Eigen/Core 15 // * Eigen/LU 16 // * Eigen/SVD 17 // * Eigen/Array 18 19 namespace Eigen { 20 21 #ifndef EIGEN_PARSED_BY_DOXYGEN 22 23 // These helpers are required since it allows to use mixed types as parameters 24 // for the Umeyama. The problem with mixed parameters is that the return type 25 // cannot trivially be deduced when float and double types are mixed. 26 namespace internal { 27 28 // Compile time return type deduction for different MatrixBase types. 29 // Different means here different alignment and parameters but the same underlying 30 // real scalar type. 31 template<typename MatrixType, typename OtherMatrixType> 32 struct umeyama_transform_matrix_type 33 { 34 enum { 35 MinRowsAtCompileTime = EIGEN_SIZE_MIN_PREFER_DYNAMIC(MatrixType::RowsAtCompileTime, OtherMatrixType::RowsAtCompileTime), 36 37 // When possible we want to choose some small fixed size value since the result 38 // is likely to fit on the stack. So here, EIGEN_SIZE_MIN_PREFER_DYNAMIC is not what we want. 39 HomogeneousDimension = int(MinRowsAtCompileTime) == Dynamic ? Dynamic : int(MinRowsAtCompileTime)+1 40 }; 41 42 typedef Matrix<typename traits<MatrixType>::Scalar, 43 HomogeneousDimension, 44 HomogeneousDimension, 45 AutoAlign | (traits<MatrixType>::Flags & RowMajorBit ? RowMajor : ColMajor), 46 HomogeneousDimension, 47 HomogeneousDimension 48 > type; 49 }; 50 51 } 52 53 #endif 54 55 /** 56 * \geometry_module \ingroup Geometry_Module 57 * 58 * \brief Returns the transformation between two point sets. 59 * 60 * The algorithm is based on: 61 * "Least-squares estimation of transformation parameters between two point patterns", 62 * Shinji Umeyama, PAMI 1991, DOI: 10.1109/34.88573 63 * 64 * It estimates parameters \f$ c, \mathbf{R}, \f$ and \f$ \mathbf{t} \f$ such that 65 * \f{align*} 66 * \frac{1}{n} \sum_{i=1}^n \vert\vert y_i - (c\mathbf{R}x_i + \mathbf{t}) \vert\vert_2^2 67 * \f} 68 * is minimized. 69 * 70 * The algorithm is based on the analysis of the covariance matrix 71 * \f$ \Sigma_{\mathbf{x}\mathbf{y}} \in \mathbb{R}^{d \times d} \f$ 72 * of the input point sets \f$ \mathbf{x} \f$ and \f$ \mathbf{y} \f$ where 73 * \f$d\f$ is corresponding to the dimension (which is typically small). 74 * The analysis is involving the SVD having a complexity of \f$O(d^3)\f$ 75 * though the actual computational effort lies in the covariance 76 * matrix computation which has an asymptotic lower bound of \f$O(dm)\f$ when 77 * the input point sets have dimension \f$d \times m\f$. 78 * 79 * Currently the method is working only for floating point matrices. 80 * 81 * \todo Should the return type of umeyama() become a Transform? 82 * 83 * \param src Source points \f$ \mathbf{x} = \left( x_1, \hdots, x_n \right) \f$. 84 * \param dst Destination points \f$ \mathbf{y} = \left( y_1, \hdots, y_n \right) \f$. 85 * \param with_scaling Sets \f$ c=1 \f$ when <code>false</code> is passed. 86 * \return The homogeneous transformation 87 * \f{align*} 88 * T = \begin{bmatrix} c\mathbf{R} & \mathbf{t} \\ \mathbf{0} & 1 \end{bmatrix} 89 * \f} 90 * minimizing the resudiual above. This transformation is always returned as an 91 * Eigen::Matrix. 92 */ 93 template <typename Derived, typename OtherDerived> 94 typename internal::umeyama_transform_matrix_type<Derived, OtherDerived>::type 95 umeyama(const MatrixBase<Derived>& src, const MatrixBase<OtherDerived>& dst, bool with_scaling = true) 96 { 97 typedef typename internal::umeyama_transform_matrix_type<Derived, OtherDerived>::type TransformationMatrixType; 98 typedef typename internal::traits<TransformationMatrixType>::Scalar Scalar; 99 typedef typename NumTraits<Scalar>::Real RealScalar; 100 typedef typename Derived::Index Index; 101 102 EIGEN_STATIC_ASSERT(!NumTraits<Scalar>::IsComplex, NUMERIC_TYPE_MUST_BE_REAL) 103 EIGEN_STATIC_ASSERT((internal::is_same<Scalar, typename internal::traits<OtherDerived>::Scalar>::value), 104 YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY) 105 106 enum { Dimension = EIGEN_SIZE_MIN_PREFER_DYNAMIC(Derived::RowsAtCompileTime, OtherDerived::RowsAtCompileTime) }; 107 108 typedef Matrix<Scalar, Dimension, 1> VectorType; 109 typedef Matrix<Scalar, Dimension, Dimension> MatrixType; 110 typedef typename internal::plain_matrix_type_row_major<Derived>::type RowMajorMatrixType; 111 112 const Index m = src.rows(); // dimension 113 const Index n = src.cols(); // number of measurements 114 115 // required for demeaning ... 116 const RealScalar one_over_n = RealScalar(1) / static_cast<RealScalar>(n); 117 118 // computation of mean 119 const VectorType src_mean = src.rowwise().sum() * one_over_n; 120 const VectorType dst_mean = dst.rowwise().sum() * one_over_n; 121 122 // demeaning of src and dst points 123 const RowMajorMatrixType src_demean = src.colwise() - src_mean; 124 const RowMajorMatrixType dst_demean = dst.colwise() - dst_mean; 125 126 // Eq. (36)-(37) 127 const Scalar src_var = src_demean.rowwise().squaredNorm().sum() * one_over_n; 128 129 // Eq. (38) 130 const MatrixType sigma = one_over_n * dst_demean * src_demean.transpose(); 131 132 JacobiSVD<MatrixType> svd(sigma, ComputeFullU | ComputeFullV); 133 134 // Initialize the resulting transformation with an identity matrix... 135 TransformationMatrixType Rt = TransformationMatrixType::Identity(m+1,m+1); 136 137 // Eq. (39) 138 VectorType S = VectorType::Ones(m); 139 if (sigma.determinant()<Scalar(0)) S(m-1) = Scalar(-1); 140 141 // Eq. (40) and (43) 142 const VectorType& d = svd.singularValues(); 143 Index rank = 0; for (Index i=0; i<m; ++i) if (!internal::isMuchSmallerThan(d.coeff(i),d.coeff(0))) ++rank; 144 if (rank == m-1) { 145 if ( svd.matrixU().determinant() * svd.matrixV().determinant() > Scalar(0) ) { 146 Rt.block(0,0,m,m).noalias() = svd.matrixU()*svd.matrixV().transpose(); 147 } else { 148 const Scalar s = S(m-1); S(m-1) = Scalar(-1); 149 Rt.block(0,0,m,m).noalias() = svd.matrixU() * S.asDiagonal() * svd.matrixV().transpose(); 150 S(m-1) = s; 151 } 152 } else { 153 Rt.block(0,0,m,m).noalias() = svd.matrixU() * S.asDiagonal() * svd.matrixV().transpose(); 154 } 155 156 if (with_scaling) 157 { 158 // Eq. (42) 159 const Scalar c = Scalar(1)/src_var * svd.singularValues().dot(S); 160 161 // Eq. (41) 162 Rt.col(m).head(m) = dst_mean; 163 Rt.col(m).head(m).noalias() -= c*Rt.topLeftCorner(m,m)*src_mean; 164 Rt.block(0,0,m,m) *= c; 165 } 166 else 167 { 168 Rt.col(m).head(m) = dst_mean; 169 Rt.col(m).head(m).noalias() -= Rt.topLeftCorner(m,m)*src_mean; 170 } 171 172 return Rt; 173 } 174 175 } // end namespace Eigen 176 177 #endif // EIGEN_UMEYAMA_H 178