此例利用随机采样一致性算法对有噪声的点云场景提取一个圆柱体模型,处理流程如下:
(1)过滤掉远与1.5m的数据点
(2)计算每个点的表面法线
(3)分割出平面模型(本例中为桌面)并保存到磁盘中
(4)分割出圆柱体模型并保存到磁盘中
PCL法线估计:
平面的法线是垂直于它的单位向量。在点云的表面的法线被定义为垂直于与点云表面相切的平面的向量。表面法线也可以计算点云中一点的法线,被认为是一种十分重要的性质。常常在被使用在很多计算机视觉的应用里面,比如可以用来推出光源的位置,通过阴影与其他视觉影响,表面法线的问题可以近似化解为切面的问题,这个切面的问题又会变成最小二乘法拟合平面的问题。
解决表面法线估计的问题可以最终化简为对一个协方差矩阵的特征向量和特征值的分析(或者也叫PCA-Principal Component Analysis 主成分分析),这个协方差矩阵是由查询点的最近邻产生的。对于每个点Pi,我们假设协方差矩阵C如下:
C++中声明对象和NEW对象的区别:
new出来的对象是直接放在堆上,而声明一个对象是放在栈中。换句话说,new出来的对象的生命周期是全局的,譬如在一个函数块里new一个对象,可以将该对象的指针返回回去,该对象依旧存在。而声明的对象的生命周期只存在于声明了该对象的函数块中,如果返回该声明的对象,将会返回一个已经被销毁的对象。
参考博客:https://blog.csdn.net/city_to_sky/article/details/79876992
FPS每秒纯属帧率(Frames Per Second):
FPS是图像领域中的定义,是指画面每秒传输帧数,通俗来讲就是指动画或视频的画面数。FPS是测量用于保存、显示动态视频的信息数量。每秒钟帧数愈多,所显示的动作就会越流畅。通常,要避免动作不流畅的最低是30。某些计算机视频格式,每秒只能提供15帧。
FPS”也可以理解为我们常说的“刷新率(单位为Hz)”,例如我们常在CS游戏里说的“FPS值”。我们在装机选购显卡和显示器的时候,都会注意到“刷新率”。一般我们设置缺省刷新率都在75Hz(即75帧/秒)以上。例如:75Hz的刷新率刷也就是指屏幕一秒内只扫描75次,即75帧/秒。而当刷新率太低时我们肉眼都能感觉到屏幕的闪烁,不连贯,对图像显示效果和视觉感观产生不好的影响。
电影以每秒24张画面的速度播放,也就是一秒钟内在屏幕上连续投射出24张静止画面。有关动画播放速度的单位是fps,其中的f就是英文单词Frame(画面、帧),p就是Per(每),s就是Second(秒)。用中文表达就是多少帧每秒,或每秒多少帧。电影是24fps,通常简称为24帧。
调试过程中出现的问题:
1.当前不会命中断点 还没有为该文档加载任何符号
这因为编译器生成的符号文件与源代码不同。将工具->选项->调试中的要求源文件与原始版本完全匹配选项取消。清除项目并重新生成再运行。
2.读取文件失败
由于电脑处理速度有限,加上点云数据很大,因此加载时间会很长,并不是读取有问题,耐心等待即可。
程序代码:
// RANSAC.cpp: 定义控制台应用程序的入口点。
#include "stdafx.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace pcl;
typedef pcl::PointXYZ PointT;
void viewerOneOff(visualization::PCLVisualizer& viewer) {
viewer.setBackgroundColor(0, 0, 0); //设置背景颜色为黑色
}
int
main(int argc, char** argv)
{
// All the objects needed
pcl::PCDReader reader; //PCD文件读取对象
pcl::PassThrough
pcl::NormalEstimation
pcl::SACSegmentationFromNormals
pcl::PCDWriter writer; //PCD文件读取对象
pcl::ExtractIndices
pcl::ExtractIndices
pcl::search::KdTree
// Datasets
pcl::PointCloud
pcl::PointCloud
pcl::PointCloud
pcl::PointCloud
pcl::PointCloud
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);
// Read in the cloud data
reader.read
if (-1 == pcl::io::loadPCDFile
{
return -1;
}
visualization::CloudViewer viewer("Cloud Viewer: Scene"); //创建viewer对象
viewer.showCloud(cloud);
viewer.runOnVisualizationThreadOnce(viewerOneOff);
std::cerr << "PointCloud has: " << cloud->points.size() << " data points." << std::endl;
// 直通滤波,将Z轴不在(0,1.5)范围的点过滤掉,将剩余的点存储到cloud_filtered对象中
pass.setInputCloud(cloud);
pass.setFilterFieldName("z");
pass.setFilterLimits(0, 1.5);
pass.filter(*cloud_filtered);
std::cerr << "PointCloud after filtering has: " << cloud_filtered->points.size() << " data points." << std::endl;
// 过滤后的点云进行法线估计,为后续进行基于法线的分割准备数据
ne.setSearchMethod(tree);
ne.setInputCloud(cloud_filtered);
ne.setKSearch(50);
ne.compute(*cloud_normals);
// 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);
seg.setMaxIterations(100);
seg.setDistanceThreshold(0.03);
seg.setInputCloud(cloud_filtered);
seg.setInputNormals(cloud_normals);
//获取平面模型的系数和处在平面的内点
seg.segment(*inliers_plane, *coefficients_plane);
std::cerr << "Plane coefficients: " << *coefficients_plane << std::endl;
// 从点云中抽取分割的处在平面上的点集
extract.setInputCloud(cloud_filtered);
extract.setIndices(inliers_plane);
extract.setNegative(false);
// 存储分割得到的平面上的点到点云文件
pcl::PointCloud
extract.filter(*cloud_plane);
std::cerr << "PointCloud representing the planar component: " << cloud_plane->points.size() << " data points." << std::endl;
writer.write("table_scene_mug_stereo_textured_plane.pcd", *cloud_plane, false);
if (-1 == pcl::io::loadPCDFile
{
return -1;
}
visualization::CloudViewer viewer_plane("Cloud Viewer:Plane"); //创建viewer对象
viewer_plane.showCloud(cloud_plane);
viewer_plane.runOnVisualizationThreadOnce(viewerOneOff);
// Remove the planar inliers, extract the rest
extract.setNegative(true);
extract.filter(*cloud_filtered2);
extract_normals.setNegative(true);
extract_normals.setInputCloud(cloud_normals);
extract_normals.setIndices(inliers_plane);
extract_normals.filter(*cloud_normals2);
// Create the segmentation object for cylinder segmentation and set all the parameters
seg.setOptimizeCoefficients(true); //设置对估计模型优化
seg.setModelType(pcl::SACMODEL_CYLINDER); //设置分割模型为圆柱形
seg.setMethodType(pcl::SAC_RANSAC); //参数估计方法
seg.setNormalDistanceWeight(0.1); //设置表面法线权重系数
seg.setMaxIterations(10000); //设置迭代的最大次数10000
seg.setDistanceThreshold(0.05); //设置内点到模型的距离允许最大值
seg.setRadiusLimits(0, 0.1); //设置估计出的圆柱模型的半径的范围
seg.setInputCloud(cloud_filtered2);
seg.setInputNormals(cloud_normals2);
// Obtain the cylinder inliers and coefficients
seg.segment(*inliers_cylinder, *coefficients_cylinder);
std::cerr << "Cylinder coefficients: " << *coefficients_cylinder << std::endl;
// Write the cylinder inliers to disk
extract.setInputCloud(cloud_filtered2);
extract.setIndices(inliers_cylinder);
extract.setNegative(false);
pcl::PointCloud
extract.filter(*cloud_cylinder);
if (cloud_cylinder->points.empty())
std::cerr << "Can't find the cylindrical component." << std::endl;
else
{
std::cerr << "PointCloud representing the cylindrical component: " << cloud_cylinder->points.size() << " data points." << std::endl;
writer.write("table_scene_mug_stereo_textured_cylinder.pcd", *cloud_cylinder, false);
}
if (-1 == pcl::io::loadPCDFile
{
return -1;
}
visualization::CloudViewer viewer_cylinder("Cloud Viewer:Cylinder"); //创建viewer对象
viewer_cylinder.showCloud(cloud_cylinder);
viewer_cylinder.runOnVisualizationThreadOnce(viewerOneOff);
system("pause");
return (0);
}
运行结果:
数据输出:
点云窗口——处理前场景:
点云窗口——分割的平面:
点云窗口——分割的圆柱体: