【PCL】Segmentation 模块—— 基于图割算法的点云分割(Min-Cut Based Segmentation)

1、简介

PCL(Point Cloud Library)中的 Min-Cut Based Segmentation 是一种基于图割(Graph Cut)算法的点云分割方法,用于将点云数据分割为前景和背景。该方法通过构建图模型,利用最小割(Min-Cut)算法找到最优分割边界。

1.1 核心思想

a. 图模型构建

  • 将点云中的每个点视为图中的一个节点。
  • 定义边的权重,包括:
    • 数据项(Data Term):表示点属于前景或背景的代价。
    • 平滑项(Smoothness Term):表示相邻点之间的一致性。

b. 最小割算法

  • 通过最小割算法将图分为前景和背景两部分,使得分割代价最小。

1.2 算法步骤

1). 输入点云 :输入待分割的点云数据。

2). 设置种子点 :手动或自动选择前景和背景的种子点。

3). 构建图 :根据点云构建图模型,定义数据项和平滑项。

4). 计算最小割 :使用最小割算法分割图。

5). 输出结果:得到分割后的前景和背景点云。

1.3 参数设置

  • 前景种子点:标记为前景的点。
  • 背景种子点:标记为背景的点。
  • Sigma:控制平滑项权重。
  • Radius:定义邻域范围。
  • Foreground Penalty:前景惩罚项。
  • Background Penalty:背景惩罚项。

2、代码示例

2.1 min_cut_segmentation.cpp

通过min_cut_segmentation.cpp这段代码,可以实现基于Min-Cut算法的点云分割,并将分割结果可视化:

cpp 复制代码
#include <iostream>
#include <vector>
#include <pcl/io/pcd_io.h> // PCL的文件IO模块,用于读取和保存点云数据
#include <pcl/point_types.h> // PCL点云数据类型的定义
#include <pcl/visualization/cloud_viewer.h> // PCL点云可视化模块
#include <pcl/filters/filter_indices.h> // 用于去除点云中的NaN点
#include <pcl/segmentation/min_cut_segmentation.h> // PCL的Min-Cut分割算法模块

int main ()
{
  // 创建一个点云对象,用于存储从PCD文件加载的点云数据
  pcl::PointCloud <pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud <pcl::PointXYZ>);
  
  // 从PCD文件加载点云数据
  if ( pcl::io::loadPCDFile <pcl::PointXYZ> ("min_cut_segmentation_tutorial.pcd", *cloud) == -1 )
  {
    std::cout << "Cloud reading failed." << std::endl; // 如果加载失败,输出错误信息
    return (-1); // 返回-1表示程序异常退出
  }

  // 创建一个索引容器,用于存储去除NaN点后的点云索引
  pcl::IndicesPtr indices (new std::vector <int>);
  
  // 去除点云中的NaN点,并将有效点的索引存储在indices中
  pcl::removeNaNFromPointCloud(*cloud, *indices);

  // 创建Min-Cut分割对象
  pcl::MinCutSegmentation<pcl::PointXYZ> seg;
  
  // 设置输入点云
  seg.setInputCloud (cloud);
  
  // 设置有效点的索引
  seg.setIndices (indices);

  // 创建一个点云对象,用于存储前景种子点
  pcl::PointCloud<pcl::PointXYZ>::Ptr foreground_points(new pcl::PointCloud<pcl::PointXYZ> ());
  
  // 定义一个前景种子点
  pcl::PointXYZ point;
  point.x = 68.97;
  point.y = -18.55;
  point.z = 0.57;
  
  // 将前景种子点添加到前景点云中
  foreground_points->points.push_back(point);
  
  // 设置前景种子点
  seg.setForegroundPoints (foreground_points);

  // 设置Min-Cut分割算法的参数
  seg.setSigma (0.25); // 设置高斯平滑参数
  seg.setRadius (3.0433856); // 设置点云邻域搜索半径
  seg.setNumberOfNeighbours (14); // 设置邻域点的数量
  seg.setSourceWeight (0.8); // 设置前景权重

  // 创建一个容器,用于存储分割后的聚类结果
  std::vector <pcl::PointIndices> clusters;
  
  // 执行分割,并将结果存储在clusters中
  seg.extract (clusters);

  // 输出最大流的值(Min-Cut算法的计算结果)
  std::cout << "Maximum flow is " << seg.getMaxFlow () << std::endl;

  // 获取分割后的彩色点云(前景和背景用不同颜色表示)
  pcl::PointCloud <pcl::PointXYZRGB>::Ptr colored_cloud = seg.getColoredCloud ();
  
  // 创建一个点云可视化对象
  pcl::visualization::CloudViewer viewer ("Cluster viewer");
  
  // 显示彩色点云
  viewer.showCloud(colored_cloud);
  
  // 保持可视化窗口打开,直到用户关闭窗口
  while (!viewer.wasStopped ())
  {
  }

  return (0); // 程序正常退出
}

