点云滤波方法记录

本片博客的滤波方式引用https://blog.csdn.net/qq_34719188/article/details/79179430该网址的博主内容。

1.本人主要用SICK扫描仪做管道对接课题,前期针对于测量采集的txt数据转换为pcd文件格式:代码如下

点云数据txt转pcd格式
#include "pch.h"
#include<iostream> 
#include<fstream>
#include<vector>
#include<string>
#include<pcl\io\pcd_io.h>
#include<pcl\point_types.h>
using namespace std;
int main()
{	//定义一种类型表示TXT中xyz	
	typedef struct TXT_Point_XYZ	
	{	
		double x;		
	    double y;		
	    double z;	
	}TOPOINT_XYZ; 	
	//读取txt文件	
	int num_txt;	
	FILE *fp_txt;	
	TXT_Point_XYZ txt_points;	
	vector<TXT_Point_XYZ> my_vTxtPoints;	
	fp_txt = fopen("C://Users//HEHE//Desktop//Chair.txt","r"); 	
	if (fp_txt)	
	{		
		while (fscanf(fp_txt, "%lf %lf %lf", &txt_points.x, &txt_points.y, &txt_points.z) != EOF)		
		{//将点存入容器尾部			
			my_vTxtPoints.push_back(txt_points);		
		}	
	}	
	else		
		cout << "读取txt文件失败"<<endl; 	
	num_txt = my_vTxtPoints.size(); 	
	//写入点云数据	
	pcl::PointCloud<pcl::PointXYZ> ::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);	
	cloud->width = num_txt;	
	cloud->height = 1;	
	cloud->is_dense = false;	
	cloud->points.resize(cloud->width*cloud->height);	
	for (int i = 0; i < cloud->points.size(); ++i)	
	{		
		cloud->points[i].x = my_vTxtPoints[i].x;		
		cloud->points[i].y = my_vTxtPoints[i].y;		
		cloud->points[i].z = my_vTxtPoints[i].z;	
	}	
	pcl::io::savePCDFileASCII("C://Users//HEHE//Desktop//Chair.pcd", *cloud);	
	cout<< "从 Chair.txt读取" << cloud->points.size() << "点写入Chair.pcd" << endl;		
	//打印出写入的点    cout << "_________________________________" << endl;	
	for (size_t i = 0; i < cloud->points.size(); ++i)		
		cout << "    " << cloud->points[i].x		
		<< " " << cloud->points[i].y		
		<< " " << cloud->points[i].z << endl; 	
	return 0;
}

如下左边为txt格式数据,右边为转化的pcd文件格式。

2.针对于转化的pcd格式文件再进行滤波处理,在滤波的算法选择上,个人觉得针对于自己的数据采集区域不同,要达到的目的不一样,所选择的滤波算法也应该不一样,但是在滤波算法选择上还是挺迷茫的,希望有大神能指导一下(QQ:1753939345)

       第一种滤波算法:VoxelGrid滤波器对点云进行下采样,(使用体素化网格方法实现下采样,即减少点的数量 减少点云数据,并同时保存点云的形状特征,在提高配准,曲面重建,形状识别等算法速度中非常实用,PCL是实现的VoxelGrid类通过输入的点云数据创建一个三维体素栅格,容纳后每个体素内用体素中所有点的重心来近似显示体素中其他点,这样该体素内所有点都用一个重心点最终表示,对于所有体素处理后得到的过滤后的点云,这种方法比用体素中心(注意中心重心)逼近的方法更慢,但是对于采样点对应曲面的表示更为准确。)

#include "pch.h"
#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());

	//点云对象的读取
	pcl::PCDReader reader;

	reader.read("C://Users//HEHE//Desktop//Chair.pcd", *cloud);    //读取点云到cloud中

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

	/******************************************************************************
	创建一个叶大小为1cm的pcl::VoxelGrid滤波器,
  **********************************************************************************/
	pcl::VoxelGrid<pcl::PCLPointCloud2> sor;  //创建滤波对象
	sor.setInputCloud(cloud);            //设置需要过滤的点云给滤波对象
	//sor.setLeafSize(0.01f, 0.01f, 0.01f);  //设置滤波时创建的体素体积为1cm的立方体
	sor.setLeafSize(10.0f, 10.0f, 10.0f);//该部分如果选择的体素体积太小,就会造成索引溢
