文章目录

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, &amp;QAction::triggered, this, &amp;MainWindow::doActionPCLRandomSample);//随机下采样
connect(m_UI->actionVoxelGrid_2, &amp;QAction::triggered, this, &amp;MainWindow::doActionPCLVoxelGrid);//体素下采样  
connect(m_UI->actionUniformSampling_2, &amp;QAction::triggered, this, &amp;MainWindow::doActionPCLUniformSampling);//均匀采样  
connect(m_UI->actionMovingLeastSquares, &amp;QAction::triggered, this, &amp;MainWindow::doActionPCLMovingLeastSquares);//增采样  
connect(m_UI->actionSamplingSurfaceNormal, &amp;QAction::triggered, this, &amp;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].