2.2 CMakeLists.txt

cmake_minimum_required(VERSION 3.5 FATAL_ERROR)

project(min_cut_segmentation)

find_package(PCL 1.5 REQUIRED)

include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})

add_executable (min_cut_segmentation min_cut_segmentation.cpp)
target_link_libraries (min_cut_segmentation ${PCL_LIBRARIES})

3、运行结果

3.1 编译运行

bash 复制代码
mkdir build && cd build
cmake ..
make
./min_cut_segmentation

3.2 结果

  • 处理前的点云文件min_cut_segmentation_tutorial.pcd
  • 处理后

4、总结

应用场景
  • 物体分割:从背景中分离目标物体。
  • 场景分析:识别场景中的不同物体或区域。
优缺点
  • 优点:能有效处理复杂场景,分割结果较准确。
  • 缺点:对种子点选择敏感,计算复杂度较高。

5、参考理论

该算法的思想如下:

对于给定的点云,算法构建一个图,该图包含点云中的每个点作为一组顶点,以及两个额外的顶点,称为源点和汇点。图中每个对应于点云的顶点都与源点和汇点通过边连接。除此之外,每个顶点(除了源点和汇点)还有边连接对应的点与其最近邻点。

算法为每条边分配权重。共有三种不同类型的权重,让我们逐一分析:

首先,算法为点云之间的边分配权重。这个权重称为平滑代价(smooth cost),其计算公式为:
smoothCost = e − ( dist σ ) 2 \text{smoothCost} = e^{-\left(\frac{\text{dist}}{\sigma}\right)^2} smoothCost=e−(σdist)2

其中,dist 是点之间的距离。点之间的距离越远,边被切割的概率就越大。

接下来,算法设置数据代价(data cost)。数据代价由前景惩罚和背景惩罚组成。前景惩罚是连接点云点与源点的边的权重,具有用户定义的常数值。背景惩罚是连接点与汇点的边的权重,其计算公式为:
backgroundPenalty = ( distanceToCenter radius ) \text{backgroundPenalty} = \left(\frac{\text{distanceToCenter}}{\text{radius}}\right) backgroundPenalty=(radiusdistanceToCenter)

其中,distanceToCenter 是点到预期对象中心在水平面上的距离:
distanceToCenter = ( x − centerX ) 2 + ( y − centerY ) 2 \text{distanceToCenter} = \sqrt{(x - \text{centerX})^2 + (y - \text{centerY})^2} distanceToCenter=(x−centerX)2+(y−centerY)2

公式中的 radius 是该算法的输入参数,可以大致视为从对象中心向外的范围,超出该范围的点不属于前景(对象的水平半径)。

在所有准备工作完成后,算法进行最小割的搜索。基于对该割的分析,点云被分为前景点和背景点。

相关推荐
dreadp2 小时前
解锁豆瓣高清海报(二) 使用 OpenCV 拼接和压缩
图像处理·python·opencv·计算机视觉·数据分析
涛ing3 小时前
32. C 语言 安全函数( _s 尾缀)
linux·c语言·c++·vscode·算法·安全·vim
独正己身3 小时前
代码随想录day4
数据结构·c++·算法
我不是代码教父6 小时前
[原创](Modern C++)现代C++的关键性概念: 流格式化
c++·字符串格式化·流格式化·cout格式化
利刃大大6 小时前
【回溯+剪枝】找出所有子集的异或总和再求和 && 全排列Ⅱ
c++·算法·深度优先·剪枝
子燕若水6 小时前
mac 手工安装OpenSSL 3.4.0
c++
*TQK*7 小时前
ZZNUOJ(C/C++)基础练习1041——1050(详解版)
c语言·c++·编程知识点
Rachela_z7 小时前
代码随想录算法训练营第十四天| 二叉树2
数据结构·算法
细嗅蔷薇@7 小时前
迪杰斯特拉(Dijkstra)算法
数据结构·算法
追求源于热爱!7 小时前
记5(一元逻辑回归+线性分类器+多元逻辑回归
算法·机器学习·逻辑回归