CloudCompare二次开发之如何通过PCL进行点云分割?

news2024/11/15 2:12:51

文章目录

  • 0.引言
  • 1.CloudCompare界面设计配准(segment)按钮
  • 2.欧式聚类分割(Euclidean_Seg)
  • 3.基于区域生长的分割(Region_Seg)

0.引言

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

1.CloudCompare界面设计配准(segment)按钮

  (1)设计.ui文件
  ①设计按钮
  在这里插入图片描述

  ②编译.ui
  在这里插入图片描述

  (2)修改mainwindow.h文件
  在这里插入图片描述

//点云分割
void doActionPCLEuclidean_Seg(); // 欧式聚类分割  
void doActionPCLRegion_Seg(); // 基于区域生长的分割

  (3)修改mainwindow.cpp文件
  ①添加头文件
  在这里插入图片描述

#include <pcl/ModelCoefficients.h>
#include <pcl/sample_consensus/method_types.h>//模型定义头文件  
#include <pcl/sample_consensus/model_types.h>//随机参数估计方法头文件  
#include <pcl/segmentation/sac_segmentation.h>//基于采样一致性分割的类的头文件  
#include <pcl/segmentation/extract_clusters.h>  
#include <pcl/segmentation/region_growing.h>//区域生成分割的头文件  
#include <pcl/filters/extract_indices.h>

  ②添加实现代码
  在这里插入图片描述

//欧式聚类分割
void MainWindow::doActionPCLEuclidean_Seg()  
{  
}  
//基于区域生长的分割  
void MainWindow::PCLRegion_Seg()  
{  
}

  ③添加信号槽函数
  在这里插入图片描述

connect(m_UI->actionEuclidean_Seg, &amp;QAction::triggered, this, &amp;MainWindow::doActionPCLEuclidean_Seg);//欧式聚类分割
connect(m_UI->actionRegion_Seg, &amp;QAction::triggered, this, &amp;MainWindow::doActionPCLRegion_Seg);//基于区域生长的分割

  (4)生成
  在这里插入图片描述

2.欧式聚类分割(Euclidean_Seg)

  (1)实现代码

