PCL-基于超体聚类的LCCP点云分割

news2024/11/16 5:06:16

目录

  • 一、LCCP方法
  • 二、代码实现
  • 三、实验结果
  • 四、总结
  • 五、相关链接

一、LCCP方法

LCCP指的是Local Convexity-Constrained Patch,即局部凸约束补丁的意思。LCCP方法的基本思想是在图像中找到局部区域内的凸结构,并将这些结构用于分割图像或提取特征。这种方法可以帮助识别图像中的凸物体,并对它们进行分割。LCCP方法通常结合了空间和法线信息,以提高图像分割的准确性和稳定性。

LCCP算法大致可以分成两个部分:1.基于超体聚类的过分割。2.在超体聚类的基础上再聚类。
该方法流程图如下:
在这里插入图片描述

二、代码实现

#include <iostream>
#include <pcl/ModelCoefficients.h>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/sample_consensus/method_types.h>
#include <pcl/sample_consensus/model_types.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/filters/extract_indices.h>
#include <boost/thread/thread.hpp>
#include <stdlib.h>
#include <cmath>
#include <limits.h>
#include <boost/format.hpp>
#include <pcl/console/parse.h>
#include <pcl/io/pcd_io.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/visualization/point_cloud_color_handlers.h>
#include <pcl/filters/passthrough.h>
#include <pcl/segmentation/supervoxel_clustering.h>
#include <pcl/segmentation/lccp_segmentation.h>
#include <vtkPolyLine.h> 
#include <pcl/point_cloud.h>
#include <pcl/segmentation/supervoxel_clustering.h>
#include <pcl/visualization/pcl_visualizer.h>


using namespace std;
typedef pcl::PointXYZ PointT;
typedef pcl::LCCPSegmentation<PointT>::SupervoxelAdjacencyList SuperVoxelAdjacencyList;
//邻接线条可视化
void addSupervoxelConnectionsToViewer(pcl::PointXYZRGBA& supervoxel_center, pcl::PointCloud<pcl::PointXYZRGBA>& adjacent_supervoxel_centers,
	std::string supervoxel_name, pcl::visualization::PCLVisualizer::Ptr& viewer)
{
	vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
	vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
	vtkSmartPointer<vtkPolyLine> polyLine = vtkSmartPointer<vtkPolyLine>::New();


	for (auto adjacent_itr = adjacent_supervoxel_centers.begin(); adjacent_itr != adjacent_supervoxel_centers.end(); ++adjacent_itr)
	{
		points->InsertNextPoint(supervoxel_center.data);
		points->InsertNextPoint(adjacent_itr->data);
	}
	vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New();
	polyData->SetPoints(points);
	polyLine->GetPointIds()->SetNumberOfIds(points->GetNumberOfPoints());
	for (unsigned int i = 0; i < points->GetNumberOfPoints(); i++)
		polyLine->GetPointIds()->SetId(i, i);
	cells->InsertNextCell(polyLine);
	polyData->SetLines(cells);
	viewer->addModelFromPolyData(polyData, supervoxel_name);
}


int main(int argc, char** argv)
{
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PCDReader reader;
	// 读入点云PCD文件
	reader.read("E:****.pcd", *cloud);
	cout << "Point cloud data: " << cloud->points.size() << " points" << endl;
	pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients);
	pcl::PointIndices::Ptr inliers(new pcl::PointIndices);
	// 创建分割对象
	pcl::SACSegmentation<pcl::PointXYZ> seg;
	// 可选择配置,设置模型系数需要优化
	seg.setOptimizeCoefficients(true);
	// 必须配置,设置分割的模型类型、所用随机参数估计方法
	seg.setModelType(pcl::SACMODEL_PLANE);
	seg.setMethodType(pcl::SAC_RANSAC);
	seg.setDistanceThreshold(0.02);// 距离阈值 单位m。距离阈值决定了点被认为是局内点时必须满足的条件
	//seg.setDistanceThreshold(0.15);// 距离阈值 单位m。距离阈值决定了点被认为是局内点时必须满足的条件
	//距离阈值表示点到估计模型的距离最大值。
	seg.setInputCloud(cloud);//输入点云
	seg.segment(*inliers, *coefficients);//实现分割,并存储分割结果到点集合inliers及存储平面模型系数coefficients
	if (inliers->indices.size() == 0)
	{
		PCL_ERROR("Could not estimate a planar model for the given dataset.");
		return (-1);
	}
	//***********************************************************************
	//-----------输出平面模型的系数 a,b,c,d-----------
	cout << "Model coefficients: " << coefficients->values[0] << " "
		<< coefficients->values[1] << " "
		<< coefficients->values[2] << " "
		<< coefficients->values[3] << endl;
	cout << "Model inliers: " << inliers->indices.size() << endl;

	//***********************************************************************
	// 提取地面
	pcl::ExtractIndices<pcl::PointXYZ> extract;
	extract.setInputCloud(cloud);
	extract.setIndices(inliers);
	extract.filter(*cloud_filtered);

	cout << "Ground cloud after filtering: " << endl;
	cout << *cloud_filtered << std::endl;
	pcl::PCDWriter writer;
	writer.write<pcl::PointXYZ>("3dpoints_ground.pcd", *cloud_filtered, false);

	// 提取除地面外的物体
	extract.setNegative(true);
	extract.filter(*cloud_filtered);
	cout << "Object cloud after filtering: " << endl;
	cout << *cloud_filtered << endl;
	//writer.write<pcl::PointXYZ>(".pcd", *cloud_filtered, false);

	// 点云可视化
	boost::shared_ptr<pcl::visualization::PCLVisualizer>viewer0(new pcl::visualization::PCLVisualizer("显示点云"));
	//左边窗口显示输入的点云,右边的窗口显示分割后的点云
	int v1(0), v2(0);
	viewer0->createViewPort(0, 0, 0.5, 1, v1);
	viewer0->createViewPort(0.5, 0, 1, 1, v2);
	viewer0->setBackgroundColor(0, 0, 0, v1);
	viewer0->setBackgroundColor(0.3, 0.3, 0.3, v2);
	pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> color_in(cloud, 255, 0, 0);
	viewer0->addPointCloud<pcl::PointXYZ>(cloud, color_in, "cloud_in", v1);
	viewer0->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud_in", v1);

	viewer0->addPointCloud<pcl::PointXYZ>(cloud_filtered, "cloud_out", v2);
	viewer0->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0, 255, 0, "cloud_out", v2);
	viewer0->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud_out", v2);

	while (!viewer0->wasStopped())
	{
		viewer0->spinOnce(100);
		boost::this_thread::sleep(boost::posix_time::microseconds(1000));
	}

	//***********************************************************************
	//超体聚类 
	float voxel_resolution = 0.01f;    // 设置体素大小,该设置决定底层八叉树的叶子尺寸
	float seed_resolution = 0.15f;    // 设置种子大小,该设置决定超体素的大小
	float color_importance = 0.0f;    // 设置颜色在距离测试公式中的权重,即颜色影响超体素分割结果的比重。 真实点云都是一个颜色,所以这个参数无作用
	float spatial_importance = 0.9f;  // 设置空间距离在距离测试公式中的权重,较高的值会构建非常规则的超体素,较低的值产生的体素会按照法线
	float normal_importance = 4.0f;   // 设置法向量的权重,即表面法向量影响超体素分割结果的比重。
	bool use_single_cam_transform = false;
	bool use_supervoxel_refinement = false;

	unsigned int k_factor = 0;

	//voxel_resolution is the resolution (in meters) of voxels used、seed_resolution is the average size (in meters) of resulting supervoxels  
	pcl::SupervoxelClustering<PointT> super(voxel_resolution, seed_resolution);
	super.setUseSingleCameraTransform(use_single_cam_transform);
	super.setInputCloud(cloud_filtered); //cloud_filtered
	super.setColorImportance(color_importance);
	//Set the importance of spatial distance for supervoxels.
	super.setSpatialImportance(spatial_importance);
	//Set the importance of scalar normal product for supervoxels. 
	super.setNormalImportance(normal_importance);
	std::map<uint32_t, pcl::Supervoxel<PointT>::Ptr> supervoxel_clusters;
	super.extract(supervoxel_clusters);
	std::multimap<uint32_t, uint32_t> supervoxel_adjacency;
	super.getSupervoxelAdjacency(supervoxel_adjacency);
	pcl::PointCloud<pcl::PointNormal>::Ptr sv_centroid_normal_cloud = pcl::SupervoxelClustering<PointT>::makeSupervoxelNormalCloud(supervoxel_clusters);

	cout << "超体素分割的体素个数为:" << supervoxel_clusters.size() << endl;
	// 获取点云对应的超体素分割标签
	pcl::PointCloud<pcl::PointXYZL>::Ptr supervoxel_cloud = super.getLabeledCloud();
	pcl::visualization::PCLVisualizer::Ptr viewer1(new pcl::visualization::PCLVisualizer("VCCS"));
	viewer1->setWindowName("超体素分割");
	viewer1->addPointCloud(supervoxel_cloud, "超体素分割");
	viewer1->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "超体素分割");
	viewer1->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, 0.5, "超体素分割");

	//-----------------------------------------获得体素点云的邻接单元----------------------------------------------
	multimap<uint32_t, uint32_t>SupervoxelAdjacency;
	super.getSupervoxelAdjacency(SupervoxelAdjacency);

	for (auto label_itr = SupervoxelAdjacency.cbegin(); label_itr != SupervoxelAdjacency.cend();)
	{
		uint32_t super_label = label_itr->first;//获取体素单元的标签
		pcl::Supervoxel<pcl::PointXYZ>::Ptr super_cloud = supervoxel_clusters.at(super_label);//把对应标签内的点云、体素质心、以及质心对应的法向量提取出来

		pcl::PointCloud<pcl::PointXYZRGBA> adjacent_supervoxel_centers;
		for (auto adjacent_itr = SupervoxelAdjacency.equal_range(super_label).first; adjacent_itr != SupervoxelAdjacency.equal_range(super_label).second; ++adjacent_itr)
		{
			pcl::Supervoxel<pcl::PointXYZ>::Ptr neighbor_supervoxel = supervoxel_clusters.at(adjacent_itr->second);
			adjacent_supervoxel_centers.push_back(neighbor_supervoxel->centroid_);
		}
		std::stringstream ss;
		ss << "supervoxel_" << super_label;
		addSupervoxelConnectionsToViewer(super_cloud->centroid_, adjacent_supervoxel_centers, ss.str(), viewer1);
		label_itr = SupervoxelAdjacency.upper_bound(super_label);
	}
	// 等待直到可视化窗口关闭
	while (!viewer1->wasStopped())
	{
		viewer1->spinOnce(100);
		boost::this_thread::sleep(boost::posix_time::microseconds(1000));
	}

	//return 0;

	 //***********************************************************************
	//LCCP分割
	float concavity_tolerance_threshold = 10;
	float smoothness_threshold = 0.8;
	uint32_t min_segment_size = 0;
	bool use_extended_convexity = false;
	bool use_sanity_criterion = false;
	pcl::LCCPSegmentation<PointT> lccp;
	lccp.setConcavityToleranceThreshold(concavity_tolerance_threshold);//CC效验beta值
	lccp.setSmoothnessCheck(true, voxel_resolution, seed_resolution, smoothness_threshold);
	lccp.setKFactor(k_factor);               //CC效验的k邻点
	lccp.setInputSupervoxels(supervoxel_clusters, supervoxel_adjacency);
	lccp.setMinSegmentSize(min_segment_size);//最小分割尺寸
	lccp.segment();

	pcl::PointCloud<pcl::PointXYZL>::Ptr sv_labeled_cloud = super.getLabeledCloud();
	pcl::PointCloud<pcl::PointXYZL>::Ptr lccp_labeled_cloud = sv_labeled_cloud->makeShared();
	lccp.relabelCloud(*lccp_labeled_cloud);
	SuperVoxelAdjacencyList sv_adjacency_list;
	lccp.getSVAdjacencyList(sv_adjacency_list);

	pcl::visualization::PCLVisualizer::Ptr viewer2(new pcl::visualization::PCLVisualizer("LCCP超体素分割"));
	viewer2->setWindowName("LCCP超体素分割");
	viewer2->addPointCloud(lccp_labeled_cloud, "LCCP超体素分割");
	viewer2->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "LCCP超体素分割");
	viewer2->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, 0.5, "LCCP超体素分割");
	// 等待直到可视化窗口关闭
	while (!viewer2->wasStopped())
	{
		viewer2->spinOnce(100);
		boost::this_thread::sleep(boost::posix_time::microseconds(1000));
	}

	return 0;

}


三、实验结果

原数据
原数据
去除地面后
在这里插入图片描述
超体聚类过分割
在这里插入图片描述
LCCP分割
在这里插入图片描述

四、总结

从实验结果来看,LCCP算法在相似物体场景分割方面有着较好的表现,对于颜色类似但棱角分明的物体可使用该算法。

五、相关链接

[1]PCL-低层次视觉-点云分割(超体聚类)
[2]PCL_使用LCCP进行点云分割

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

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

相关文章

SVN文件夹没有图标(绿钩子和红感叹号)

3分钟教会你解决SVN文件夹没有绿勾和红色感叹号的问题_svn文件被改动过不显示红色-CSDN博客https://blog.csdn.net/weixin_43382915/article/details/124251563 关于SVN状态图标不显示的解决办法(史上最全) - 简书 (jianshu.com)https://www.jianshu.com/p/92e8e1f345c0

墨烯的C语言技术栈-C语言基础-018

char c; //1byte字节 8bit比特位 int main() { int a 10; //向内存申请四个字节,存储10 &a; //取地址操作符 return 0; } 每个字节都有地址 而a的地址就是它第一个字节的地址 要先开始调试才可以查看监控和查看内存 左边是地址 中间是内存中的数据 最后面的是…

【数据结构面试有那些常见问题?】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

