PCL点云初步

一、PCL滤波

直通滤波

设置一个x,y,z的要保存的点的范围,滤出超过范围的点

//创建一个直通滤波器的对象,并设置相关参数
pcl::PassThrough<pcl::PointXYZ> pass;	//创建对象
pass.setInputCloud(cloud);				//设置输入点云
pass.setFilterFieldName("z");			//设置过滤字段,这里对z轴上的点云进行过滤
pass.setFilterLimits(0.0, 1.0);			//设置过滤范围,这里选择过滤掉0~1范围内的点云
//pass.setFilterLimitsNegative (true);	//设置选择保留范围内的还是过滤掉范围内的点云
pass.filter(*cloud_filtered);			//执行滤波,结果保存在cloud_filter中

体素滤波

将空间划分为一定体积的网格,用网格内所有点的质心来代替所有点(适合于一个比较稠密的场景)特征后期需要根据应用场景进行调参,去寻找一个合适的网格大小

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/voxel_grid.h>			//头文件

int
main(int argc, char** argv)
{
    pcl::PCLPointCloud2::Ptr cloud(new pcl::PCLPointCloud2());
    pcl::PCLPointCloud2::Ptr cloud_filtered(new pcl::PCLPointCloud2());

    // Fill in the cloud data
    pcl::PCDReader reader;	//读文件对象
    // Replace the path below with the path where you saved your file
    //可以换成你自己电脑上的对应文件路径
    reader.read("./pcd/table_scene_lms400.pcd", *cloud); //确保你有这个pcd的文件

    std::cerr << "PointCloud before filtering: " << cloud->width * cloud->height
        << " data points (" << pcl::getFieldsList(*cloud) << ").";

	//创建voxelGrid对象,及相关参数的设置
    pcl::VoxelGrid<pcl::PCLPointCloud2> sor;	//创建voxelGrid对象
    sor.setInputCloud(cloud);					//设置输入点云
    sor.setLeafSize(0.01f, 0.01f, 0.01f);		//设置体素大小,单位是m,这里设置成了1cm的立方体
    sor.filter(*cloud_filtered);				//执行滤波,结果保存在cloud_filter中

    std::cerr << "PointCloud after filtering: " << cloud_filtered->width * cloud_filtered->height
        << " data points (" << pcl::getFieldsList(*cloud_filtered) << ").";

    //写入文件中
    pcl::PCDWriter writer;
    writer.write("table_scene_lms400_downsampled.pcd", *cloud_filtered,
        Eigen::Vector4f::Zero(), Eigen::Quaternionf::Identity(), false);

    return (0);
}

基于统计学的离群点滤波

统计每个点与周围最邻近的若干个点的平均距离,滤除大于阈值的离群点

//创建滤波器对象,及相关参数设置
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;		//创建对象
sor.setInputCloud(cloud);								//设置输入点云
sor.setMeanK(50);										//设置统计时考虑查询点邻近点数
/*
	* 设置判断是否为离群点的阈值
	* 更具体为设置标准差倍数阈值 std_mul ,点云中所有点与其邻域的距离大于 μ ±σ• std_mul 
	* 则被认为是离群点,其中 μ 代表估计的平均距离, σ 代表标准差 。
*/
sor.setStddevMulThresh(1.0);	//设置为1代表:如果一个点的距离超过平均距离一个标准差以上,则会被当做离群点去除
sor.filter(*cloud_filtered);	//执行滤波,并将结果保存在cloud_filter中

半径滤波

用于用户指定每个点的一定范围内周围至少有足够多的近邻,如果指定至少需要2个近邻,那么以某个点为圆心,在一定半径大小的圆内,除了它自己还需要存在两个点,否则这个圆内的所有点都将被删除。

//创建过滤器
pcl::RadiusOutlierRemoval<pcl::PointXYZ> outrem;
outrem.setInputCloud(cloud);        //设置输入点云
outrem.setRadiusSearch(0.8);        //设置在0.8的半径范围内找近邻点
outrem.setMinNeighborsInRadius(2);  //设置查询查询点的近邻点集数小于2的删除

outrem.filter(*cloud_filtered);     //执行滤波,结果保存在cloud_filter

可以用于处理空气中的噪点和边缘的噪点