//欧式聚类分割
void MainWindow::doActionPCLEuclidean_Seg()  
{  
    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);  
    }  
    pcl::VoxelGrid<pcl::PointXYZ> vg;  
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);  
    vg.setInputCloud(cloud);  
    vg.setLeafSize(0.05f, 0.05f, 0.05f);  
    vg.filter(*cloud_filtered);  
    pcl::SACSegmentation<pcl::PointXYZ> seg;//实例化一个分割对象  
    pcl::PointIndices::Ptr inliers(new pcl::PointIndices);//实例化一个索引  
    pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients);//实例化模型参数  
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_plane(new pcl::PointCloud<pcl::PointXYZ>());//提取到的平面保存至cloud_plane  
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_f(new pcl::PointCloud<pcl::PointXYZ>());//提取到的平面保存至cloud_plane  
    //pcl::PCDWriter writer;  
    seg.setOptimizeCoefficients(true);//参数优化  
    seg.setModelType(pcl::SACMODEL_PLANE);//模型类型:平面  
    seg.setMethodType(pcl::SAC_RANSAC);//参数估计方法  
    seg.setMaxIterations(100);//最大迭代次数  
    seg.setDistanceThreshold(0.02);//设置内点到模型的距离允许最大值  
    //创建一个文件夹来放点云  
    ccHObject* CloudGroup = new ccHObject(QString("SegmentGroup"));  
    int i = 0, nr_points = (int)cloud_filtered->points.size();//计数变量i,记下提取的平面的个数  
    dispToConsole("cloud_filtered->points.size()1:" + QString::number(cloud_filtered->points.size()), ccMainAppInterface::STD_CONSOLE_MESSAGE);  
    while (cloud_filtered->points.size() > 0.3 * nr_points)  
    {  
    // Segment the largest planar component from the remaining cloud  
    seg.setInputCloud(cloud_filtered);//设置要分割的点云  
    seg.segment(*inliers, *coefficients);//输出平面点的索引和参数  
    // Extract the planar inliers from the input cloud  
    pcl::ExtractIndices<pcl::PointXYZ> extract;//实例化一个提取对象  
    extract.setInputCloud(cloud_filtered);//设置要提取的点云  
    extract.setIndices(inliers);//根据分割得到的平面的索引提取平面  
    extract.setNegative(false);//提取内点  
      // Write the planar inliers to disk  
    extract.filter(*cloud_plane);//保存提取到的平面  
    //std::cout << "PointCloud representing the planar component: " << cloud_plane->points.size() << " data points." << std::endl;  
    //存写指针的参数  
    cloud_plane->width = cloud_plane->points.size();  
    cloud_plane->height = 1;  
    cloud_plane->resize(cloud_plane->width);  
    cloud_plane->is_dense = false;  
    ccPointCloud* newPointCloud = new ccPointCloud(QString::number(i + 1) + ".1segment");  
    for (int i = 0; i < cloud_plane->size(); ++i)  
    {  
        double x = cloud_plane->points[i].x;  
        double y = cloud_plane->points[i].y;  
        double z = cloud_plane->points[i].z;  
        newPointCloud->addPoint(CCVector3(x, y, z));  
    }  
    newPointCloud->setRGBColor(ccColor::Rgba(255, 100, 100, 255));  
    newPointCloud->setRGBColor(ccColor::Rgba(rand() % 205 + 50, rand() % 155 + 100, rand() % 105 + 150, 255));  
    newPointCloud->showColors(true);  
    CloudGroup->addChild(newPointCloud);  
    //CloudGroup->getLastChild()->setEnabled(false);  
    addToDB(newPointCloud);  
    //计数变量加1  
    i++;  
    // Remove the planar inliers, extract the rest  
    extract.setNegative(true);//提取外点(除第一个平面之外的点)  
    extract.filter(*cloud_f);//保存除平面之外的剩余点  
    cloud_filtered = cloud_f;//将剩余点作为下一次分割、提取的平面的输入点云  
    }  
    // Creating the KdTree object for the search method of the extraction  
    pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);  
    tree->setInputCloud(cloud_filtered);//将无法提取平面的点云作为cloud_filtered  
    std::vector<pcl::PointIndices> cluster_indices;//保存每一种聚类,每一种聚类下还有具体的聚类的点  
    pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;//实例化一个欧式聚类提取对象  
    ec.setClusterTolerance(0.02); // 近邻搜索的搜索半径为2cm,重要参数  
    ec.setMinClusterSize(100);//设置一个聚类需要的最少点数目为100  
    ec.setMaxClusterSize(25000);//一个聚类最大点数目为25000  
    ec.setSearchMethod(tree);//设置点云的搜索机制  
    ec.setInputCloud(cloud_filtered);//设置输入点云  
    ec.extract(cluster_indices);//将聚类结果保存至cluster_indices中  
    //迭代访问点云索引cluster_indices,直到分割出所有聚类,一个循环提取出一类点云  
    int j = 0;  
    for (std::vector<pcl::PointIndices>::const_iterator it = cluster_indices.begin(); it != cluster_indices.end(); ++it)  
    {  
        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_cluster(new pcl::PointCloud<pcl::PointXYZ>);  
        //创建新的点云数据集cloud_cluster,直到分割出所有聚类  
        for (std::vector<int>::const_iterator pit = it->indices.begin(); pit != it->indices.end(); pit++)  
            cloud_cluster->points.push_back(cloud_filtered->points[*pit]); //*  
  
        cloud_cluster->width = cloud_cluster->points.size();  
        cloud_cluster->height = 1;  
        cloud_cluster->is_dense = true;  
        ccPointCloud* newPointCloud = new ccPointCloud(QString::number(i + 1) + ".2segment");  
        for (int i = 0; i < cloud_cluster->size(); ++i)  
        {  
            double x = cloud_cluster->points[i].x;  
            double y = cloud_cluster->points[i].y;  
            double z = cloud_cluster->points[i].z;  
            newPointCloud->addPoint(CCVector3(x, y, z));  
        }  
        newPointCloud->setRGBColor(ccColor::Rgba(255, 255, 255, 255));  
        newPointCloud->showColors(true);  
    CloudGroup->addChild(newPointCloud);  
    CloudGroup->getLastChild()->setEnabled(false);  
    addToDB(newPointCloud);  
    j++;  
    }  
    m_ccRoot->addElement(CloudGroup);  
}

  (2)分割结果
  ①分割前
  在这里插入图片描述

  ②分割后
  在这里插入图片描述

3.基于区域生长的分割(Region_Seg)

  (1)实现代码

