pcl點雲庫-平面及邊界提取
項目背景:需要從下圖中提取焊道邊界
思路一:先提取平面,再從邊界中提取輪廓。
思路二:計算點雲法向量方向,根據法向量判斷邊界。
先嘗試了思路一,具體實現步驟如下。
(1)首先計算點雲法向量
// Create the normal estimation class, and pass the input dataset to it
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
ne.setInputCloud(cloud);
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
ne.setSearchMethod(tree);
pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
// Use all neighbors in a sphere of radius 1cm
ne.setRadiusSearch(1);
//ne.setKSearch(20);
ne.compute(*normals);
pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals(new pcl::PointCloud<pcl::PointNormal>);
pcl::concatenateFields(*cloud, *normals, *cloud_with_normals);
(2)採用RANSAC提取平面
pcl::SACSegmentationFromNormals<pcl::PointXYZ, pcl::Normal> seg;
pcl::ModelCoefficients::Ptr coefficients_plane(new pcl::ModelCoefficients), coefficients_cylinder(new pcl::ModelCoefficients);
pcl::PointIndices::Ptr inliers_plane(new pcl::PointIndices), inliers_cylinder(new pcl::PointIndices);
pcl::PCDWriter writer;
pcl::ExtractIndices<pcl::PointXYZ> extract;
// Create the segmentation object for the planar model and set all the parameters
seg.setOptimizeCoefficients(true);//設置對估計的模型係數需要進行優化
seg.setModelType(pcl::SACMODEL_NORMAL_PLANE); //設置分割模型
seg.setNormalDistanceWeight(0.1);//設置表面法線權重係數
seg.setMethodType(pcl::SAC_RANSAC);//設置採用RANSAC作為演算法的參數估計方法
seg.setMaxIterations(500); //設置迭代的最大次數
seg.setDistanceThreshold(0.5); //設置內點到模型的距離允許最大值
seg.setInputCloud(cloud);
seg.setInputNormals(normals);
// Obtain the plane inliers and coefficients
seg.segment(*inliers_plane, *coefficients_plane);
std::cerr << "Plane coefficients: " << *coefficients_plane << std::endl;
// Extract the planar inliers from the input cloud
extract.setInputCloud(cloud);
extract.setIndices(inliers_plane);
extract.setNegative(false);
提取的效果如下:
值得注意的是參數seg.setMaxIterations()和seg.setDistanceThreshold();的設置,需要根據點雲數據的間隔來取值,不然無法得到想要的提取效果。
(3)計算邊界
要先計算分割出平面的法向量,然後提取輪廓邊界。
//calculate boundary;
pcl::PointCloud<pcl::Boundary> boundary;
pcl::BoundaryEstimation<pcl::PointXYZ, pcl::Normal, pcl::Boundary> est;
est.setInputCloud(cloud_plane);
est.setInputNormals(normals_plane);
est.setSearchMethod(tree_plane);
est.setKSearch(50); //一般這裡的數值越高,最終邊界識別的精度越好
pcl::search::KdTree<pcl::PointXYZ>));
est.compute(boundary);
est.setKSearch()的數值越高,最終邊界識別的精度越好,但是識別出的邊緣具有斜鋸齒效果,不知道是否取決於演算法原理,打算看看源碼研究一番。
以上三個步驟大體實現了輪廓提取的目的,雖然效果還不夠理想。更需要的是上圖中的內輪廓,而不包括矩形邊界,後期需要在改進演算法除去。
要是讀者有更好的方法可以一起交流,一起進步!
推薦閱讀: