Win 下 PCL部分函数析构崩溃问题总结

引:项目近日需要对三维点云做重建,使用win pcl1.13版本提供的法线计算、贪婪三角计算等方法创建带伪彩的三维表面。但是调用gp3函数作重建结束后进程会崩溃,初步判断是析构过程的内存释放问题,但是苦于水平有限无法独立排查,遂在网络上搜索找到解决方法。同时去年的《人头三维重建》项目同样碰到过崩溃问题:在调用pcl::StatisticalOutlierRemoval和pcl::VoxelGrid使用pcl::PointCloud<pcl::PointXYZ>会导致析构崩溃。借此机会对Win平台下PCL库碰到的崩溃问题作总结

0 PCL版本及运行平台

使用的pcl链接及其版本如下,为方便使用了官方提供的已编译版本。
Fig 1. PCL版本

给出github链接 : Releases · PointCloudLibrary/pcl
Fig 2. 底层架构及系统环境

1 gp3 析构崩溃问题

1.1 问题分析

上游函数给出pcl::PointXYZRGB数据的PLY文件,下面模块主要负责读取点云后使用gp3重建表面

cpp 复制代码
// // 0121 -- 添加三维伪彩图模块
    {
        std::string ply_path = "./XYZRGBPointCloud_after_sample_form_zernike.ply";
        pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud = loadPointCloudFromPLY(ply_path);
        
        reconstruction_with_gp3(cloud, "./gp3_reconstructed_mesh.ply");
    }

逐个函数debug时,loadPointCloudFromPLT()函数不导致进程崩溃,遂问题直接定位到reconstruction_with_gp3()函数。给出函数伪代码及关键代码如下:

cpp 复制代码
int reconstruction_with_gp3(
    pcl::PointCloud<pcl::PointXYZRGB>::Ptr& cloud,
    const std::string& output_file)
{
    pcl::PolygonMesh triangles;
    
    //估计法向量
    // ...
    
    //创建搜索树
    pcl::search::KdTree<pcl::PointNormal>::Ptr tree2(new pcl::search::KdTree<pcl::PointNormal>);
    tree2->setInputCloud(cloud_with_normals);
    
    // GP3重建 -- 关键代码
    pcl::GreedyProjectionTriangulation<pcl::PointNormal> gp3;
    
    // 参数设置(根据你的点云密度调整)
    gp3.setSearchRadius(2.0f);              // 连接点之间的最大距离
    gp3.setMu(2.0f);                        // 最近邻距离的乘子
    gp3.setMaximumNearestNeighbors(50);    // 最近邻点的最大数量
    gp3.setMaximumSurfaceAngle(M_PI / 4);   // 45度 - 最大平面角
    gp3.setMinimumAngle(M_PI / 18);         // 10度 - 最小角度
    gp3.setMaximumAngle(2 * M_PI / 3);      // 120度 - 最大角度
    gp3.setNormalConsistency(false);        // 法向量一致性
    
    gp3.setInputCloud(cloud_with_normals);
    gp3.setSearchMethod(tree2);
    gp3.reconstruct(triangles);
       
    
    visualizeMesh(triangles); // 调用pcl::Visualization::Visualizer模块可视化
    return 0;
}

在这个模块执行结束后,下面的函数被阻塞,初步判断是析构过程存在指针/内存释放问题。

具体问题原因分析可以参照博客https://blog.gitcode.com/67819160f3f59846e55dc277fd993d09.html

1.2 解决办法

在包含gp3头文件之前添加定义PCL_NO_PRECOMPILE宏,强制编译器重新编译GreedyProjectionTriangulation的实现代码

cpp 复制代码
#define PCL_NO_PRECOMPILE  // 禁用PCL预编译以支持自定义点类型
#include <pcl/surface/gp3.h>

1.3 深入分析PCL_NO_PRECOMPILE

这个宏定义在做什么?先普及几个c++上的概念

宏:在编译前,对代码作文本替换。不检查数据类型也没有作用域。写法上可以像变量的定义,也可以向函数的定义和实现

cpp 复制代码
#define WIDTH 640;     //定义宏,在代码中使用WIDTH会被统一替换成640

#define SQR(x) (x)*(x) // 宏定义 在代码中用到SQR(x)会统一替换: int a = SQR(x)

