今天学习这段 点云超体素聚类(Supervoxel Clustering) 的原理、代码逻辑和实际作用,帮你彻底理解:
一、先搞懂「超体素聚类」是做什么的?
简单说,它是一种 3D 点云的 “超像素” 分割算法:把点云分成许多 “超体素”(Supervoxel)—— 类似图像的 “超像素”,但在 3D 空间中。
- 核心目标:用 “超体素” 替代原始点,减少后续处理的复杂度(比如分割、识别);
- 优势:超体素能贴合物体边界,且分布均匀,让后续算法更高效、更准确。
二、算法原理:怎么生成 “超体素”?
超体素聚类基于 “体素云连接分割(VCCS)” 算法,步骤如下:
-
体素化与种子初始化:
- 把点云体素化(分成小立方体),用
voxel_resolution
控制体素大小; - 在体素中均匀撒 “种子点”,用
seed_resolution
控制种子间距(种子决定超体素的初始位置)。
- 把点云体素化(分成小立方体),用
-
区域生长(Region Growing):
- 从种子点开始,逐步扩展超体素:计算当前体素与种子的 “特征距离”(融合空间、颜色、法向量信息);
- 距离近的体素被合并到超体素中,直到无法扩展(达到搜索范围或无新体素)。
-
邻接关系维护:
-
超体素在 3D 空间中保持 26 - 邻接关系(共享面、边、顶点的体素视为相邻);
-
用八叉树(Octree)高效维护体素的邻接关系,加速区域生长和后续处理。
从右到左,6(面)、18(面、边)和 26(面、边、顶点)的邻接关系
-
关键公式:特征距离计算
超体素扩展时,用以下公式计算 “特征距离”,决定是否合并体素:
-
Dc:颜色距离(RGB 空间的欧氏距离);
-
Ds:空间距离(体素与种子的欧氏距离);
-
Dn:法向量距离(法向量夹角的余弦距离);
-
wc,ws,wn:颜色、空间、法向量的权重(用户可调);
-
Rseed:种子间距(
seed_resolution
)。 影响超体素聚类的各种尺寸参数。R_seed 和 R_voxel 必须由用户设置。
三、代码逐段拆解:每一步在干啥?
1. 头文件 & 类型定义
#include <pcl/console/parse.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/segmentation/supervoxel_clustering.h>
// VTK 用于绘制连线
#include <vtkPolyLine.h>
// 类型定义(简化代码)
typedef pcl::PointXYZRGBA PointT;
typedef pcl::PointCloud<PointT> PointCloudT;
typedef pcl::PointNormal PointNT;
typedef pcl::PointCloud<PointNT> PointNCloudT;
typedef pcl::PointXYZL PointLT;
typedef pcl::PointCloud<PointLT> PointLCloudT;
- 作用:引入必要的库(IO、可视化、超体素聚类等),定义点云类型(简化代码)。
2. 辅助函数:绘制超体素邻接关系
void addSupervoxelConnectionsToViewer (PointT &supervoxel_center,
PointCloudT &adjacent_supervoxel_centers,
std::string supervoxel_name,
pcl::visualization::PCLVisualizer::Ptr & viewer)
{
// VTK 数据结构:点、线、多边形
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);
}
// 构建 VTK 多边形数据
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);
}
- 作用:用 VTK 绘制 “超体素中心” 与 “邻接超体素中心” 的连线,展示超体素的邻接关系。
3. 主函数:流程控制
int main (int argc, char ** argv)
{
// 解析命令行参数
if (argc < 2)
{
pcl::console::print_error ("参数错误!用法:...\n");
return (1);
}
// 加载点云
PointCloudT::Ptr cloud (new PointCloudT);
pcl::console::print_highlight ("加载点云...\n");
if (pcl::io::loadPCDFile<PointT> (argv[1], *cloud))
{
pcl::console::print_error ("加载点云失败!\n");
return (1);
}
// 参数设置(默认值 + 命令行解析)
bool disable_transform = pcl::console::find_switch (argc, argv, "--NT");
float voxel_resolution = 0.008f; // 体素大小
float seed_resolution = 0.1f; // 种子间距
float color_importance = 0.2f; // 颜色权重
float spatial_importance = 0.4f; // 空间权重
float normal_importance = 1.0f; // 法向量权重
// 配置超体素聚类
pcl::SupervoxelClustering<PointT> super (voxel_resolution, seed_resolution);
if (disable_transform)
super.setUseSingleCameraTransform (false); // 关闭单视角变换(适用于无序点云)
super.setInputCloud (cloud);
super.setColorImportance (color_importance);
super.setSpatialImportance (spatial_importance);
super.setNormalImportance (normal_importance);
// 提取超体素
std::map <std::uint32_t, pcl::Supervoxel<PointT>::Ptr > supervoxel_clusters;
pcl::console::print_highlight ("提取超体素...\n");
super.extract (supervoxel_clusters);
pcl::console::print_info ("找到 %d 个超体素\n", supervoxel_clusters.size ());
// 可视化准备
pcl::visualization::PCLVisualizer::Ptr viewer (new pcl::visualization::PCLVisualizer ("3D Viewer"));
viewer->setBackgroundColor (0, 0, 0);
// 显示体素中心
PointCloudT::Ptr voxel_centroid_cloud = super.getVoxelCentroidCloud ();
viewer->addPointCloud (voxel_centroid_cloud, "voxel centroids");
viewer->setPointCloudRenderingProperties (...);
// 显示带标签的体素
PointLCloudT::Ptr labeled_voxel_cloud = super.getLabeledVoxelCloud ();
viewer->addPointCloud (labeled_voxel_cloud, "labeled voxels");
viewer->setPointCloudRenderingProperties (...);
// 显示超体素法向量(可选)
PointNCloudT::Ptr sv_normal_cloud = super.makeSupervoxelNormalCloud (supervoxel_clusters);
// viewer->addPointCloudNormals (...); // 取消注释可显示法向量
// 提取超体素邻接关系并可视化
pcl::console::print_highlight ("获取超体素邻接关系...\n");
std::multimap<std::uint32_t, std::uint32_t> supervoxel_adjacency;
super.getSupervoxelAdjacency (supervoxel_adjacency);
// 遍历邻接关系,绘制连线
for (auto label_itr = supervoxel_adjacency.cbegin (); label_itr != supervoxel_adjacency.cend (); )
{
std::uint32_t supervoxel_label = label_itr->first;
pcl::Supervoxel<PointT>::Ptr supervoxel = supervoxel_clusters.at (supervoxel_label);
// 收集邻接超体素的中心
PointCloudT adjacent_supervoxel_centers;
for (auto adjacent_itr = ...; adjacent_itr != ...; ++adjacent_itr)
{
pcl::Supervoxel<PointT>::Ptr neighbor_supervoxel = ...;
adjacent_supervoxel_centers.push_back (neighbor_supervoxel->centroid_);
}
// 绘制超体素中心与邻接中心的连线
std::stringstream ss;
ss << "supervoxel_" << supervoxel_label;
addSupervoxelConnectionsToViewer (supervoxel->centroid_, adjacent_supervoxel_centers, ss.str (), viewer);
// 移动迭代器到下一个超体素
label_itr = supervoxel_adjacency.upper_bound (supervoxel_label);
}
// 持续可视化
while (!viewer->wasStopped ())
{
viewer->spinOnce (100);
}
return (0);
}
4. 关键步骤解释
- 参数设置:
voxel_resolution
控制体素大小,seed_resolution
控制种子间距,color_importance
/spatial_importance
/normal_importance
控制超体素扩展的 “特征距离” 权重。 - 超体素提取:用
SupervoxelClustering::extract
生成超体素,结果存在supervoxel_clusters
(标签 → 超体素指针的映射)。 - 可视化:
voxel_centroid_cloud
:体素中心(下采样后的点云);labeled_voxel_cloud
:带超体素标签的体素(颜色随机);sv_normal_cloud
:超体素的法向量(可选显示);- 邻接关系:用
addSupervoxelConnectionsToViewer
绘制超体素中心的连线,展示邻接关系。
四、运行流程
# 运行可执行文件(参数:点云文件 + 可选参数)
./supervoxel_clustering milk_cartoon_all_small_clorox.pcd --NT
milk_cartoon_all_small_clorox.pcd
:输入点云文件;--NT
:关闭单视角变换(适用于无序点云,如 LiDAR 扫描);- 可添加其他参数(如
-v 0.01
调整体素大小,-s 0.2
调整种子间距),观察超体素变化。
五、结合点云图理解
上方点云图包含瓶子、纸盒等物体,超体素聚类的作用是:
- 分割物体:每个物体被分成一个或多个超体素(颜色不同的区域);
- 简化后续处理:用超体素替代原始点,后续算法(如识别、分割)只需处理少量超体素,效率更高;
- 邻接关系:超体素之间的连线展示了它们的邻接关系(哪些超体素属于同一物体或相邻物体)。
可以尝试调整参数(如 seed_resolution
),观察超体素的大小和数量变化 —— 比如增大 seed_resolution
,超体素会更大、数量更少。
总结:核心知识点
- 算法思想:用 “区域生长” 生成超体素,融合空间、颜色、法向量信息,贴合物体边界。
- 关键参数:体素大小、种子间距、颜色 / 空间 / 法向量权重,决定超体素的形状和分布。
- 适用场景:点云分割、识别的预处理,减少计算量,提升算法效率。
如果想更深入,建议修改参数(如权重、分辨率),观察超体素的变化,或者尝试处理自己的点云数据~
内容源自pcl官方文档Introduction — Point Cloud Library 0.0 documentation
本文为基于 PCL 官方文档的学习整理与个人理解