基于图的图像分割 Effective graph-based image segmentation
- 三、代码实现
一、前言
最近一段时间在复现基于区域的对比度方法(region-based contrast 简称RC)的显著性检测。 其中,遇到了问题。主要是用到了基于图的图像分割。 显著性检测RC算法:其前期工作就是利用Graph-Based Image Segmentation的分割算法。主要涉及了图网络的一些知识。
Graph-Based Image Segmentation是2004年由Felzenszwalb发表在IJCV上的一篇文章,主要介绍了一种基于图表示(graph-based)的图像分割方法。图像分割(Image Segmentation)的主要目的也就是将图像(image)分割成若干个特定的、具有独特性质的区域(region),然后从中提取出感兴趣的目标(object)。而图像区域之间的边界定义是图像分割算法的关键,论文给出了一种在图表示(graph-based)下图像区域之间边界的定义的判断标准(predicate),其分割算法就是利用这个判断标准(predicate)使用贪心选择(greedy decision)来产生分割(segmentation)。
该算法在时间效率上,基本上与图像(Image)的图(Graph)表示的边(edge)数量成线性关系,而图像的图表示的边与像素点成正比,也就说图像分割的时间效率与图像的像素点个数成线性关系。这个算法有一个非常重要的特性,它能保持低变化(low-variability)区域(region)的细节,同时能够忽略高变化(high-variability)区域(region)的细节。这个性质很特别也很重要,对图像有一个很好的分割效果(能够找出视觉上一致的区域,简单讲就是高变化区域有一个很好聚合(grouping),能够把它们分在同一个区域),这也是为什么那么多人引用该论文的原因吧。
无论在分割领域还是显著性检测上,都是能够捕捉视觉上重要的区域(perceptually important regions)。举个栗子:在下图1左侧有个红三角,下图2左侧有个更大的红三角,我们可以认为图2的红三角更显眼(更加的靠左侧),
二、算法理论
该论文主要有两个关键点:
- 图像(image)的图(graph)表示;
- 最小生成树(Minimun Spanning Tree)。
2.1 构建图
图像(image)的图表示是指将图像(image)表达成图论中的图(graph)。具体说来就是,把图像中的每一个像素点看成一个顶点 (node或vertex),像素点之间的关系对(可以自己定义其具体关系,一般来说是指相邻关系)构成图的一条边 ,这样就构建好了一个图 。
相邻的两个像素点像素颜色值的差异构成边的权值。其中权值越小,表示像素点之间的相似度就越高,反之,相似度就越低。图每条边的权值是基于像素点之间的关系,可以是像素点之间的灰度值差,也可以是像素点(RGB)之间的距离:
灰度值素点之间距离:
像素点(RGB)之间的距离:
2.2 分割图
将图像表达成图之后,接下来就是要如何分割这个图。将每个节点(像素点)看成单一的区域,然后进行合并。使用最小生成树方法合并像素点,然后构成一个个区域。大致意思就是讲图(Graph)简化,相似的区域在一个分支(Branch)上面(有一条最边连接),大大减少了图的边数。
图(Graph)分割是将
2.3 算法的实现
- 分割区域(Component)的内部差(internal difference)。可以先假定图G已经简化成了最小生成树 MST,一个分割区域C 包含若干个顶点 ,顶点之间通过最小生成树的边连接。这个内部差就是指分割区域C中包含的最大边的权值。
- 分割区域(Component)之间的差别(Difference),是指两个分割区域之间顶点相互连接的最小边的权值。
- 如果两个分割部分之间没有边连接,定义。分割区域的差别可以有很多种定义的方式,可以选择中位置,或者其他的分位点(quantile,中位置是0.5分位点),但是选取其他的方式将会使得这个问题成为一个NP-hard问题。
- 分割区域(Component)边界的一种判断标准(predicate)。判断两个分割区域之间是否有明显的边界,主要是判断两个分割部分之间的差别Dif相对于和中较小的那个值MInt的大小,这里引入了一个阈值函数τ 来控制两者之间的差值。下面给出这个判断标准的定义:
- 其中,是指最小的分割内部差,其定义如下:
- 阈值函数主要是为了更好的控制分割区域边界的定义。比较直观的理解,小分割区域的边界定义要强于大分割区域,否则可以将小分割区域继续合并形成大区域。在这里给出的阈值函数与区域的大小有关。
- |C|是指分割部分顶点的个数(或者像素点个数),k是一个参数,可以根据不同的需求(主要根据图像的尺寸)进行调节。
2.4 几个分割概念
- 如果一个分割S,存在图(Graph)的分割区域之间,没有明显的边界,那么就说这个分割S“太精细”(too fine)。也就是说它们之间没有明显的分界线,硬要把它们分割开来的话,有点过头,也就是说分得太细。
- 如果一个分割S,存在一个合适的调整(refinement)S’使得S不是”太精细“,那么就说这个分割S”太粗糙“(too coarse)。简单来讲就是,分割程度的还不够(粒度还比较大),可以继续分割,这样刚开始的那个分割就是”太粗糙“(too coarse)了。
对于一个图graph来说,都存在一个分割S,既不是”太精细“(too fine)也不是”太粗糙“(too coarse)。
2.5 算法步骤
- 对于图G的所有边,按照权值进行排序(升序)
- S[0]是一个原始分割,相当于每个顶点当做是一个分割区域
- q = 1,2,…,m 重复3的操作(m为边的条数,也就是每次处理一条边)
- 根据上次的构建。选择一条边o[q](vi,vj),如果vi和vj在分割的互不相交的区域中,比较这条边的权值与这两个分割区域之间的最小分割内部差MInt,如果o[q](vi,vj) < MInt,那么合并这两个区域,其他区域不变;如果否,什么都不做。
- 最后得到的就是所求的分割 S = S[m]
三、代码实现
C++实现代码请查看:http://cs.brown.edu/people/pfelzens/segment/
numpy实现
主要使用numpy数组,去除了类的使用
cython实现
主要用来加速该算法运算时间