二、PCL聚类与分割

RANSAC随机抽样一致性算法(主要是平面、聚类、法线、圆柱分割、索引提取)

1.平面分割

pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);
pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
// Create the segmentation object
pcl::SACSegmentation<pcl::PointXYZ> seg;
seg.setModelType (pcl::SACMODEL_PLANE);//待拟合的类型
seg.setMethodType (pcl::SAC_RANSAC);
seg.setDistanceThreshold (0.01);//设置阈值

seg.setInputCloud (cloud);
seg.segment (*inliers, *coefficients);//前者存放内点的索引和模型参数

2.法线估计

#include <pcl/io/io.h>
#include <pcl/io/pcd_io.h>
#include <pcl/features/integral_image_normal.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/point_types.h>
#include <pcl/features/normal_3d.h>//法线

int main()
{
	//加载点云
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::io::loadPCDFile("E:/pcl_project/normal/normal/table_scene_lms400.pcd", *cloud);
	//估计法线
	pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
	ne.setInputCloud(cloud);
	//创建一个空的kdtree对象,并把它传递给法线估计对象
	//基于给出的输入数据集,kdtree将被建立
	pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
	ne.setSearchMethod(tree);
	//输出数据集
	pcl::PointCloud<pcl::Normal>::Ptr cloud_normals(new pcl::PointCloud<pcl::Normal>);
	//使用半径在查询点周围3厘米范围内的所有邻元素,这里参数的单位为米
	ne.setRadiusSearch(0.03);
	//计算特征值
	ne.compute(*cloud_normals);
	// cloud_normals->points.size ()应该与input cloud_downsampled->points.size ()有相同尺寸
	//法线可视化
	pcl::visualization::PCLVisualizer viewer("PCL Viewer");
	viewer.setBackgroundColor(0.0, 0.0, 0.0);
	viewer.addPointCloudNormals<pcl::PointXYZ, pcl::Normal>(cloud, cloud_normals);

	while (!viewer.wasStopped())
	{
		viewer.spinOnce();
	}

	return 0;
}

3.圆柱拟合

#include <pcl/io/pcd_io.h>
#include <pcl/features/normal_3d.h>				// 法向量估计
#include <pcl/segmentation/sac_segmentation.h>	// 模型分割
#include <pcl/filters/extract_indices.h>		// 索引提取
#include <pcl/visualization/cloud_viewer.h>		// 可视化

using namespace std;

typedef pcl::PointXYZ PointT;