//基于区域生长的分割
void MainWindow::doActionPCLRegion_Seg()  
{  
    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);  
    }  
    int KN_normal = 50; //设置默认输入参数  
    bool Bool_Cuting = false;//设置默认输入参数  
    float far_cuting = 10, near_cuting = 0, SmoothnessThreshold = 30.0, CurvatureThreshold = 0.05;//设置默认输入参数  
    pcl::search::Search<pcl::PointXYZ>::Ptr tree = boost::shared_ptr<pcl::search::Search<pcl::PointXYZ> >(new pcl::search::KdTree<pcl::PointXYZ>);//创建一个指向kd树搜索对象的共享指针  
    pcl::PointCloud <pcl::Normal>::Ptr normals(new pcl::PointCloud <pcl::Normal>);  
    pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normal_estimator;//创建法线估计对象  
    normal_estimator.setSearchMethod(tree);//设置搜索方法  
    normal_estimator.setInputCloud(cloud);//设置法线估计对象输入点集  
    normal_estimator.setKSearch(KN_normal);// 设置用于法向量估计的k近邻数目  
    normal_estimator.compute(*normals);//计算并输出法向量  
    // 区域生长算法的5个参数  
    pcl::RegionGrowing<pcl::PointXYZ, pcl::Normal> reg;//创建区域生长分割对象  
    reg.setMinClusterSize(50);//设置一个聚类需要的最小点数  
    reg.setMaxClusterSize(1000000);//设置一个聚类需要的最大点数  
    reg.setSearchMethod(tree);//设置搜索方法  
    reg.setNumberOfNeighbours(30);//设置搜索的临近点数目  
    reg.setInputCloud(cloud);//设置输入点云  
    reg.setInputNormals(normals);//设置输入点云的法向量  
    reg.setSmoothnessThreshold(SmoothnessThreshold / 180.0 * M_PI);//设置平滑阈值  
    reg.setCurvatureThreshold(CurvatureThreshold);//设置曲率阈值  
    std::vector <pcl::PointIndices> clusters;//保存每一种聚类,每一种聚类下面还有具体的点  
    reg.extract(clusters);//获取聚类的结果,分割结果保存在点云索引的向量中。  
    //创建一个文件夹来放点云  
    ccHObject* CloudGroup = new ccHObject(QString("SegmentGroup"));  
    for (size_t i = 0; i < clusters.size(); i++)  
    {  
        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_seg1(new pcl::PointCloud<pcl::PointXYZ>);  
        for (std::vector<int>::const_iterator pit = clusters[i].indices.begin(); pit != clusters[i].indices.end(); pit++)//创建一个迭代器pit以访问第一个聚类的每一个点  
        {  
            cloud_seg1->points.push_back(cloud->points[*pit]);//迭代器pit类似于一个指针,将第一个聚类分割中的每一个点进行强制类型转换,并放置在points中  
        }  
        cloud_seg1->width = cloud_seg1->points.size();  
        cloud_seg1->height = 1;  
        cloud_seg1->is_dense = false;  
    ccPointCloud* newPointCloud = new ccPointCloud(QString::number(i + 1) + ".Region_Seg");  
    for (int i = 0; i < cloud_seg1->size(); ++i)  
    {  
        double x = cloud_seg1->points[i].x;  
        double y = cloud_seg1->points[i].y;  
        double z = cloud_seg1->points[i].z;  
        newPointCloud->addPoint(CCVector3(x, y, z));  
    }  
    //newPointCloud->setRGBColor(ccColor::Rgba(100, 255, 100, 255));  
    newPointCloud->setRGBColor(ccColor::Rgba(rand() % 105 + 150, rand() % 155 + 100, rand() % 205 + 50, 255));  
    newPointCloud->showColors(true);  
    CloudGroup->addChild(newPointCloud);  
    //CloudGroup->getLastChild()->setEnabled(false);  
    addToDB(newPointCloud);  
    }  
    m_ccRoot->addElement(CloudGroup);  
}

  (2)分割结果
  ①分割前
  在这里插入图片描述

  ②分割后
  在这里插入图片描述