模板类:定义模板类头文件即可,在cpp中有使用到这个类时候指定具体数据类型后,编译器会自动生成正真的类。

cpp 复制代码
template<typename T>
class Box {
public:
    T value;
};

pcl库大量使用模板类,包括gp3

cpp 复制代码
pcl::GreedyProjectionTriangulation<T>

传递的数据类型被错误的链接到了PCL 预编译的另一份模板实现,即PCL已经编译好的模板类实现中使用了错误的数据类型。

在gp3头文件之前添加定义PCL_NO_PRECOMPILE宏,就是告诉PCL:别用你编好的模板,我自己来编!

cpp 复制代码
GreedyProjectionTriangulation<MyPoint>

这样能实现类型、内存布局、Eigen 对齐一致,避免析构过程的崩溃。

2 StatisticalOutlierRemoval、VoxelGrid析构崩溃

2.1 问题分析

除以上问题,其他项目也碰到过析构崩溃问题,给出关键的代码上下文:

cpp 复制代码
int concat(){
    // ===== 相机参数 =====
    int width = 1280;
    int height = 720;
    float fx = 848.162f, fy = 847.514f, cx = 638.071f, cy = 355.201f;
    processRGBDSequence(img_List, depth_img_List, width, height, fx, fy, cx, cy,
                        last_T_final_vector, merged_cloud);
    
    // ============================= 3. 降噪 -- 剔除离群点 =========================================
    pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZRGB>);

    pcl::PCLPointCloud2::Ptr tmpcloud(new pcl::PCLPointCloud2());
    pcl::PCLPointCloud2::Ptr cloud_filtered2(new pcl::PCLPointCloud2());
    pcl::toPCLPointCloud2(*merged_cloud, *tmpcloud);

    pcl::StatisticalOutlierRemoval<pcl::PCLPointCloud2> sor;   //创建滤波器对象
    sor.setInputCloud(tmpcloud);                           //设置待滤波的点云
    sor.setMeanK (30);                               //设置在进行统计时考虑的临近点个数
    sor.setStddevMulThresh (1.0);                      //设置判断是否为离群点的阀值,用来倍乘标准差,也就是上面的std_mul
    sor.filter (*cloud_filtered2);                    //滤波结果存储到cloud_filtered

    
    


    // ============================= 下采样 (上层会奔溃,只能放在main中使用) ========================================= 
    float leaf_size = 0.005f;  // 体素大小,单位通常为米
    pcl::VoxelGrid<pcl::PCLPointCloud2> voxel_filter;
    voxel_filter.setInputCloud(cloud_filtered2);
    voxel_filter.setLeafSize(leaf_size, leaf_size, leaf_size);
    pcl::PCLPointCloud2::Ptr cloud_downsampled(new pcl::PCLPointCloud2);
    voxel_filter.filter(*cloud_downsampled);
    pcl::fromPCLPointCloud2(*cloud_downsampled, *cloud_filtered);
    visualizePointCloud(cloud_filtered, "cloud_filtered_downsampled");

    // ============================= 4. 表面重建 =========================================
    // 执行三角化重建并显示
    pcl::PolygonMesh mesh = greedyTriangulationReconstruction(
        cloud_filtered,
        2,  // 邻域半径
        2.5,   // mu 参数
        100,   // 最大邻域点数
        M_PI / 4,
        M_PI / 18,
        2 * M_PI / 3,
        true   // 是否显示可视化
    );

    // ============================= 5. 结果保存 =========================================
    std::string save_path = "output_cloud_ASCII.ply";
    if (pcl::io::savePLYFileASCII(save_path, *cloud_filtered) == 0) {
        std::cout << "二进制PCD保存成功: " << save_path << std::endl;
    } else {
        std::cerr << "保存失败: " << save_path << std::endl;
    }
    
    std::cout << "请输入任意!" << std::endl;
    std::getchar();
}


// === 主函数入口 ===
int main() {

    concat(); // 函数退出调用析构奔溃

    std::cout << "wwwww" << std::endl;
    std::getchar();
    return 0;
}

程序执行后,应该在结束concat()后正常打印"wwww",但是已知无法打印,往上推应该是caocat()函数的析构问题(函数内部已经逐步debug排查都没问题)。

2.2 解决办法

使用pcl::PCLPointCloud2通用数据类型替换原来采样和提出离群点函数的PointXYZ数据类型输入,函数处理结束后再作类型回替换

