CloudCompare二次开发之如何通过PCL进行点云曲面重建?

文章目录

0.引言
1.CloudCompare界面设计重建(reconstruct)按钮
2.使用贪婪三角化进行曲面重建(Surface_Rec)

0.引言

  因笔者课题涉及点云处理,需要通过PCL进行点云数据一系列处理分析,查阅现有网络资料,对常用PCL点云曲面重建进行代码实现,本文记录曲面重建实现过程。

1.CloudCompare界面设计重建(reconstruct)按钮

  (1)设计.ui文件

  ①设计按钮

②编译.ui

(2)修改mainwindow.h文件

//点云重建
void doActionPCLSurface_Rec(); // 使用贪婪三角化进行曲面重建

 (3)修改mainwindow.cpp文件

  ①添加头文件

    #include <pcl/filters/radius_outlier_removal.h>
    #include <pcl/filters/voxel_grid.h>  
    #include <pcl/filters/statistical_outlier_removal.h>  
    #include <pcl/surface/mls.h>  
    #include <pcl/features/normal_3d.h>  
    #include <pcl/surface/gp3.h>

②添加实现代码

//使用贪婪三角化进行曲面重建
void MainWindow::doActionPCLSurface_Rec()  
{  
}


③添加信号槽函数

onnect(m_UI->actionSurface_Rec, &amp;QAction::triggered, this, &amp;MainWindow::doActionPCLSurface_Rec);//使用贪婪三角化进行曲面重建

(4)生成


2.使用贪婪三角化进行曲面重建(Surface_Rec)

  (1)实现代码

typedef pcl::PointXYZ PointT;
//使用贪婪三角化进行曲面重建  
void MainWindow::doActionPCLSurface_Rec() {  
    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;  
    }  
    pcl::PointCloud<PointT>::Ptr cloud_downSampled(new pcl::PointCloud<PointT>);  
    pcl::PointCloud<PointT>::Ptr cloud_filtered(new pcl::PointCloud<PointT>);  
    pcl::PointCloud<PointT>::Ptr cloud_smoothed(new pcl::PointCloud<PointT>);  
    // 下采样,同时保持点云形状特征  
    pcl::VoxelGrid<PointT> downSampled;  //创建滤波对象  
    downSampled.setInputCloud(cloud);            //设置需要过滤的点云给滤波对象  
    downSampled.setLeafSize(0.01f, 0.01f, 0.01f);  //设置滤波时创建的体素体积为1cm的立方体  
    downSampled.filter(*cloud_downSampled);           //执行滤波处理,存储输出  
    // 统计滤波  
    pcl::StatisticalOutlierRemoval<PointT> statisOutlierRemoval;       //创建滤波器对象  
    statisOutlierRemoval.setInputCloud(cloud_downSampled);            //设置待滤波的点云  
    statisOutlierRemoval.setMeanK(50);                                //设置在进行统计时考虑查询点临近点数  
    statisOutlierRemoval.setStddevMulThresh(3.0);                     //设置判断是否为离群点的阀值:均值+1.0*标准差  
    statisOutlierRemoval.filter(*cloud_filtered);                     //滤波结果存储到cloud_filtered  
     // 对点云重采样  
    pcl::search::KdTree<PointT>::Ptr treeSampling(new pcl::search::KdTree<PointT>);  
    pcl::PointCloud<PointT> mls_point;  
    pcl::MovingLeastSquares<PointT, PointT> mls;  
    mls.setComputeNormals(false);  
    mls.setInputCloud(cloud_filtered);  
    mls.setPolynomialOrder(2);  
    mls.setPolynomialFit(false);  
    mls.setSearchMethod(treeSampling);  
    mls.setSearchRadius(0.05);  
    mls.process(mls_point);  
    // 输出重采样结果  
    cloud_smoothed = mls_point.makeShared();  
    // 法线估计  
    pcl::NormalEstimation<PointT, pcl::Normal> normalEstimation;  
    normalEstimation.setInputCloud(cloud_smoothed);  
    pcl::search::KdTree<PointT>::Ptr tree(new pcl::search::KdTree<PointT>);  
    normalEstimation.setSearchMethod(tree);  
    pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);  
    normalEstimation.setKSearch(10);  
    normalEstimation.compute(*normals);  
    //Triangle Projection  
    pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals(new pcl::PointCloud<pcl::PointNormal>);  
    pcl::concatenateFields(*cloud_smoothed, *normals, *cloud_with_normals);  
    pcl::search::KdTree<pcl::PointNormal>::Ptr tree2(new pcl::search::KdTree<pcl::PointNormal>);  
    tree2->setInputCloud(cloud_with_normals);  
    pcl::GreedyProjectionTriangulation<pcl::PointNormal> gp3;  
    pcl::PolygonMesh triangles;  
    gp3.setSearchRadius(0.1);  
    gp3.setMu(2.5);  
    gp3.setMaximumNearestNeighbors(52);  
    gp3.setMaximumAngle(2 * M_PI / 3);  
    gp3.setMinimumAngle(M_PI / 18);  
    gp3.setMaximumSurfaceAngle(M_PI / 4);  
    gp3.setNormalConsistency(false);  
    gp3.setInputCloud(cloud_with_normals);  
    gp3.setSearchMethod(tree2);  
    gp3.reconstruct(triangles);  
    ccPointCloud* newPointCloud = new ccPointCloud(QString("reconstruct_points"));  
    for (int i = 0; i < cloud_with_normals->size(); ++i)  
    {  
        double x = cloud_with_normals->points[i].x;  
        double y = cloud_with_normals->points[i].y;  
        double z = cloud_with_normals->points[i].z;  
        newPointCloud->addPoint(CCVector3(x, y, z));  
    }  
    //创建一个Mesh网格  
    ccMesh* mesh = new ccMesh(newPointCloud);  
    mesh->setName(ccCloud->getName() + "-mesh");  
    unsigned int v1, v2, v3;                //用于接收顶点  
    for (int i = 0; i < triangles.polygons.size(); i++) {  
        v1 = triangles.polygons[i].vertices[0];  
        v2 = triangles.polygons[i].vertices[1];  
        v3 = triangles.polygons[i].vertices[2];  
        mesh->addTriangle(v1, v2, v3);                //通过这种方式来添加三角面片  
    }  
    mesh->setTempColor(ccColor::Rgba(rand() % 255, rand() % 255, rand() % 255, 60));  
    mesh->showColors(true);  
    // ------------------------PCL->CloudCompare--------------------------------  
    if (ccCloud->getParent())  
    {  
        ccCloud->getParent()->addChild(mesh);  
    }  
    ccCloud->setEnabled(false);  
    addToDB(mesh);  
    refreshAll();  
    updateUI();  
}

 (2)重建结果

  ①重建前

②重建后

参考资料:
[1] 来吧!我在未来等你!. CloudCompare二次开发之如何配置PCL点云库?; 2023-05-15 [accessed 2023-05-17].
[2] Tech沉思录. PCL点云使用贪婪三角化进行曲面重构; 2019-09-06 [accessed 2023-05-17].
[3] 大鱼BIGFISH. CloudCompare&PCL AlphaShape算法曲面重建; 2022-10-26 [accessed 2023-05-17].
[4] 点云侠. PCL 泊松曲面重建法(多线程加速版); 2023-02-01 [accessed 2023-05-17].
[5] 悠缘之空. PCL函数库摘要——点云曲面重建; 2021-11-07 [accessed 2023-05-17].