参考资料:
[1] 来吧!我在未来等你!. CloudCompare二次开发之如何配置PCL点云库?; 2023-05-15 [accessed 2023-05-17].
[2] Tech沉思录. PCL 【点云分割】; 2019-08-17 [accessed 2023-05-17].
[3] 步子小不扯淡. PCL: Segmentation模块之SACSegmentation点云分割; 2014-09-07 [accessed 2023-05-17].
[4] SOC罗三炮. PCL教程-点云分割之区域生长分割算法; 2023-01-08 [accessed 2023-05-17].
[5] 悠缘之空. PCL函数库摘要——点云分割; 2021-11-07 [accessed 2023-05-17].

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/536146.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【漏洞预警】泛微E-Cology ofsLogin任意用户登陆漏洞

泛微ofsLogin任意用户登陆漏洞 漏洞描述 泛微e-cology是一套兼具企业信息门户、知识文档管理、工作流程管理、人力资源管理、客户关系管理、项目管理、财务管理、资产管理、供应链管理、数据中心功能的企业大型协同管理平台。 泛微e-cology前台任意用户登录漏洞&#xff1a;…

Git 代码分支管理 | 京东云技术团队

作者&#xff1a;京东科技 周新智 一、引言 近日&#xff0c;IoT 研发团队加入了不少新同学&#xff0c;对 git 分支的命名和管理方式有些许的模糊&#xff0c;分支的命名规范以及管理方式对项目的版本发布至关重要&#xff0c;为了解决实际开发过程中版本发布时代码管理混乱…

大模型高效调参—PEFT库( Parameter-Efficient Fine-Tuning)

介绍 在面对特定的下游任务时&#xff0c;如果进行Full FineTuning&#xff08;即对预训练模型中的所有参数都进行微调&#xff09;&#xff0c;太过低效&#xff1b;而如果采用固定预训练模型的某些层&#xff0c;只微调接近下游任务的那几层参数&#xff0c;又难以达到较好的…

Java 8 腰斩!Java 17 暴涨 430%!!

前言 New Relic 最新发布了一份 “2023 年 Java 生态系统状况报告”&#xff0c;旨在提供有关当今 Java 生态系统状态的背景和见解。该报告基于从数百万个提供性能数据的应用程序中收集的数据&#xff0c;对生产中使用最多的版本、最受欢迎的 JDK 供应商、容器的兴起等多方面进…

数据链路层讲解

目录 一、数据链路层解决的问题 二、以太网协议 2.1 认识以太网 2.2 以太网帧格式 2.3 MAC地址 2.3.1 认识MAC地址 2.3.2 对比MAC地址和IP地址 2.4 MTU 2.4.1 认识MTU 2.4.2 MUT对IP协议的影响 2.4.3 MTU对UDP协议的影响 2.4.4 MTU对TCP协议的影响 2.5 数据跨网络…

什么是npu算力盒子,算力是越大越好吗?

一、什么是npu算力盒子&#xff1f;该怎么选&#xff1f; NPU&#xff08;神经处理单元&#xff09;算力盒子是一种专门用于进行人工智能计算的硬件设备&#xff0c;其中集成了高性能的NPU芯片。NPU是一种针对深度学习任务进行优化的处理器&#xff0c;具备高度并行计算和低功…

MySQL高级_第07章_InnoDB数据存储结构

MySQL高级_第07章_InnoDB数据存储结构 1.数据库的存储结构:页 索引结构给我们提供了高效的索引万式&#xff0c;不过索引信息以及数据记录都是保存在文件上的, 确切说是存储在页结构中。另一方面&#xff0c;索引是在存储引擎中实现的&#xff0c;MySQL服务器上的存储引擎负责…

文章生成器-原创文章生成器

在网络营销领域&#xff0c;优质文章是吸引新客户和保留老客户的重要工具。然而&#xff0c;生成高质量且符合SEO优化的文章并不是一件容易的事情。这就是为什么网站文章生成器如今备受欢迎的原因。而在众多的文章生成工具中&#xff0c;147GPT批量生成文章软件是一款非常出色的…

apache doris自定义udf函数