//出,所以本人改大点。
	sor.filter(*cloud_filtered);           //执行滤波处理,存储输出

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

	pcl::PCDWriter writer;
	writer.write("C://Users//HEHE//Desktop//Chair_downsampled.pcd", *cloud_filtered,
		Eigen::Vector4f::Zero(), Eigen::Quaternionf::Identity(), false);

	return (0);
}

左边图形是Chair的原采集数据,右边是经过滤波降采样之后的数据,本人是将保存的数据分别导入进CouldCompare进行查看的:

        第2中滤波算法:statisticalOutlierRemoval滤波器移除离群点(**问题描述:**激光扫描通常会产生密度不均匀的点云数据集,另外测量中的误差也会产生稀疏的离群点,此时,估计局部点云特征(例如采样点处法向量或曲率变化率)时运算复杂,这会导致错误的数值,反过来就会导致点云配准等后期的处理失败。)(**解决办法:**对每个点的邻域进行一个统计分析,并修剪掉一些不符合标准的点。具体方法为在输入数据中对点到临近点的距离分布的计算,对每一个点,计算它到所有临近点的平均距离(假设得到的结果是一个高斯分布,其形状是由均值和标准差决定),那么平均距离在标准范围之外的点,可以被定义为离群点并从数据中去除。)代码如下:

//statisticalOutlierRemoval滤波器移除离群点
#include "pch.h"
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/statistical_outlier_removal.h>

int
main(int argc, char** argv)
{
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);

	// 定义读取对象
	pcl::PCDReader reader;
	// 读取点云文件
	reader.read<pcl::PointXYZ>("C://Users//HEHE//Desktop//Chair.pcd", *cloud);

	std::cerr << "Cloud before filtering: " << std::endl;
	std::cerr << *cloud << std::endl;

	// 创建滤波器,对每个点分析的临近点的个数设置为50 ,并将标准差的倍数设置为1  这意味着如果一
	 //个点的距离超出了平均距离一个标准差以上,则该点被标记为离群点,并将它移除,存储起来
	pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;   //创建滤波器对象
	sor.setInputCloud(cloud);                           //设置待滤波的点云
	sor.setMeanK(50);                               //设置在进行统计时考虑查询点临近点数
	sor.setStddevMulThresh(1.0);                      //设置判断是否为离群点的阀值
	sor.filter(*cloud_filtered);                    //存储

	std::cerr << "Cloud after filtering: " << std::endl;
	std::cerr << *cloud_filtered << std::endl;

	pcl::PCDWriter writer;
	writer.write<pcl::PointXYZ>("C://Users//HEHE//Desktop//Chair_inliers.pcd", *cloud_filtered, false);

	sor.setNegative(true);
	sor.filter(*cloud_filtered);
	writer.write<pcl::PointXYZ>("C://Users//HEHE//Desktop//Chair_outliers.pcd", *cloud_filtered, false);

	return (0);
}

在运行该代码时可能会出现一个错误:

解决方案如下:https://github.com/mariusmuja/flann/issues/386

找到你自己的dist.h文件,修改方案为:将该条语句放置在#if之前即可。

In order to fix the issues and get my program and the PCL libraries working correctly I had to move the "typedef unsigned long long pop_t;" outside the #if and #else so that either one will use the parameter to do the math. I don't know if this was the intent of this code but in order to progress in my software application I needed this error fixed. Please fix this as soon as possible and let me know so I can just update the flann and not worry about it.

上述代码运行结果:

    第3:从一个点云中提取索引,基于某一分割算法提取点云中的一个子集:

//基于某一分割算法提取点云中的一个子集
#include "pch.h"
#include <iostream>
#include <pcl/ModelCoefficients.h>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/sample_consensus/method_types.h>
#include <pcl/sample_consensus/model_types.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/filters/extract_indices.h>

