基于PCL的点云平面以及边界的提取

1、PCL 点云边界提取
该边界提取采用PCL库里边的方法,基于法线估计来实现的边界检测与提取:首先从原始点云上计算出法线,再由法线结合数据估计出边界。

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/features/boundary.h>
#include <pcl/features/normal_3d.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>


int
main(int argc, char** argv)
{
    
    
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::io::loadPCDFile("test.pcd", *cloud);
	pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normEst;
	pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
	normEst.setInputCloud(pcl::PointCloud<pcl::PointXYZ>::Ptr(cloud));
	normEst.setRadiusSearch(0.1);
	normEst.compute(*normals);
	pcl::PointCloud<pcl::Boundary> boundaries;
	pcl::BoundaryEstimation<pcl::PointXYZ, pcl::Normal, pcl::Boundary> boundEst;
	boundEst.setInputCloud(cloud);
	boundEst.setInputNormals(normals);
	boundEst.setRadiusSearch(0.1);
	boundEst.setAngleThreshold(M_PI / 4);
	boundEst.setSearchMethod(pcl::search::KdTree<pcl::PointXYZ>::Ptr(new pcl::search::KdTree<pcl::PointXYZ>));
	boundEst.compute(boundaries);

	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_boundary(new pcl::PointCloud<pcl::PointXYZ>);
	for (int i = 0; i < cloud->points.size(); i++)
	{
    
    

		if (boundaries[i].boundary_point > 0)
		{
    
    
			cloud_boundary->push_back(cloud->points[i]);
		}
	}

	boost::shared_ptr<pcl::visualization::PCLVisualizer> view(new pcl::visualization::PCLVisualizer("3D"));
	int v1(0);
	view->createViewPort(0.0, 0.0, 0.5, 1.0, v1);
	view->setBackgroundColor(0.3, 0.3, 0.3, v1);
	view->addText("Raw point clouds", 10, 10, "v1_text", v1);
	int v2(0);
	view->createViewPort(0.5, 0.0, 1, 1.0, v2);
	view->setBackgroundColor(0.5, 0.5, 0.5, v2);
	view->addText("Boudary point clouds", 10, 10, "v2_text", v2);
	view->addPointCloud<pcl::PointXYZ>(cloud, "sample cloud", v1);
	view->addPointCloud<pcl::PointXYZ>(cloud_boundary, "cloud_boundary", v2);
	view->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 0, 0, "sample cloud", v1);
	view->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0, 1, 0, "cloud_boundary", v2);
	view->addCoordinateSystem(1.0);
	view->initCameraParameters();

	view->spin();

	return 0;

}

在这里插入图片描述
2、点云焊道提取
在这里插入图片描述
思路一:先提取平面,再从边界中提取轮廓。

思路二:计算点云法向量方向,根据法向量判断边界。

先尝试了思路一,具体实现步骤如下。

(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()的数值越高,最终边界识别的精度越好,但是识别出的边缘具有斜锯齿效果,不知道是否取决于算法原理,打算看看源码研究一番。

以上三个步骤大体实现了轮廓提取的目的,虽然效果还不够理想。更需要的是上图中的内轮廓,而不包括矩形边界,后期需要在改进算法除去。

3、焊点提取
在这里插入图片描述

体素栅格:

	pcl::VoxelGrid<pcl::PointXYZ> sor;
	sor.setInputCloud(cloudA);
	sor.setLeafSize(1.2f, 1.2f, 1.2f);//设置滤波时创建的体素大小为1.2cm立方体
	sor.filter(*cloud_filtered);

高斯:

pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>); //kdtree搜索
	pcl::PointCloud<pcl::PointNormal> mls_points;
	pcl::MovingLeastSquares<pcl::PointXYZ, pcl::PointNormal> mls;
		mls.setComputeNormals(true);// 最小二乘计算中
	mls.setInputCloud(cloud);
	mls.setPolynomialFit(true);  //多项式拟合提高精度,
	mls.setPolynomialOrder(2);//2次多项式
	mls.setSearchMethod(tree);
	mls.setSearchRadius(0.05);//半径
	mls.setSqrGaussParam(10);

导入点云:

//导入pcd
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloudA(new pcl::PointCloud<pcl::PointXYZ>);
	if (pcl::io::loadPCDFile<pcl::PointXYZ>("xx.pcd", *cloudA) == -1)
	{
    
    
		PCL_ERROR("未导入点云\n");
		return -1;
	}

2.法线计算:

//先计算法线
	pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
	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>);
	ne.setRadiusSearch(1);//搜索半径1cm
	ne.compute(*normals);
	pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals(new pcl::PointCloud<pcl::PointNormal>);
	pcl::concatenateFields(*cloudA, *normals, *cloud_with_normals);

3.提取平面:采用RANSAC提取平面

     setOptimizeCoefficients(true);
	seg.setModelType(pcl::SACMODEL_NORMAL_PLANE); 
	seg.setNormalDistanceWeight(0.1);//法线权重系数0.1
	seg.setMethodType(pcl::SAC_RANSAC);
	seg.setMaxIterations(1000); //迭代的次数1000
	seg.setDistanceThreshold(0.5); //内点到模型的距离最大0.5

4.边界提取

	normEst.setKSearch(9);  //法向估计的点数
	normEst.compute(*normals1);
	est.setInputCloud(cloud_filtered);
	est.setInputNormals(normals1);
	est.setSearchMethod(tree1);
	est.setKSearch(20);  
	est.compute(boundaries);

在这里插入图片描述
参考文献:
https://blog.csdn.net/uranus1992/article/details/107047504
https://zhuanlan.zhihu.com/p/32111069

猜你喜欢

转载自blog.csdn.net/qq_27353621/article/details/126592252