cpp 复制代码
// ============================= 3. 降噪 -- 剔除离群点 =========================================
    pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZRGB>);

    pcl::PCLPointCloud2::Ptr tmpcloud(new pcl::PCLPointCloud2());
    pcl::PCLPointCloud2::Ptr cloud_filtered2(new pcl::PCLPointCloud2());
    pcl::toPCLPointCloud2(*merged_cloud, *tmpcloud); // 替换成通用数据类型
    
    // ... 

    pcl::FromPCLPointCloud2(); // 回替换

参考博客:PCL使用VoxelGrid出现Segmentation fault (core dumped)的解决办法_pcl applyfilter 段错误-CSDN博客

2.3 深度分析

为什么使用pcl::PCLPointCloud2替换原有的数据类型就解决了问题:这是一个Windows平台特有的PCL库问题 ,根本原因是内存管理不兼容 。PCL在Windows上编译为DLL ,模板特化版本在不同模块中可能内存布局不一致。 pcl::PointXYZ是具体类型,pcl::PCLPointCloud2是通用容器,跨DLL边界传递具体类型的对象可能导致内存分配/释放不匹配。

cpp 复制代码
// ❌ 在Windows上可能导致问题
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
pcl::VoxelGrid<pcl::PointXYZ> vg;

// ✅ 改用PointCloud2
pcl::StatisticalOutlierRemoval<pcl::PCLPointCloud2> sor2;
pcl::VoxelGrid<pcl::PCLPointCloud2> vg2;
cpp 复制代码
// 问题示例
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
sor.setInputCloud(cloud);  // 内存在一个堆分配
sor.filter(*filtered_cloud);  // 在另一个堆释放(如果DLL使用不同运行时库)

2.4 深究pcl::PCLPointCloud2数据类型

pcl::PCLPointCloud2 是"通用/底层/动态结构",类似openGL中的vertex buffer以字节为单位存储数据,不同的数据属性使用vertex array表示(类似解析协议)。

cpp 复制代码
struct PCLPointCloud2
{
    uint32_t width, height;
    std::vector<PCLPointField> fields;
    std::vector<uint8_t> data;   // 原始字节流
    bool is_dense;
};

pcl::PointCloud<T> 是"强类型/模板化/高效结构",真正的 C++ struct

cpp 复制代码
struct PointXYZ {
    float x, y, z;
};

struct PointCloud<PointXYZ> {
    std::vector<PointXYZ> points;
    uint32_t width, height;
    bool is_dense;
};

算法内部、自己写逻辑pcl::PointCloud<T>

IO、滤波器、ROS、多类型混合、未知字段pcl::PCLPointCloud2

end.

相关推荐
qq_12498707531 分钟前
基于JavaWeb的大学生房屋租赁系统(源码+论文+部署+安装)
java·数据库·人工智能·spring boot·计算机视觉·毕业设计·计算机毕业设计
杜子不疼.10 分钟前
CANN计算机视觉算子库ops-cv的图像处理与特征提取优化实践
图像处理·人工智能·计算机视觉
艾莉丝努力练剑11 分钟前
【Linux:文件】Ext系列文件系统(初阶)
大数据·linux·运维·服务器·c++·人工智能·算法
张人玉11 分钟前
VisionPro 定位与卡尺测量学习笔记
笔记·学习·计算机视觉·vsionprp
Once_day27 分钟前
C++之《程序员自我修养》读书总结(1)
c语言·开发语言·c++·程序员自我修养
Trouvaille ~37 分钟前
【Linux】TCP Socket编程实战(一):API详解与单连接Echo Server
linux·运维·服务器·网络·c++·tcp/ip·socket
勾股导航42 分钟前
OpenCV图像坐标系
人工智能·opencv·计算机视觉
坚果派·白晓明1 小时前
在鸿蒙设备上快速验证由lycium工具快速交叉编译的C/C++三方库
c语言·c++·harmonyos·鸿蒙·编程语言·openharmony·三方库
小镇敲码人1 小时前
深入剖析华为CANN框架下的Ops-CV仓库:从入门到实战指南
c++·python·华为·cann
张张努力变强2 小时前
C++ STL string 类:常用接口 + auto + 范围 for全攻略,字符串操作效率拉满
开发语言·数据结构·c++·算法·stl