int
main(int argc, char** argv)
{

	/**********************************************************************************************************
	 从输入的.PCD 文件载入数据后,创建一个VOxelGrid滤波器对数据进行下采样,在这里进行下才样是为了加速处理过程,
	 越少的点意味着分割循环中处理起来越快
	 **********************************************************************************************************/

	pcl::PCLPointCloud2::Ptr cloud_blob(new pcl::PCLPointCloud2), cloud_filtered_blob(new pcl::PCLPointCloud2);//申明滤波前后的点云
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>), cloud_p(new pcl::PointCloud<pcl::PointXYZ>), cloud_f(new pcl::PointCloud<pcl::PointXYZ>);

	// 读取PCD文件
	pcl::PCDReader reader;
	reader.read("C://Users//HEHE//Desktop//Chair.pcd", *cloud_blob);
	//统计滤波前的点云个数
	std::cerr << "PointCloud before filtering: " << cloud_blob->width * cloud_blob->height << " data points." << std::endl;

	// 创建体素栅格下采样: 下采样的大小为1cm
	pcl::VoxelGrid<pcl::PCLPointCloud2> sor;  //体素栅格下采样对象
	sor.setInputCloud(cloud_blob);             //原始点云
	//sor.setLeafSize(0.01f, 0.01f, 0.01f);    // 设置采样体素大小
	sor.setLeafSize(8.0f, 8.0f, 8.0f);    // 设置采样体素大小
	sor.filter(*cloud_filtered_blob);        //保存

	// 转换为模板点云
	pcl::fromPCLPointCloud2(*cloud_filtered_blob, *cloud_filtered);

	std::cerr << "PointCloud after filtering: " << cloud_filtered->width * cloud_filtered->height << " data points." << std::endl;

	// 保存下采样后的点云
	pcl::PCDWriter writer;
	writer.write<pcl::PointXYZ>("C://Users//HEHE//Desktop//CCChair_downsampled.pcd", *cloud_filtered, false);

	pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients());
	pcl::PointIndices::Ptr inliers(new pcl::PointIndices());

	pcl::SACSegmentation<pcl::PointXYZ> seg;               //创建分割对象

	seg.setOptimizeCoefficients(true);                    //设置对估计模型参数进行优化处理

	seg.setModelType(pcl::SACMODEL_PLANE);                //设置分割模型类别
	seg.setMethodType(pcl::SAC_RANSAC);                   //设置用哪个随机参数估计方法
	seg.setMaxIterations(1000);                            //设置最大迭代次数
	seg.setDistanceThreshold(0.01);                      //判断是否为模型内点的距离阀值

	// 设置ExtractIndices的实际参数
	pcl::ExtractIndices<pcl::PointXYZ> extract;        //创建点云提取对象

	int i = 0, nr_points = (int)cloud_filtered->points.size();
	// While 30% of the original cloud is still there
	while (cloud_filtered->points.size() > 0.3 * nr_points)
	{
		// 为了处理点云包含的多个模型,在一个循环中执行该过程并在每次模型被提取后,保存剩余的点进行迭代
		seg.setInputCloud(cloud_filtered);
		seg.segment(*inliers, *coefficients);
		if (inliers->indices.size() == 0)
		{
			std::cerr << "Could not estimate a planar model for the given dataset." << std::endl;
			break;
		}

		// Extract the inliers
		extract.setInputCloud(cloud_filtered);
		extract.setIndices(inliers);
		extract.setNegative(false);
		extract.filter(*cloud_p);
		std::cerr << "PointCloud representing the planar component: " << cloud_p->width * cloud_p->height << " data points." << std::endl;

		std::stringstream ss;
		ss << "table_scene_lms400_plane_" << i << ".pcd";
		writer.write<pcl::PointXYZ>(ss.str(), *cloud_p, false);

		// Create the filtering object
		extract.setNegative(true);
		extract.filter(*cloud_f);
		cloud_filtered.swap(cloud_f);
		i++;
	}

	return (0);
}

程序运行截图:

左边对应原始图,右边对应提取图。

发布了28 篇原创文章 · 获赞 4 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/m0_37957160/article/details/98587673