Layui Selcet选择框动态选择问题

前言 时隔多日我也是重新回归写作&#xff0c;高考已经完毕&#xff0c;我将继续我的文章创作&#xff0c;今天我将分享的是我在开发我自己的一个新项目所遇到的问题&#xff0c;这里预告一下我的新项目: VitaApi管理系统 这个系统可以看作是萌新源api管理系统的延续&#xff…

谷粒商城实战笔记-54-商品服务-API-三级分类-拖拽效果

文章目录 一&#xff0c;54-商品服务-API-三级分类-修改-拖拽效果1&#xff0c;el-tree控件加上允许拖拽的属性2&#xff0c;是否允许拖拽3&#xff0c;完整代码 一&#xff0c;54-商品服务-API-三级分类-修改-拖拽效果 本节的主要内容是给三级分类树形结构加上拖拽功能&#…

CentOS怎么关闭自动锁屏?

禁止自动锁屏 有时候几分钟不用Centos&#xff0c;系统就自动锁屏了&#xff0c;这是一种安全措施&#xff0c;防止别人趁你不在时使用你的系统。但对于大部分人而言&#xff0c;这是没有必要的&#xff0c;尤其是Centos虚拟机&#xff0c;里面没啥重要的东西&#xff0c;每次…

基于面向对象重构模型训练器

引言 深度学习领域我们常用jupyter来演练代码&#xff0c;但实际生产环境中不可能像jupyter一样&#xff0c;所有代码逻辑都在面向过程编程&#xff0c;这会导致代码可复用性差&#xff0c;维护难度高。 前面这篇文章 基于pytorch可视化重学线性回归模型 已经封装了数据加载器…

Jenkins卡在等待界面解决方法

一、问题 部署jenkins服务器出现Please wait while Jenkins is getting ready to work。 二、原因分析 jenkins里面文件指向国外的官网&#xff0c;因为防火墙的原因连不上。 三、解决方法 将配置文件里面的url换成国内镜像&#xff1a; &#xff08;1&#xff09;修改配…

[k8s源码]9.workqueue

client-go 是一个库&#xff0c;提供了与 Kubernetes API 服务器交互的基础设施。它提供了诸如 Informer、Lister、ClientSet 等工具&#xff0c;用于监听、缓存和操作 Kubernetes 资源。而自定义控制器则利用这些工具来实现特定的业务逻辑和自动化任务。业务逻辑实现&#xff…

