文章目录
- 0.引言
- 1.CloudCompare界面设计采样(sample)按钮
- 2.RandomSample随机下采样
- 3.VoxelGrid体素下采样
- 4.UniformSampling均匀采样
- 5. MovingLeastSquares增采样
- 6.SamplingSurfaceNormal非均匀体素采样
0.引言
因笔者课题涉及点云处理,需要通过PCL进行点云数据一系列处理分析,查阅现有网络资料,对常用PCL点云采样进行代码实现,本文记录采样实现过程。
1.CloudCompare界面设计采样(sample)按钮
(1)设计.ui文件
①设计按钮
②编译.ui
(2)修改mainwindow.h文件
//点云采样
void doActionPCLRandomSample(); // 随机下采样
void doActionPCLVoxelGrid(); // 体素下采样
void doActionPCLUniformSampling(); // 均匀采样
void doActionPCLMovingLeastSquares(); // 增采样
void doActionPCLSamplingSurfaceNormal(); // 非均匀体素采样
(3)修改mainwindow.cpp文件
①添加头文件
#include <pcl/filters/random_sample.h>// 随机下采样
#include <pcl/filters/voxel_grid.h>// 体素下采样
#include <pcl/keypoints/uniform_sampling.h>// 均匀采样
#include <pcl/surface/mls.h>// 增采样
#include <pcl/filters/sampling_surface_normal.h>//非均匀体素采样
②添加实现代码
//随机下采样
void MainWindow::doActionPCLRandomSample()
{
}
//体素下采样
void MainWindow::doActionPCLVoxelGrid()
{
}
// 均匀采样
void MainWindow::doActionPCLUniformSampling()
{
}
//增采样
void MainWindow::doActionPCLMovingLeastSquares()
{
}
//非均匀体素采样
void MainWindow::doActionPCLSamplingSurfaceNormal()
{
}
③添加信号槽函数
connect(m_UI->actionRandomSample, &QAction::triggered, this, &MainWindow::doActionPCLRandomSample);//随机下采样
connect(m_UI->actionVoxelGrid_2, &QAction::triggered, this, &MainWindow::doActionPCLVoxelGrid);//体素下采样
connect(m_UI->actionUniformSampling_2, &QAction::triggered, this, &MainWindow::doActionPCLUniformSampling);//均匀采样
connect(m_UI->actionMovingLeastSquares, &QAction::triggered, this, &MainWindow::doActionPCLMovingLeastSquares);//增采样
connect(m_UI->actionSamplingSurfaceNormal, &QAction::triggered, this, &MainWindow::doActionPCLSamplingSurfaceNormal);//非均匀体素采样
(4)生成
2.RandomSample随机下采样
(1)实现代码
//随机下采样
void MainWindow::doActionPCLRandomSample()
{
if (getSelectedEntities().size() != 1)
{
ccLog::Print(QStringLiteral("只能选择一个点云实体"));
return;
}
ccHObject* entity = getSelectedEntities()[0];
ccPointCloud* ccCloud = ccHObjectCaster::ToPointCloud(entity);
// ---------------------------读取数据到PCL----------------------------------
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->resize(ccCloud->size());
for (int i = 0; i < cloud->size(); ++i)
{
const CCVector3* point = ccCloud->getPoint(i);
cloud->points[i].x = point->x;
cloud->points[i].y = point->y;
cloud->points[i].z = point->z;
}
// -----------------------------对话框---------------------------------------
float radius = QInputDialog::getDouble(this, QStringLiteral("参数设置"), QStringLiteral("采样比例: "), 50, 0, 100, 4);
// ----------------------------随机下采样--------------------------------------
pcl::PointCloud<pcl::PointXYZ>::Ptr filtered(new pcl::PointCloud<pcl::PointXYZ>);
pcl::RandomSample<pcl::PointXYZ> us;
us.setInputCloud(cloud);
us.setSample(cloud->size()*radius/100.0);
us.filter(*filtered);
// ------------------------PCL->CloudCompare--------------------------------
if (!filtered->empty())
{
ccPointCloud* newPointCloud = new ccPointCloud(QString("RandomSample"));
for (int i = 0; i < filtered->size(); ++i)
{
double x = filtered->points[i].x;
double y = filtered->points[i].y;
double z = filtered->points[i].z;
newPointCloud->addPoint(CCVector3(x, y, z));
}
newPointCloud->setRGBColor(ccColor::Rgba(255, 255, 255, 255));
newPointCloud->showColors(true);
if (ccCloud->getParent())
{
ccCloud->getParent()->addChild(newPointCloud);
}
ccCloud->setEnabled(false);
addToDB(newPointCloud);
refreshAll();
updateUI();
}
else
{
ccCloud->setEnabled(true);
// Display a warning message in the console
dispToConsole("Warning: example shouldn't be used as is", ccMainAppInterface::WRN_CONSOLE_MESSAGE);
}
}
(2)采样结果
①采样前
②采样后
3.VoxelGrid体素下采样
(1)实现代码
// 体素滤波器
void MainWindow::doActionPCLVoxelGrid()
{
if (getSelectedEntities().size() != 1)
{
ccLog::Print(QStringLiteral("只能选择一个点云实体"));
return;
}
ccHObject* entity = getSelectedEntities()[0];
ccPointCloud* ccCloud = ccHObjectCaster::ToPointCloud(entity);
// ---------------------------读取数据到PCL----------------------------------
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->resize(ccCloud->size());
for (int i = 0; i < cloud->size(); ++i)
{
const CCVector3* point = ccCloud->getPoint(i);
cloud->points[i].x = point->x;
cloud->points[i].y = point->y;
cloud->points[i].z = point->z;
}
// -----------------------------对话框---------------------------------------
float radius = QInputDialog::getDouble(this, QStringLiteral("参数设置"), QStringLiteral("体素大小: "), 0.1, 0, 100, 4);
// ----------------------------体素下采样-------------------------------------
pcl::PointCloud<pcl::PointXYZ>::Ptr filtered(new pcl::PointCloud<pcl::PointXYZ>);
pcl::VoxelGrid<pcl::PointXYZ> us;
us.setInputCloud(cloud);
us.setLeafSize(radius, radius, radius);
us.filter(*filtered);
// ------------------------PCL->CloudCompare--------------------------------
if (!filtered->empty())
{
ccPointCloud* newPointCloud = new ccPointCloud(QString("VoxelGrid"));
for (int i = 0; i < filtered->size(); ++i)
{
double x = filtered->points[i].x;
double y = filtered->points[i].y;
double z = filtered->points[i].z;
newPointCloud->addPoint(CCVector3(x, y, z));
}
newPointCloud->setRGBColor(ccColor::Rgba(255, 255, 255, 255));
newPointCloud->showColors(true);
if (ccCloud->getParent())
{
ccCloud->getParent()->addChild(newPointCloud);
}
ccCloud->setEnabled(false);
addToDB(newPointCloud);
refreshAll();
updateUI();
}
else
{
ccCloud->setEnabled(true);
// Display a warning message in the console
dispToConsole("Warning: example shouldn't be used as is", ccMainAppInterface::WRN_CONSOLE_MESSAGE);
}
}
(2)采样结果
①采样前
②采样后
4.UniformSampling均匀采样
(1)实现代码
// 均匀采样
void MainWindow::doActionPCLUniformSampling()
{
if (getSelectedEntities().size() != 1)
{
ccLog::Print(QStringLiteral("只能选择一个点云实体"));
return;
}
ccHObject* entity = getSelectedEntities()[0];
ccPointCloud* ccCloud = ccHObjectCaster::ToPointCloud(entity);
// ---------------------------读取数据到PCL----------------------------------
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->resize(ccCloud->size());
for (int i = 0; i < cloud->size(); ++i)
{
const CCVector3* point = ccCloud->getPoint(i);
cloud->points[i].x = point->x;
cloud->points[i].y = point->y;
cloud->points[i].z = point->z;
}
// -----------------------------对话框---------------------------------------
float radius = QInputDialog::getDouble(this, QStringLiteral("参数设置"), QStringLiteral("搜索半径: "), 0.1, 0, 100, 4);
// ----------------------------均匀采样--------------------------------------
pcl::PointCloud<pcl::PointXYZ>::Ptr filtered(new pcl::PointCloud<pcl::PointXYZ>);
pcl::UniformSampling<pcl::PointXYZ> us;
us.setInputCloud(cloud);
us.setRadiusSearch(radius);
us.filter(*filtered);
// ------------------------PCL->CloudCompare--------------------------------
if (!filtered->empty())
{
ccPointCloud* newPointCloud = new ccPointCloud(QString("UniformSampling"));
for (int i = 0; i < filtered->size(); ++i)
{
double x = filtered->points[i].x;
double y = filtered->points[i].y;
double z = filtered->points[i].z;
newPointCloud->addPoint(CCVector3(x, y, z));
}
newPointCloud->setRGBColor(ccColor::Rgba(255, 255, 255, 255));
newPointCloud->showColors(true);
if (ccCloud->getParent())
{
ccCloud->getParent()->addChild(newPointCloud);
}
ccCloud->setEnabled(false);
addToDB(newPointCloud);
refreshAll();
updateUI();
}
else
{
ccCloud->setEnabled(true);
// Display a warning message in the console
dispToConsole("Warning: example shouldn't be used as is", ccMainAppInterface::WRN_CONSOLE_MESSAGE);
}
}
(2)采样结果
①采样前
②采样后
5. MovingLeastSquares增采样
(1)实现代码
//增采样
void MainWindow::doActionPCLMovingLeastSquares()
{
if (getSelectedEntities().size() != 1)
{
ccLog::Print(QStringLiteral("只能选择一个点云实体"));
return;
}
ccHObject* entity = getSelectedEntities()[0];
ccPointCloud* ccCloud = ccHObjectCaster::ToPointCloud(entity);
// ---------------------------读取数据到PCL----------------------------------
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->resize(ccCloud->size());
for (int i = 0; i < cloud->size(); ++i)
{
const CCVector3* point = ccCloud->getPoint(i);
cloud->points[i].x = point->x;
cloud->points[i].y = point->y;
cloud->points[i].z = point->z;
}
// -----------------------------对话框---------------------------------------
float radius = QInputDialog::getDouble(this, QStringLiteral("参数设置"), QStringLiteral("搜索邻域的半径: "), 0.03, 0, 100, 4);
// ----------------------------增采样--------------------------------------
pcl::PointCloud<pcl::PointXYZ>::Ptr filtered(new pcl::PointCloud<pcl::PointXYZ>);
pcl::MovingLeastSquares<pcl::PointXYZ, pcl::PointXYZ> us;
us.setInputCloud(cloud);
pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree; //定义搜索方法
us.setSearchMethod(kdtree); //设置搜索方法
us.setSearchRadius(radius); //设置搜索邻域的半径
us.setUpsamplingMethod(pcl::MovingLeastSquares<pcl::PointXYZ, pcl::PointXYZ>::SAMPLE_LOCAL_PLANE); //对点云进行上采样
us.setUpsamplingRadius(0.03); //设置采样半径大小,3cm
us.setUpsamplingStepSize(0.02); //设置采样步长大小,2cm
us.process(*filtered);
// ------------------------PCL->CloudCompare--------------------------------
if (!filtered->empty())
{
ccPointCloud* newPointCloud = new ccPointCloud(QString("MovingLeastSquares"));
for (int i = 0; i < filtered->size(); ++i)
{
double x = filtered->points[i].x;
double y = filtered->points[i].y;
double z = filtered->points[i].z;
newPointCloud->addPoint(CCVector3(x, y, z));
}
newPointCloud->setRGBColor(ccColor::Rgba(255, 255, 255, 255));
newPointCloud->showColors(true);
if (ccCloud->getParent())
{
ccCloud->getParent()->addChild(newPointCloud);
}
ccCloud->setEnabled(false);
addToDB(newPointCloud);
refreshAll();
updateUI();
}
else
{
ccCloud->setEnabled(true);
// Display a warning message in the console
dispToConsole("Warning: example shouldn't be used as is", ccMainAppInterface::WRN_CONSOLE_MESSAGE);
}
}
(2)采样结果
①采样前
②采样后
6.SamplingSurfaceNormal非均匀体素采样
(1)实现代码
//非均匀体素采样
void MainWindow::doActionPCLSamplingSurfaceNormal()
{
if (getSelectedEntities().size() != 1)
{
ccLog::Print(QStringLiteral("只能选择一个点云实体"));
return;
}
ccHObject* entity = getSelectedEntities()[0];
ccPointCloud* ccCloud = ccHObjectCaster::ToPointCloud(entity);
// ---------------------------读取数据到PCL----------------------------------
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->resize(ccCloud->size());
pcl::PointCloud<pcl::PointNormal>::Ptr incloud(new pcl::PointCloud <pcl::PointNormal>());
for (int i = 0; i < cloud->size(); ++i)
{
const CCVector3* point = ccCloud->getPoint(i);
cloud->points[i].x = point->x;
cloud->points[i].y = point->y;
cloud->points[i].z = point->z;
pcl::PointNormal pt;
pt.x = point->x;
pt.y = point->y;
pt.z = point->z;
incloud->points.push_back(pt);
}
// -----------------------------对话框---------------------------------------
float radius = QInputDialog::getDouble(this, QStringLiteral("参数设置"), QStringLiteral("采样比例: "),0.3, 0, 100, 4);
incloud->width = 1;
incloud->height = uint32_t(incloud->points.size());
// ----------------------------随机下采样--------------------------------------
pcl::PointCloud<pcl::PointNormal>::Ptr filtered(new pcl::PointCloud<pcl::PointNormal>);
pcl::SamplingSurfaceNormal<pcl::PointNormal> us;
us.setInputCloud(incloud);
us.setRatio(radius);
us.filter(*filtered);
// ------------------------PCL->CloudCompare--------------------------------
if (!filtered->empty())
{
ccPointCloud* newPointCloud = new ccPointCloud(QString("SamplingSurfaceNormal"));
for (int i = 0; i < filtered->size(); ++i)
{
double x = filtered->points[i].x;
double y = filtered->points[i].y;
double z = filtered->points[i].z;
newPointCloud->addPoint(CCVector3(x, y, z));
}
newPointCloud->setRGBColor(ccColor::Rgba(255, 255, 255, 255));
newPointCloud->showColors(true);
if (ccCloud->getParent())
{
ccCloud->getParent()->addChild(newPointCloud);
}
ccCloud->setEnabled(false);
addToDB(newPointCloud);
refreshAll();
updateUI();
}
else
{
ccCloud->setEnabled(true);
// Display a warning message in the console
dispToConsole("Warning: example shouldn't be used as is", ccMainAppInterface::WRN_CONSOLE_MESSAGE);
}
}
(2)采样结果
①采样前
②采样后
参考资料:
[1] 来吧!我在未来等你!. CloudCompare二次开发之如何配置PCL点云库?; 2023-05-15 [accessed 2023-05-16].
[2] 自动驾驶之心. 聊一聊点云PCL中常用的高级采样方法; 2023-01-17 [accessed 2023-05-16].
[3] 悠缘之空. PCL函数库摘要——采样一致性; 2021-11-07 [accessed 2023-05-16].
[4] 陈三章. PCL学习笔记——点云曲面重建(一); 2019-04-03 [accessed 2023-05-16].