参考文章:pcl最小图割(pcl::MinCutSegmentation)算法
此类方法把图像分割问题与图的最小割(min cut)问题相关联。
(1)首先用一个无向图G=
(2)此处的Graph和普通的Graph稍有不同。普通的图分为有向图和无向图;由顶点和边构成,如果边的有方向的,这样的图被则称为有向图,否则为无向图,且边是有权值的,不同的边可以有不同的权值,分别代表不同的物理意义。
(3)而GraphCuts图是在普通图的基础上多了2个顶点,这2个顶点分别用符号”S”和”T”表示,统称为终端顶点。其它所有的顶点都必须和这2个顶点相连形成边集合中的一部分。所以Graph Cuts中有两种顶点,也有两种边。
(4)第一种顶点和边是:第一种普通顶点对应于图像中的每个像素。每两个邻域顶点(对应于图像中每两个邻域像素)的连接就是一条边。这种边也叫n-links。n-links具有潜在将相邻类似像素分类在一起且相邻差距较大的像素分割开的能力。
(5)第二种顶点和边是:除图像像素外,还有另外两个终端顶点,叫S(source:源点,取源头之意)和T(sink:汇点,取汇聚之意)。每个普通顶点和这2个终端顶点之间都有连接,组成第二种边。这种边也叫t-links。t-links具有潜在将目标像素分类在一起且背景像素分类在一起的能力。
通过设置目标物体的半径,三维坐标重心等参数,该算法将输入点云集合,划分为两个点集,前景点集(目标物体)和背景点云(剩余部分)。算法思想如下:
1、构造GraphCut图
对于一个给出的点云,算法会以点云中每一个点,另加一个source(源点)、一个sink(汇点),作为GraphCut图的顶点;点云中每个点与其近邻连线所形成的边叫n-links,与sink、source连线形成的边叫t-links。
2、根据连接点的类型的不同,边被分为以下三种情况,不同的情况,边被赋予不同的权值。
(1)连接输入点云各点之间的边,为上述边赋予的权值被称为平滑代价,由一下公式计算
dist为各点之间的距离,距离越大,该边被切断的概率越大。
(2)连接输入点云中的点与原点的边,该边赋予的权值,被称为前景惩罚,该值由用户输入,作为该算法的输入参数。
(3)连接输入点云中的点与汇点的边,该边赋予的权值,被称为背景惩罚,由以下公式计算
式中distanceToCenter是点到前景点云(目标点云)预期中心的水平距离,利用以下公式给出
式中radius为该算法的输入参数,可粗略看成前景点云(目标物体)的水平半径,小于该半径的点被作为前景点云(目标物体),大于该半径的点作为背景点云(其他物体)。
(3)分割
实现代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
VTK_MODULE_INIT(vtkRenderingOpenGL)
using namespace pcl::console;
int main(int argc, char** argv)
{
if (argc < 2)
{
std::cout << ".exe xx.pcd -bc 1 -fc 1.0 -nc 0 -cx 68.97 -cy -18.55 -cz 0.57 -s 0.25 -r 3.0433856 -non 14 -sw 0.5" << endl;
return 0;
}//如果输入参数小于1个,输出提示信息
time_t start, end, diff[5], option;
start = time(0);
int NumberOfNeighbours = 14;//设置默认参数
bool Bool_Cuting = false;//设置默认参数
float far_cuting = 1, near_cuting = 0, C_x = 68.97, C_y = -18.55, C_z = 0.57, Sigma = 0.25, Radius = 3.0433856, SourceWeight = 0.8;//设置默认输入参数
pcl::PointCloud <pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud <pcl::PointXYZ>);// 创建一个PointCloud 共享指针并进行实例化
if (pcl::io::loadPCDFile <pcl::PointXYZ>(argv[1], *cloud) == -1)// 加载点云数据
{
std::cout << "Cloud reading failed." << std::endl;
return (-1);
}
parse_argument(argc, argv, "-bc", Bool_Cuting);
parse_argument(argc, argv, "-fc", far_cuting);
parse_argument(argc, argv, "-nc", near_cuting);
parse_argument(argc, argv, "-cx", C_x);
parse_argument(argc, argv, "-cy", C_y);
parse_argument(argc, argv, "-cz", C_z);
parse_argument(argc, argv, "-s", Sigma);
parse_argument(argc, argv, "-r", Radius);
parse_argument(argc, argv, "-non", NumberOfNeighbours);
parse_argument(argc, argv, "-sw", SourceWeight);//设置输入参数方式
pcl::IndicesPtr indices(new std::vector <int>);//创建一组索引
if (Bool_Cuting)//判断是否需要直通滤波
{
pcl::PassThrough<pcl::PointXYZ> pass;//设置直通滤波器对象
pass.setInputCloud(cloud);//设置输入点云
pass.setFilterFieldName("z");//设置指定过滤的维度
pass.setFilterLimits(near_cuting, far_cuting);//设置指定纬度过滤的范围
pass.filter(*indices);//执行滤波,保存滤波结果在上述索引中
}
pcl::MinCutSegmentation<pcl::PointXYZ> seg;//创建一个PointXYZRGB类型的区域生长分割对象
seg.setInputCloud(cloud);//设置输入点云
if (Bool_Cuting)seg.setIndices(indices);//设置输入点云的索引
pcl::PointCloud<pcl::PointXYZ>::Ptr foreground_points(new pcl::PointCloud<pcl::PointXYZ>());//创建一个PointCloud 共享指针并进行实例化
pcl::PointXYZ point;//定义中心点并赋值
point.x = C_x;
point.y = C_y;
point.z = C_z;
foreground_points->points.push_back(point);
seg.setForegroundPoints(foreground_points);//输入前景点云(目标物体)的中心点
seg.setSigma(Sigma);//设置平滑成本的Sigma值
seg.setRadius(Radius);//设置背景惩罚权重的半径
seg.setNumberOfNeighbours(NumberOfNeighbours);//设置临近点数目
seg.setSourceWeight(SourceWeight);//设置前景惩罚权重
std::vector <pcl::PointIndices> clusters;
seg.extract(clusters);//获取分割的结果,分割结果保存在点云索引的向量中。
std::cout << "Maximum flow is " << seg.getMaxFlow() << std::endl;//计算并输出分割期间所计算出的流值
pcl::PointCloud <pcl::PointXYZRGB>::Ptr colored_cloud = seg.getColoredCloud();//对前景点赋予红色,对背景点赋予白色。
pcl::visualization::PCLVisualizer viewer("点云库PCL学习教程第二版-最小割分割方法");
viewer.addPointCloud(colored_cloud);
viewer.addSphere(point, Radius, 122, 122, 0, "sphere");
viewer.setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, 0.2, "sphere");
while (!viewer.wasStopped())
{
viewer.spin();
}//进行可视化
return (0);
}
黄色点为中心点,半径为Radius,可粗略看成前景点云(目标物体)的水平半径,小于该半径的点被作为前景点云(目标物体),大于该半径的点作为背景点云(其他物体)。
显然,最小割算法更注重分割的精确性而不是分割自动进行。最小割算法用于半自动分割识别有着巨大的优势,适合用于计算机视觉、城市场景点云分析一类。但对机器人来说,或许和特征点检测算法联合起来能获得较好的效果。
最小割算法必须先由人为指定至少一个前景点,该前景点必须在待分割的目标物体上才行;该算法适用于多个物体水平排列的点云图像,它只能在水平方向将目标物体分割出来,默认分割后的图形中,白色为前景点,红色为背景点。