jmeter实战(2)- 入门使用教程

一、运行Jmeter 参考上一篇博客&#xff1a;jmeter实战&#xff08;1&#xff09;- Mac环境安装 二、创建线程组 JMeter的线程组是进行负载测试的基本构建单元&#xff0c;它用于模拟多个用户对目标系统进行并发访问。线程组中的属性允许你控制测试的并发级别和执行模式。 1.…

聚观早报 | Meta将推出新款AR眼镜;iPhone SE 4将升级显示屏

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 7月24日消息 Meta将推出新款AR眼镜 iPhone SE 4将升级显示屏 华硕天选Air 2024开启预约 巴菲特再次减持比亚迪股…

DT浏览器首页征集收录海内外网址

DT浏览器首页征集收录海内外网址&#xff0c;要求页面整洁&#xff0c;内容丰富&#xff0c;知识性和可读性强&#xff0c;符合大众价值观&#xff0c;不含恶意代码

linux添加普通用户后无法使用K8S的kubectl命令怎么办/Linux普通用户管理K8S/Linux下普通用户无法使用K8S命令

1.给Linux添加普通用户 sudo useradd mqq #添加mqq账号 sudo passwd mqq #给mqq账号设置密码&#xff0c;需要输入2次&#xff0c;我输入密码是Admin1232.利用mqq用户输入K8S命令报错 3.给mqq用户提权 suduers文件位于路径/etc/sudoers #编辑文件/etc/sudoers vim /etc/su…

第十四章 数据库

第十四章 数据库 14.1 引言 数据存储在传统上是使用单独的没有关联的文件&#xff0c;称为平面文件 14.1.1 定义 定义&#xff1a;数据库是一个组织内被应用程序使用的逻辑相一致的相关数据的集合 14.1.2 数据库的优点 数据库的优点&#xff1a; 冗余少避免数据的不一致…

ZLMRTCClient配置说明与用法(含示例)

webRTC播放视频 后面在项目中会用到通过推拉播放视频流的技术&#xff0c;所以最近预研了一下webRTC 首先需要引入封装好的webRTC客户端的js文件ZLMRTCClient.js 下面是地址需要的自行下载 http://my.zsyou.top/2024/ZLMRTCClient.js 配置说明 new ZLMRTCClient.Endpoint…

小猪佩奇.js

闲着没事 使用js 画一个小猪佩奇把 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</tit…

BUUCTF [MRCTF2020]Ezpop

这道题对于刚接触到pop链的我直接把我整懵了&#xff0c;一边看着魔术方法一边分析 魔术方法可以看这里PHP 魔术方法 - 简介 - PHP 魔术方法 - 简单教程&#xff0c;简单编程 (twle.cn) 代码解析 经过以上的分析我们可以理一下解题思路&#xff1a;接收参数反序列化之前先触发…

基于 HTML+ECharts 实现智慧交通数据可视化大屏(含源码)

构建智慧交通数据可视化大屏&#xff1a;基于 HTML 和 ECharts 的实现 随着城市化进程的加快&#xff0c;智慧交通系统已成为提升城市管理效率和居民生活质量的关键。通过数据可视化&#xff0c;交通管理部门可以实时监控交通流量、事故发生率、道路状况等关键指标&#xff0c;…

C#使用csvhelper实现csv的操作

新建控制台项目 安装csvhelper 33.0.1 写入csv 新建Foo.cs namespace CsvSut02;public class Foo {public int Id { get; set; }public string Name { get; set; } }批量写入 using System.Globalization; using CsvHelper; using CsvHelper.Configuration;namespace Csv…

Python3网络爬虫开发实战(2)爬虫基础库

文章目录 一、urllib1. urlparse 实现 URL 的识别和分段2. urlunparse 用于构造 URL3. urljoin 用于两个链接的拼接4. urlencode 将 params 字典序列化为 params 字符串5. parse_qs 和 parse_qsl 用于将 params 字符串反序列化为 params 字典或列表6. quote 和 unquote 对 URL的…