环境准备 下载(https://doris.apache.org/zh-CN/download) 上传文件到节点,并解压 apache-doris-fe-1.2.4.1-bin-x86_64.tar.xzapache-doris-be-1.2.4.1-bin-x86_64-noavx2.tar.xzapache-doris-dependencies-1.2.4.1-bin-x86_64.tar.xz部署 官方部署文档(本文的端口均有修…

如何将 Confluence 数据自助迁移至 ONES|软件国产化替代

近日&#xff0c;ONES 升级了 Confluence 自助迁移工具&#xff0c;对迁移数据类型、迁移范围、迁移模式等多个维度的能力进行了提升&#xff0c;帮助企业更高效率、更低成本地将 Confluence 中的数据完整、准确地迁移至 ONES Wiki 中。 在 Confluence 与 ONES 服务资源充足的…

win下C++通过Clion部署yolov5——libtorch+yolov5

libtorchyolov5 一、环境配置二、下载官网例子三、测试3.1、创建项目3.2、cmakelist.txt编写3.3、运行测试 一、环境配置 需要配置libtorchOpenCV&#xff0c;此处参考博文&#xff1a;clion配置libtorchOpenCV环境配置。 环境解决后即可开始下一步啦。 二、下载官网例子 下…

【debug】分立式网络切片部署

文章目录 启动代码部署全流程网络配置配置静态IP 部署核心网部署基站部署基站1部署基站2部署基站3查看amf日志 问题routing-config的问题不加routing-config与加的对比调查 nr-binder功能测试基站1基站2ifconfig路由表方向解决 路由规则 启动代码 启动OAI核心网 #开启数据转发…

WebAssembly--执行与内存模型

前言 在WebAssembly初探中&#xff0c;我们已经了解了WebAssembly的发展和标准演进过程&#xff0c;并简单地体验了一把Wasm的应用&#xff0c;本篇文章会通过对比WASM和JS的执行流程&#xff0c;WebAssembly的内存模型深入分析&#xff0c;带大家理解下WebAssembly部分核心原…

IOC/DI注解开发管理第三方bean

文章目录 1 环境准备2 注解开发管理第三方bean步骤1:导入对应的jar包步骤2:在配置类中添加一个方法步骤3:在方法上添加Bean注解步骤4:从IOC容器中获取对象并打印 3 引入外部配置类3.1 使用包扫描引入步骤1:在Spring的配置类上添加包扫描步骤2:在JdbcConfig上添加配置注解步骤3:…

谷歌浏览器F请求解析说明

请求 Queued at 0s&#xff1a;表示该请求加入到请求队列中的时刻&#xff0c;请求队列在打开F12后第一次发送请求的时候创建&#xff0c;直到关闭控制台的时候销毁。 Started at 7.14s&#xff1a;表示请求开始处理的时刻。 Queueing&#xff1a;表示请求从加入到请求队列…

LeetCode 107. 二叉树的层序遍历 II

107. 二叉树的层序遍历 II 描述 给你二叉树的根节点 root &#xff0c;返回其节点值 自底向上的层序遍历 。 &#xff08;即按从叶子节点所在层到根节点所在的层&#xff0c;逐层从左向右遍历&#xff09; 示例 示例1 输入&#xff1a;root [3,9,20,null,null,15,7] 输出…

Alibaba(一)项目环境合理选择

在学习以及开始使微服务架构前&#xff0c;我们需要先选择各各模块适配的版本。以此来避免生产过程或者学习过程出现令人头疼的版本问题&#xff0c;避免花费大量时间去找更正这些版本错误&#xff0c;导致耽误学习&#xff0c;影响项目进度。 项目源码&#xff0c;及源文档地址…

XPATH 使用备忘

xpath的基础使用 一.xpath简介 XPath 是一门在 XML 文档中查找信息的语言。XPath 用于在 XML 文档中通过元素和属性进行导航。 XPath 使用路径表达式在 XML 文档中进行导航XPath 包含一个标准函数库XPath 是 XSLT 中的主要元素XPath 是一个 W3C 标准 节点 在 XPath 中&…

【Linux】有名管道介绍及使用

目录 有名管道有名管道使用 橙色 有名管道 有名管道(FIFO)和匿名管道(pipe)有一些特点是相同的,不一样的地方在于: FIFO在文件系统中作为一个特殊文件存在&#xff0c;但文件中是没有内容的&#xff0c; FIFO中的内容存放在内存中。当使用FIFO的进程退出后&#xff0c; FIFO文…

在离散混合制造环境中应用制造运营模型

介绍 本文所描述的所有制造过程、场景、操作模式和技术应用目前都在一个成熟的离散和离散/批量混合制造企业中使用&#xff0c;该企业生产和维修复杂的机器。该企业生产的产品范围从从棒材加工的简单零件到复杂的机械装配&#xff1b;最终产品包括许多内部和第三方提供的子装配…