int main()
{	
	//-----------------------------加载点云--------------------------------
	pcl::PointCloud<PointT>::Ptr cloud(new pcl::PointCloud<PointT>);
	if (pcl::io::loadPCDFile("test.pcd", *cloud) < 0)
	{
		PCL_ERROR("\a点云文件不存在!\n");
		system("pause");
		return -1;
	}
	cout << "->加载数据点的个数:" << cloud->points.size() << endl;
	//=====================================================================

	//-----------------------------法线估计--------------------------------
	cout << "->正在计算法线..." << endl;
	pcl::NormalEstimation<PointT, pcl::Normal> ne;	// 创建法向量估计对象
	pcl::search::KdTree<PointT>::Ptr tree(new pcl::search::KdTree<PointT>());
	ne.setSearchMethod(tree);						// 设置搜索方式
	ne.setInputCloud(cloud);						// 设置输入点云
	ne.setKSearch(50);								// 设置K近邻搜索点的个数
	pcl::PointCloud<pcl::Normal>::Ptr cloud_normals(new pcl::PointCloud<pcl::Normal>);
	ne.compute(*cloud_normals);						// 计算法向量,并将结果保存到cloud_normals中
	//=====================================================================

	//----------------------------圆柱体分割--------------------------------
	cout << "->正在圆柱体分割..." << endl;
	pcl::SACSegmentationFromNormals<PointT, pcl::Normal> seg;		// 创建圆柱体分割对象
	seg.setInputCloud(cloud);										// 设置输入点云:待分割点云
	seg.setOptimizeCoefficients(true);								// 设置对估计的模型系数需要进行优化
	seg.setModelType(pcl::SACMODEL_CYLINDER);						// 设置分割模型为圆柱体模型
	seg.setMethodType(pcl::SAC_RANSAC);								// 设置采用RANSAC算法进行参数估计
	seg.setNormalDistanceWeight(0.1);								// 设置表面法线权重系数
	seg.setMaxIterations(10000);									// 设置迭代的最大次数
	seg.setDistanceThreshold(0.05);									// 设置内点到模型距离的最大值
	seg.setRadiusLimits(3.0, 4.0);									// 设置圆柱模型半径的范围
	seg.setInputNormals(cloud_normals);								// 设置输入法向量
	pcl::PointIndices::Ptr inliers_cylinder(new pcl::PointIndices);	// 保存分割结果
	pcl::ModelCoefficients::Ptr coefficients_cylinder(new pcl::ModelCoefficients);	// 保存圆柱体模型系数
	seg.segment(*inliers_cylinder, *coefficients_cylinder);			// 执行分割,将分割结果的索引保存到inliers_cylinder中,同时存储模型系数coefficients_cylinder
	cout << "\n\t\t-----圆柱体系数-----" << endl;
	cout << "轴线一点坐标:(" << coefficients_cylinder->values[0] << ", "
		<< coefficients_cylinder->values[1] << ", "
		<< coefficients_cylinder->values[2] << ")"
		<< endl;
	cout << "轴线方向向量:(" << coefficients_cylinder->values[3] << ", "
		<< coefficients_cylinder->values[4] << ", "
		<< coefficients_cylinder->values[5] << ")"
		<< endl;
	cout << "圆柱体半径:" << coefficients_cylinder->values[6] << endl;
	//=====================================================================

	//------------------------------提取分割结果----------------------------
	cout << "->正在提取分割结果..." << endl;
	pcl::ExtractIndices<PointT> extract;	// 创建索引提取点对象
	extract.setInputCloud(cloud);			// 设置输入点云:待分割点云
	extract.setIndices(inliers_cylinder);	// 设置内点索引
	extract.setNegative(false);				// 默认false,提取圆柱体内点;true,提取圆柱体外点
	pcl::PointCloud<PointT>::Ptr cloud_cylinder(new pcl::PointCloud<PointT>());
	extract.filter(*cloud_cylinder);		// 执行滤波,并将结果点云保存到cloud_cylinder中
	//=====================================================================

	//----------------------------------保存分割结果------------------------
	if (!cloud_cylinder->points.empty())
	{
		pcl::PCDWriter writer;
		writer.write("cylinder.pcd", *cloud_cylinder, true);
		cout << "->圆柱体模型点云个数:" << cloud_cylinder->size() << endl;
	}
	else
	{
		PCL_ERROR("未提取出圆柱体模型点!\a\n");
	}
	//=====================================================================

	//-------------------------可视化分割结果(可选操作)--------------------
	pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("可视化分割结果"));

	///视口1:原始点云
	int v1;
	viewer->createViewPort(0.0, 0.0, 0.5, 1.0, v1); //设置第一个视口在X轴、Y轴的最小值、最大值,取值在0-1之间
	viewer->setBackgroundColor(0, 0, 0, v1);
	viewer->addText("original_point_cloud", 10, 10, "v1 text", v1);
	viewer->addPointCloud<PointT>(cloud, "original_point_cloud", v1);
	viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 0, 0, "original_point_cloud", v1);
	viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "original_point_cloud", v1);

	///视口2:分割后点云
	int v2;
	viewer->createViewPort(0.5, 0.0, 1.0, 1.0, v2);
	viewer->setBackgroundColor(0.3, 0.3, 0.3, v2);
	viewer->addText("segment_point_cloud", 10, 10, "v2 text", v2);
	pcl::visualization::PointCloudColorHandlerCustom<PointT> set_color(cloud_cylinder, 0, 255, 0);
	viewer->addPointCloud<PointT>(cloud_cylinder, set_color, "segment_point_cloud", v2);
	viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "segment_point_cloud", v2);

	while (!viewer->wasStopped())
	{
		viewer->spinOnce(100);
		boost::this_thread::sleep(boost::posix_time::microseconds(100000));
	}
	//=====================================================================

	return 0;
}

可视化

1.展示一个窗口

(采用while循环)

2.直接在终端查看

pcl_viewer xxx.pcd

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340