00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include <boost/unordered_map.hpp>
00039 #include <pcl/registration/exceptions.h>
00040
00042 template <typename PointSource, typename PointTarget>
00043 template<typename PointT> void
00044 pcl::GeneralizedIterativeClosestPoint<PointSource, PointTarget>::computeCovariances(typename pcl::PointCloud<PointT>::ConstPtr cloud,
00045 const typename pcl::KdTree<PointT>::Ptr kdtree,
00046 std::vector<Eigen::Matrix3d>& cloud_covariances)
00047 {
00048 if (k_correspondences_ > int (cloud->size ()))
00049 {
00050 PCL_ERROR ("[pcl::GeneralizedIterativeClosestPoint::computeCovariances] Number or points in cloud (%zu) is less than k_correspondences_ (%zu)!\n", cloud->size (), k_correspondences_);
00051 return;
00052 }
00053
00054 Eigen::Vector3d mean;
00055 std::vector<int> nn_indecies; nn_indecies.reserve (k_correspondences_);
00056 std::vector<float> nn_dist_sq; nn_dist_sq.reserve (k_correspondences_);
00057
00058
00059 if(cloud_covariances.size () < cloud->size ())
00060 cloud_covariances.resize (cloud->size ());
00061
00062 typename pcl::PointCloud<PointT>::const_iterator points_iterator = cloud->begin ();
00063 std::vector<Eigen::Matrix3d>::iterator matrices_iterator = cloud_covariances.begin ();
00064 for(;
00065 points_iterator != cloud->end ();
00066 ++points_iterator, ++matrices_iterator)
00067 {
00068 const PointT &query_point = *points_iterator;
00069 Eigen::Matrix3d &cov = *matrices_iterator;
00070
00071 cov.setZero ();
00072 mean.setZero ();
00073
00074
00075 kdtree->nearestKSearch(query_point, k_correspondences_, nn_indecies, nn_dist_sq);
00076
00077
00078 for(int j = 0; j < k_correspondences_; j++) {
00079 const PointT &pt = (*cloud)[nn_indecies[j]];
00080
00081 mean[0] += pt.x;
00082 mean[1] += pt.y;
00083 mean[2] += pt.z;
00084
00085 cov(0,0) += pt.x*pt.x;
00086
00087 cov(1,0) += pt.y*pt.x;
00088 cov(1,1) += pt.y*pt.y;
00089
00090 cov(2,0) += pt.z*pt.x;
00091 cov(2,1) += pt.z*pt.y;
00092 cov(2,2) += pt.z*pt.z;
00093 }
00094
00095 mean /= static_cast<double> (k_correspondences_);
00096
00097 for (int k = 0; k < 3; k++)
00098 for (int l = 0; l <= k; l++)
00099 {
00100 cov(k,l) /= static_cast<double> (k_correspondences_);
00101 cov(k,l) -= mean[k]*mean[l];
00102 cov(l,k) = cov(k,l);
00103 }
00104
00105
00106 Eigen::JacobiSVD<Eigen::Matrix3d> svd(cov, Eigen::ComputeFullU);
00107 cov.setZero ();
00108 Eigen::Matrix3d U = svd.matrixU ();
00109
00110 for(int k = 0; k < 3; k++) {
00111 Eigen::Vector3d col = U.col(k);
00112 double v = 1.;
00113 if(k == 2)
00114 v = gicp_epsilon_;
00115 cov+= v * col * col.transpose();
00116 }
00117 }
00118 }
00119
00121 template <typename PointSource, typename PointTarget> void
00122 pcl::GeneralizedIterativeClosestPoint<PointSource, PointTarget>::computeRDerivative(const Vector6d &x, const Eigen::Matrix3d &R, Vector6d& g) const
00123 {
00124 Eigen::Matrix3d dR_dPhi;
00125 Eigen::Matrix3d dR_dTheta;
00126 Eigen::Matrix3d dR_dPsi;
00127
00128 double phi = x[3], theta = x[4], psi = x[5];
00129
00130 double cphi = cos(phi), sphi = sin(phi);
00131 double ctheta = cos(theta), stheta = sin(theta);
00132 double cpsi = cos(psi), spsi = sin(psi);
00133
00134 dR_dPhi(0,0) = 0.;
00135 dR_dPhi(1,0) = 0.;
00136 dR_dPhi(2,0) = 0.;
00137
00138 dR_dPhi(0,1) = sphi*spsi + cphi*cpsi*stheta;
00139 dR_dPhi(1,1) = -cpsi*sphi + cphi*spsi*stheta;
00140 dR_dPhi(2,1) = cphi*ctheta;
00141
00142 dR_dPhi(0,2) = cphi*spsi - cpsi*sphi*stheta;
00143 dR_dPhi(1,2) = -cphi*cpsi - sphi*spsi*stheta;
00144 dR_dPhi(2,2) = -ctheta*sphi;
00145
00146 dR_dTheta(0,0) = -cpsi*stheta;
00147 dR_dTheta(1,0) = -spsi*stheta;
00148 dR_dTheta(2,0) = -ctheta;
00149
00150 dR_dTheta(0,1) = cpsi*ctheta*sphi;
00151 dR_dTheta(1,1) = ctheta*sphi*spsi;
00152 dR_dTheta(2,1) = -sphi*stheta;
00153
00154 dR_dTheta(0,2) = cphi*cpsi*ctheta;
00155 dR_dTheta(1,2) = cphi*ctheta*spsi;
00156 dR_dTheta(2,2) = -cphi*stheta;
00157
00158 dR_dPsi(0,0) = -ctheta*spsi;
00159 dR_dPsi(1,0) = cpsi*ctheta;
00160 dR_dPsi(2,0) = 0.;
00161
00162 dR_dPsi(0,1) = -cphi*cpsi - sphi*spsi*stheta;
00163 dR_dPsi(1,1) = -cphi*spsi + cpsi*sphi*stheta;
00164 dR_dPsi(2,1) = 0.;
00165
00166 dR_dPsi(0,2) = cpsi*sphi - cphi*spsi*stheta;
00167 dR_dPsi(1,2) = sphi*spsi + cphi*cpsi*stheta;
00168 dR_dPsi(2,2) = 0.;
00169
00170 g[3] = matricesInnerProd(dR_dPhi, R);
00171 g[4] = matricesInnerProd(dR_dTheta, R);
00172 g[5] = matricesInnerProd(dR_dPsi, R);
00173 }
00174
00176 template <typename PointSource, typename PointTarget> void
00177 pcl::GeneralizedIterativeClosestPoint<PointSource, PointTarget>::estimateRigidTransformationBFGS (const PointCloudSource &cloud_src,
00178 const std::vector<int> &indices_src,
00179 const PointCloudTarget &cloud_tgt,
00180 const std::vector<int> &indices_tgt,
00181 Eigen::Matrix4f &transformation_matrix)
00182 {
00183 if (indices_src.size () < 4)
00184 {
00185 PCL_THROW_EXCEPTION (NotEnoughPointsException,
00186 "[pcl::GeneralizedIterativeClosestPoint::estimateRigidTransformationBFGS] Need at least 4 points to estimate a transform! Source and target have " << indices_src.size () << " points!");
00187 return;
00188 }
00189
00190 Vector6d x = Vector6d::Zero ();
00191 x[0] = transformation_matrix (0,3);
00192 x[1] = transformation_matrix (1,3);
00193 x[2] = transformation_matrix (2,3);
00194 x[3] = atan2 (transformation_matrix (2,1), transformation_matrix (2,2));
00195 x[4] = asin (-transformation_matrix (2,0));
00196 x[5] = atan2 (transformation_matrix (1,0), transformation_matrix (0,0));
00197
00198
00199 tmp_src_ = &cloud_src;
00200 tmp_tgt_ = &cloud_tgt;
00201 tmp_idx_src_ = &indices_src;
00202 tmp_idx_tgt_ = &indices_tgt;
00203
00204
00205 const double gradient_tol = 1e-2;
00206 OptimizationFunctorWithIndices functor(this);
00207 BFGS<OptimizationFunctorWithIndices> bfgs (functor);
00208 bfgs.parameters.sigma = 0.01;
00209 bfgs.parameters.rho = 0.01;
00210 bfgs.parameters.tau1 = 9;
00211 bfgs.parameters.tau2 = 0.05;
00212 bfgs.parameters.tau3 = 0.5;
00213 bfgs.parameters.order = 3;
00214
00215 int inner_iterations_ = 0;
00216 int result = bfgs.minimizeInit (x);
00217 do
00218 {
00219 inner_iterations_++;
00220 result = bfgs.minimizeOneStep (x);
00221 if(result)
00222 {
00223 break;
00224 }
00225 result = bfgs.testGradient(gradient_tol);
00226 } while(result == BFGSSpace::Running && inner_iterations_ < max_inner_iterations_);
00227 if(result == BFGSSpace::NoProgress || result == BFGSSpace::Success || inner_iterations_ == max_inner_iterations_)
00228 {
00229 PCL_DEBUG ("[pcl::registration::TransformationEstimationBFGS::estimateRigidTransformation]");
00230 PCL_DEBUG ("BFGS solver finished with exit code %i \n", result);
00231 transformation_matrix.setIdentity();
00232 applyState(transformation_matrix, x);
00233 }
00234 else
00235 PCL_THROW_EXCEPTION(SolverDidntConvergeException,
00236 "[pcl::" << getClassName () << "::TransformationEstimationBFGS::estimateRigidTransformation] BFGS solver didn't converge!");
00237 }
00238
00240 template <typename PointSource, typename PointTarget> inline double
00241 pcl::GeneralizedIterativeClosestPoint<PointSource, PointTarget>::OptimizationFunctorWithIndices::operator() (const Vector6d& x)
00242 {
00243 Eigen::Matrix4f transformation_matrix = gicp_->base_transformation_;
00244 gicp_->applyState(transformation_matrix, x);
00245 double f = 0;
00246 int m = static_cast<int> (gicp_->tmp_idx_src_->size ());
00247 for (int i = 0; i < m; ++i)
00248 {
00249
00250 Vector4fMapConst p_src = gicp_->tmp_src_->points[(*gicp_->tmp_idx_src_)[i]].getVector4fMap ();
00251
00252 Vector4fMapConst p_tgt = gicp_->tmp_tgt_->points[(*gicp_->tmp_idx_tgt_)[i]].getVector4fMap ();
00253 Eigen::Vector4f pp (transformation_matrix * p_src);
00254
00255
00256 Eigen::Vector3d res(pp[0] - p_tgt[0], pp[1] - p_tgt[1], pp[2] - p_tgt[2]);
00257 Eigen::Vector3d temp (gicp_->mahalanobis((*gicp_->tmp_idx_src_)[i]) * res);
00258
00259 f+= double(res.transpose() * temp);
00260 }
00261 return f/m;
00262 }
00263
00265 template <typename PointSource, typename PointTarget> inline void
00266 pcl::GeneralizedIterativeClosestPoint<PointSource, PointTarget>::OptimizationFunctorWithIndices::df (const Vector6d& x, Vector6d& g)
00267 {
00268 Eigen::Matrix4f transformation_matrix = gicp_->base_transformation_;
00269 gicp_->applyState(transformation_matrix, x);
00270
00271 g.setZero ();
00272
00273 Eigen::Matrix3d R = Eigen::Matrix3d::Zero ();
00274 int m = static_cast<int> (gicp_->tmp_idx_src_->size ());
00275 for (int i = 0; i < m; ++i)
00276 {
00277
00278 Vector4fMapConst p_src = gicp_->tmp_src_->points[(*gicp_->tmp_idx_src_)[i]].getVector4fMap ();
00279
00280 Vector4fMapConst p_tgt = gicp_->tmp_tgt_->points[(*gicp_->tmp_idx_tgt_)[i]].getVector4fMap ();
00281
00282 Eigen::Vector4f pp (transformation_matrix * p_src);
00283
00284 Eigen::Vector3d res (pp[0] - p_tgt[0], pp[1] - p_tgt[1], pp[2] - p_tgt[2]);
00285
00286 Eigen::Vector3d temp (gicp_->mahalanobis ((*gicp_->tmp_idx_src_)[i]) * res);
00287
00288
00289 g.head<3> ()+= temp;
00290
00291 pp = gicp_->base_transformation_ * p_src;
00292 Eigen::Vector3d p_src3 (pp[0], pp[1], pp[2]);
00293 R+= p_src3 * temp.transpose();
00294 }
00295 g.head<3> ()*= 2.0/m;
00296 R*= 2.0/m;
00297 gicp_->computeRDerivative(x, R, g);
00298 }
00299
00301 template <typename PointSource, typename PointTarget> inline void
00302 pcl::GeneralizedIterativeClosestPoint<PointSource, PointTarget>::OptimizationFunctorWithIndices::fdf (const Vector6d& x, double& f, Vector6d& g)
00303 {
00304 Eigen::Matrix4f transformation_matrix = gicp_->base_transformation_;
00305 gicp_->applyState(transformation_matrix, x);
00306 f = 0;
00307 g.setZero ();
00308 Eigen::Matrix3d R = Eigen::Matrix3d::Zero ();
00309 const int m = static_cast<const int> (gicp_->tmp_idx_src_->size ());
00310 for (int i = 0; i < m; ++i)
00311 {
00312
00313 Vector4fMapConst p_src = gicp_->tmp_src_->points[(*gicp_->tmp_idx_src_)[i]].getVector4fMap ();
00314
00315 Vector4fMapConst p_tgt = gicp_->tmp_tgt_->points[(*gicp_->tmp_idx_tgt_)[i]].getVector4fMap ();
00316 Eigen::Vector4f pp (transformation_matrix * p_src);
00317
00318 Eigen::Vector3d res (pp[0] - p_tgt[0], pp[1] - p_tgt[1], pp[2] - p_tgt[2]);
00319
00320 Eigen::Vector3d temp (gicp_->mahalanobis((*gicp_->tmp_idx_src_)[i]) * res);
00321
00322 f+= double(res.transpose() * temp);
00323
00324
00325 g.head<3> ()+= temp;
00326 pp = gicp_->base_transformation_ * p_src;
00327 Eigen::Vector3d p_src3 (pp[0], pp[1], pp[2]);
00328
00329 R+= p_src3 * temp.transpose();
00330 }
00331 f/= double(m);
00332 g.head<3> ()*= double(2.0/m);
00333 R*= 2.0/m;
00334 gicp_->computeRDerivative(x, R, g);
00335 }
00336
00338 template <typename PointSource, typename PointTarget> inline void
00339 pcl::GeneralizedIterativeClosestPoint<PointSource, PointTarget>::computeTransformation (PointCloudSource &output, const Eigen::Matrix4f& guess)
00340 {
00341 using namespace std;
00342
00343 double delta = 0;
00344
00345 const size_t N = indices_->size ();
00346
00347 mahalanobis_.resize (N, Eigen::Matrix3d::Identity ());
00348
00349 computeCovariances<PointTarget> (target_, tree_, target_covariances_);
00350
00351 computeCovariances<PointSource> (input_, input_tree_, input_covariances_);
00352
00353 base_transformation_ = guess;
00354 nr_iterations_ = 0;
00355 converged_ = false;
00356 double dist_threshold = corr_dist_threshold_ * corr_dist_threshold_;
00357 std::vector<int> nn_indices (1);
00358 std::vector<float> nn_dists (1);
00359
00360 while(!converged_)
00361 {
00362 size_t cnt = 0;
00363 std::vector<int> source_indices (indices_->size ());
00364 std::vector<int> target_indices (indices_->size ());
00365
00366
00367 Eigen::Matrix4d transform_R = Eigen::Matrix4d::Zero ();
00368 for(size_t i = 0; i < 4; i++)
00369 for(size_t j = 0; j < 4; j++)
00370 for(size_t k = 0; k < 4; k++)
00371 transform_R(i,j)+= double(transformation_(i,k)) * double(guess(k,j));
00372
00373 Eigen::Matrix3d R = transform_R.topLeftCorner<3,3> ();
00374
00375 for (size_t i = 0; i < N; i++)
00376 {
00377 PointSource query = output[i];
00378 query.getVector4fMap () = guess * query.getVector4fMap ();
00379 query.getVector4fMap () = transformation_ * query.getVector4fMap ();
00380
00381 if (!searchForNeighbors (query, nn_indices, nn_dists))
00382 {
00383 PCL_ERROR ("[pcl::%s::computeTransformation] Unable to find a nearest neighbor in the target dataset for point %d in the source!\n", getClassName ().c_str (), (*indices_)[i]);
00384 return;
00385 }
00386
00387
00388 if (nn_dists[0] < dist_threshold)
00389 {
00390 Eigen::Matrix3d &C1 = input_covariances_[i];
00391 Eigen::Matrix3d &C2 = target_covariances_[nn_indices[0]];
00392 Eigen::Matrix3d &M = mahalanobis_[i];
00393
00394 M = R * C1;
00395
00396 Eigen::Matrix3d temp = M * R.transpose();
00397 temp+= C2;
00398
00399 M = temp.inverse ();
00400 source_indices[cnt] = static_cast<int> (i);
00401 target_indices[cnt] = nn_indices[0];
00402 cnt++;
00403 }
00404 }
00405
00406 source_indices.resize(cnt); target_indices.resize(cnt);
00407
00408 previous_transformation_ = transformation_;
00409
00410 try
00411 {
00412 rigid_transformation_estimation_(output, source_indices, *target_, target_indices, transformation_);
00413
00414 delta = 0.;
00415 for(int k = 0; k < 4; k++) {
00416 for(int l = 0; l < 4; l++) {
00417 double ratio = 1;
00418 if(k < 3 && l < 3)
00419 ratio = 1./rotation_epsilon_;
00420 else
00421 ratio = 1./transformation_epsilon_;
00422 double c_delta = ratio*fabs(previous_transformation_(k,l) - transformation_(k,l));
00423 if(c_delta > delta)
00424 delta = c_delta;
00425 }
00426 }
00427 }
00428 catch (PCLException &e)
00429 {
00430 PCL_DEBUG ("[pcl::%s::computeTransformation] Optimization issue %s\n", getClassName ().c_str (), e.what ());
00431 break;
00432 }
00433 nr_iterations_++;
00434
00435 if (nr_iterations_ >= max_iterations_ || delta < 1)
00436 {
00437 converged_ = true;
00438 previous_transformation_ = transformation_;
00439 PCL_DEBUG ("[pcl::%s::computeTransformation] Convergence reached. Number of iterations: %d out of %d. Transformation difference: %f\n",
00440 getClassName ().c_str (), nr_iterations_, max_iterations_, (transformation_ - previous_transformation_).array ().abs ().sum ());
00441 }
00442 else
00443 PCL_DEBUG ("[pcl::%s::computeTransformation] Convergence failed\n", getClassName ().c_str ());
00444 }
00445
00446
00447
00448 final_transformation_.topLeftCorner (3,3) = previous_transformation_.topLeftCorner (3,3) * guess.topLeftCorner (3,3);
00449 final_transformation_(0,3) = previous_transformation_(0,3) + guess(0,3);
00450 final_transformation_(1,3) = previous_transformation_(1,3) + guess(1,3);
00451 final_transformation_(2,3) = previous_transformation_(2,3) + guess(2,3);
00452 }
00453
00454 template <typename PointSource, typename PointTarget> void
00455 pcl::GeneralizedIterativeClosestPoint<PointSource, PointTarget>::applyState(Eigen::Matrix4f &t, const Vector6d& x) const
00456 {
00457
00458 Eigen::Matrix3f R;
00459 R = Eigen::AngleAxisf (static_cast<float> (x[5]), Eigen::Vector3f::UnitZ ())
00460 * Eigen::AngleAxisf (static_cast<float> (x[4]), Eigen::Vector3f::UnitY ())
00461 * Eigen::AngleAxisf (static_cast<float> (x[3]), Eigen::Vector3f::UnitX ());
00462 t.topLeftCorner<3,3> () = R * t.topLeftCorner<3,3> ();
00463 Eigen::Vector4f T (static_cast<float> (x[0]), static_cast<float> (x[1]), static_cast<float> (x[2]), 0.0f);
00464 t.col (3) += T;
00465 }
00466