上一篇:http://www.cnblogs.com/yhlx125/p/4924283.html截图了一些ICP算法进行点云匹配的类图。
但是将对应点剔除这块和ICP算法的关系还是没有理解。
RANSAC算法可以实现点云剔除,但是ICP算法通过稳健性的算法实现匹配,似乎不进行对应点剔除。是不是把全局的点云匹配方法和局部点云匹配方法搞混了?
ICP算法可以通过三种方式处理噪声、部分重叠的问题:剔除、权重、Trimmed方法和稳健估计方法。下面分析一下PCL中关于ICP算法的实现。
首先是IterativeClosestPoint模板类继承自Registration模板类。
查看icp.h中的定义:
template <typename PointSource, typename PointTarget, typename Scalar = float> class IterativeClosestPoint : public Registration<PointSource, PointTarget, Scalar>
查看registration.h中的定义:
template <typename PointSource, typename PointTarget, typename Scalar = float> class Registration : public PCLBase<PointSource>
同样的IterativeClosestPointNonLinear 继承自IterativeClosestPoint,查看icp_nl.h
template <typename PointSource, typename PointTarget, typename Scalar = float> class IterativeClosestPointNonLinear : public IterativeClosestPoint<PointSource, PointTarget, Scalar>
区别在于ICP算法的求解方式不同,非线性求解采用的是LM算法:http://www.cnblogs.com/yhlx125/p/4955337.html
下面重点说明IterativeClosestPoint模板类。匹配的关键方法Align在Registration中实现。
1 template <typename PointSource, typename PointTarget, typename Scalar> inline void 2 pcl::Registration<PointSource, PointTarget, Scalar>::align (PointCloudSource &output, const Matrix4& guess) 3 { 4 if (!initCompute ()) 5 return; 6 7 // Resize the output dataset 8 if (output.points.size () != indices_->size ()) 9 output.points.resize (indices_->size ()); 10 // Copy the header 11 output.header = input_->header; 12 // Check if the output will be computed for all points or only a subset 13 if (indices_->size () != input_->points.size ()) 14 { 15 output.width = static_cast<uint32_t> (indices_->size ()); 16 output.height = 1; 17 } 18 else 19 { 20 output.width = static_cast<uint32_t> (input_->width); 21 output.height = input_->height; 22 } 23 output.is_dense = input_->is_dense; 24 25 // Copy the point data to output 26 for (size_t i = 0; i < indices_->size (); ++i) 27 output.points[i] = input_->points[(*indices_)[i]]; 28 29 // Set the internal point representation of choice unless otherwise noted 30 if (point_representation_ && !force_no_recompute_) 31 tree_->setPointRepresentation (point_representation_); 32 33 // Perform the actual transformation computation 34 converged_ = false; 35 final_transformation_ = transformation_ = previous_transformation_ = Matrix4::Identity (); 36 37 // Right before we estimate the transformation, we set all the point.data[3] values to 1 to aid the rigid 38 // transformation 39 for (size_t i = 0; i < indices_->size (); ++i) 40 output.points[i].data[3] = 1.0; 41 42 computeTransformation (output, guess); 43 44 deinitCompute (); 45 }
重点关注computeTransformation虚方法。显然IterativeClosestPoint重载了基类这个方法。
virtual void computeTransformation (PointCloudSource &output, const Matrix4& guess) = 0;
代码如下:
1 template <typename PointSource, typename PointTarget, typename Scalar> void 2 pcl::IterativeClosestPoint<PointSource, PointTarget, Scalar>::computeTransformation ( 3 PointCloudSource &output, const Matrix4 &guess) 4 { 5 // Point cloud containing the correspondences of each point in <input, indices> 6 PointCloudSourcePtr input_transformed (new PointCloudSource); 7 8 nr_iterations_ = 0; 9 converged_ = false; 10 11 // Initialise final transformation to the guessed one 12 final_transformation_ = guess; 13 14 // If the guessed transformation is non identity 15 if (guess != Matrix4::Identity ()) 16 { 17 input_transformed->resize (input_->size ()); 18 // Apply guessed transformation prior to search for neighbours 19 transformCloud (*input_, *input_transformed, guess); 20 } 21 else 22 *input_transformed = *input_; 23 24 transformation_ = Matrix4::Identity (); 25 26 // Make blobs if necessary 27 determineRequiredBlobData (); 28 PCLPointCloud2::Ptr target_blob (new PCLPointCloud2); 29 if (need_target_blob_) 30 pcl::toPCLPointCloud2 (*target_, *target_blob); 31 32 // Pass in the default target for the Correspondence Estimation/Rejection code 33 correspondence_estimation_->setInputTarget (target_); 34 if (correspondence_estimation_->requiresTargetNormals ()) 35 correspondence_estimation_->setTargetNormals (target_blob); 36 // Correspondence Rejectors need a binary blob 37 for (size_t i = 0; i < correspondence_rejectors_.size (); ++i) 38 { 39 registration::CorrespondenceRejector::Ptr& rej = correspondence_rejectors_[i]; 40 if (rej->requiresTargetPoints ()) 41 rej->setTargetPoints (target_blob); 42 if (rej->requiresTargetNormals () && target_has_normals_) 43 rej->setTargetNormals (target_blob); 44 } 45 46 convergence_criteria_->setMaximumIterations (max_iterations_); 47 convergence_criteria_->setRelativeMSE (euclidean_fitness_epsilon_); 48 convergence_criteria_->setTranslationThreshold (transformation_epsilon_); 49 convergence_criteria_->setRotationThreshold (1.0 - transformation_epsilon_); 50 51 // Repeat until convergence 52 do 53 { 54 // Get blob data if needed 55 PCLPointCloud2::Ptr input_transformed_blob; 56 if (need_source_blob_) 57 { 58 input_transformed_blob.reset (new PCLPointCloud2); 59 toPCLPointCloud2 (*input_transformed, *input_transformed_blob); 60 } 61 // Save the previously estimated transformation 62 previous_transformation_ = transformation_; 63 64 // Set the source each iteration, to ensure the dirty flag is updated 65 correspondence_estimation_->setInputSource (input_transformed); 66 if (correspondence_estimation_->requiresSourceNormals ()) 67 correspondence_estimation_->setSourceNormals (input_transformed_blob); 68 // Estimate correspondences 69 if (use_reciprocal_correspondence_) 70 correspondence_estimation_->determineReciprocalCorrespondences (*correspondences_, corr_dist_threshold_); 71 else 72 correspondence_estimation_->determineCorrespondences (*correspondences_, corr_dist_threshold_); 73 74 //if (correspondence_rejectors_.empty ()) 75 CorrespondencesPtr temp_correspondences (new Correspondences (*correspondences_)); 76 for (size_t i = 0; i < correspondence_rejectors_.size (); ++i) 77 { 78 registration::CorrespondenceRejector::Ptr& rej = correspondence_rejectors_[i]; 79 PCL_DEBUG ("Applying a correspondence rejector method: %s.\n", rej->getClassName ().c_str ()); 80 if (rej->requiresSourcePoints ()) 81 rej->setSourcePoints (input_transformed_blob); 82 if (rej->requiresSourceNormals () && source_has_normals_) 83 rej->setSourceNormals (input_transformed_blob); 84 rej->setInputCorrespondences (temp_correspondences); 85 rej->getCorrespondences (*correspondences_); 86 // Modify input for the next iteration 87 if (i < correspondence_rejectors_.size () - 1) 88 *temp_correspondences = *correspondences_; 89 } 90 91 size_t cnt = correspondences_->size (); 92 // Check whether we have enough correspondences 93 if (static_cast<int> (cnt) < min_number_correspondences_) 94 { 95 PCL_ERROR ("[pcl::%s::computeTransformation] Not enough correspondences found. Relax your threshold parameters.\n", getClassName ().c_str ()); 96 convergence_criteria_->setConvergenceState(pcl::registration::DefaultConvergenceCriteria<Scalar>::CONVERGENCE_CRITERIA_NO_CORRESPONDENCES); 97 converged_ = false; 98 break; 99 } 100 101 // Estimate the transform 102 transformation_estimation_->estimateRigidTransformation (*input_transformed, *target_, *correspondences_, transformation_); 103 104 // Tranform the data 105 transformCloud (*input_transformed, *input_transformed, transformation_); 106 107 // Obtain the final transformation 108 final_transformation_ = transformation_ * final_transformation_; 109 110 ++nr_iterations_; 111 112 // Update the vizualization of icp convergence 113 //if (update_visualizer_ != 0) 114 // update_visualizer_(output, source_indices_good, *target_, target_indices_good ); 115 116 converged_ = static_cast<bool> ((*convergence_criteria_)); 117 } 118 while (!converged_); 119 120 // Transform the input cloud using the final transformation 121 PCL_DEBUG ("Transformation is:\n\t%5f\t%5f\t%5f\t%5f\n\t%5f\t%5f\t%5f\t%5f\n\t%5f\t%5f\t%5f\t%5f\n\t%5f\t%5f\t%5f\t%5f\n", 122 final_transformation_ (0, 0), final_transformation_ (0, 1), final_transformation_ (0, 2), final_transformation_ (0, 3), 123 final_transformation_ (1, 0), final_transformation_ (1, 1), final_transformation_ (1, 2), final_transformation_ (1, 3), 124 final_transformation_ (2, 0), final_transformation_ (2, 1), final_transformation_ (2, 2), final_transformation_ (2, 3), 125 final_transformation_ (3, 0), final_transformation_ (3, 1), final_transformation_ (3, 2), final_transformation_ (3, 3)); 126 127 // Copy all the values 128 output = *input_; 129 // Transform the XYZ + normals 130 transformCloud (*input_, output, final_transformation_); 131 }
该方法的主体是一个do-while循环。这里要说三个内容:correspondence_estimation_ 、correspondence_rejectors_ 和 convergence_criteria_ 。
三个变量的作用分别是查找最近点,剔除错误的对应点,收敛准则。因为是do-while循环,因此收敛准则的作用是跳出循环。
transformation_estimation_是求解ICP的具体算法:
1 transformation_estimation_->estimateRigidTransformation (*input_transformed, *target_, *correspondences_, transformation_);
查看类图可以知道包括SVD奇异值分解算法,查看transformation_estimation_svd.hpp中的TransformationEstimationSVD类的estimateRigidTransformation 方法:
template <typename PointSource, typename PointTarget, typename Scalar> inline void pcl::registration::TransformationEstimationSVD<PointSource, PointTarget, Scalar>::estimateRigidTransformation ( ConstCloudIterator<PointSource>& source_it, ConstCloudIterator<PointTarget>& target_it, Matrix4 &transformation_matrix) const
其中通过调用下面的代码实现了SVD求解,具体方法内部实现时通过Eigen3实现的。需要具体查看,可以借鉴。
transformation_matrix = pcl::umeyama (cloud_src, cloud_tgt, false);