項目背景:需要從下圖中提取焊道邊界

原始點雲圖

思路一:先提取平面,再從邊界中提取輪廓。

思路二:計算點雲法向量方向,根據法向量判斷邊界。

先嘗試了思路一,具體實現步驟如下。

(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()的數值越高,最終邊界識別的精度越好,但是識別出的邊緣具有斜鋸齒效果,不知道是否取決於演算法原理,打算看看源碼研究一番。

以上三個步驟大體實現了輪廓提取的目的,雖然效果還不夠理想。更需要的是上圖中的內輪廓,而不包括矩形邊界,後期需要在改進演算法除去。

要是讀者有更好的方法可以一起交流,一起進步!


推薦閱讀:
相關文章