分割算法|Efficient Graph-Based Image Segmentation

本文的基本结构:

论文的笔记

源码的分析笔记

一 本文的分割思想——无向图分割

1 名词和符号介绍

无向图G,每个像素是一个顶点v_i\in VG = (V,E)

V是顶点集合,E是邻边集合,\(v_i,v_j\)\in E

C(component):可以理解为分割后的每一个部分(类似region)

S(segmentation)是V划分成components的结果,每个component C \in S

2 分割的思想

2.1 分割原则

在同一个component内相邻顶点的边的权值应尽量小,不同component内的顶点的权重较大

2.2 分割方法

定义一个参数D:其值判断了两个component的之间是否有边界(boundary)即要不要合并

定义internal difference (内差):代表一个部分(component)的属性

对于一个component C \subseteq V 是C的最小生成树MST(C,E)中的最大权值,即

Int(C) =\mathop{max}\limits_{e \in MST(C,E)}w(e)

定义外差:代表了两个部分间的属性

两个component的差值为

        Dif(C_1,C_2) =\mathop{min}\limits_{v_i\in C_1,v_j\in C2,(v_i,v_j)\in E}w((v_i,v_j))

若两个component之间无任何邻边,则值等于无穷

2.3 分割的判断条件

至此,可以定义

D的法则为,Dif(C_1,C_2)大于至少其中一个component的内差(即Int(C_1),Int(C_2)),

表达式为:

D(C_1,C_2) = \begin{cases} true & \text{ if } Dif(C_1,C_2)>MInt(C_1,C_2) \\ false & \text{ if } otherwise \end{cases}

        其中,MInt(C_1,C_2) = min(Int(C_1)+\tau (C_1),Int(C_2)+\tau (C_2))

\tau是阈值函数,控制外差需要大于内差的最小值

阈值函数的计算公式为

\tau(C) = k/|C|

|C|C的大小,也就是顶点个数,k是一个常量参数,这样做也保护了小的component(数量较少)在进行判断时,不容易被判定为true

3 分割算法

3.1 分割情况的定义

称分割是too fine:个人认为意思就是分的太细了

称分割是too coarse:too fine的对立(分的太粗了)

proper refinement:假设对于一个图像,有两个分割结果T,S,都是存储了分割后的components;

若T的每一个component都是S中某个component的子集(或两个component相等,则称T是S的一个proper refinement,T比Sfiner,S比Tcoarse(T分的太细了,S分的太粗了

3.2 算法伪代码

简单来说就是

【初始化】将每个像素看成无向图的一个顶点,相邻像素连接形成边,边的权重是计算的两个顶点的不相似度 ,按权重从小到大对边排序

【分割】初始状态每个顶点都是一个component,然后逐边判断两个顶点用上文中的D函数判断是否要分割开,逐渐的融合component;重复操作直至到我们的循环退出条件

【退出条件】当前的分割结果为既不是too fine也不是too coarse(初始条件为too fine,因为每个顶点都是component,肯定是太细了,逐渐向coarse靠拢,文章中证明了这个算法是可以实现要求的分割结果的,是一个单调的过程)

二 看代码

文件包里有文件

  1. image.h
  2. disjoint-set.h
  3. segment-graph.h
  4. imutil.h
  5. convolve.h
  6. imconv.h
  7. segment-image.h

我排了一下顺序,大致按了层级关系,即序号越大的文件越需要包含前面头文件里定义的函数

1 首先第一个文件image.h

1.1 定义了一个使用类模板的image类,因为分割算法就是在图像上进行的,所以定义图像类方便后续的操作

    类中的方法有

  • image(const int width,const int height,const bool init):初始化宽,高以及init;init为true时,图像用0填充
  • ~image():删除
  • void init(const T &val):用val填充整个图像
  • image<T> *copy() :复制一个新的图像
  • int width():取图像的宽度
  • int height():取图像的高度

1.2 define了两个函数:

imRef(im,x,y):取图像im在坐标(x,y)上的值

imPtr(im,x,y): 取图像im在坐标(x,y)处的指针

2 第二个文件disjoint-set.h(中文译名不相交集)

2.1 头文件

包含头文件image.h

2.2 内容

2.2.1定义结构体uni_elt(表示森林):

        结构体内有三个变量:

  •         rank
  •         p: 树的根节点,
  •         size: 树包含的结点数

2.2.2 定义类universe

universe的方法有

  • universe(int elements)初始化一个长度为elements的uni_elt数组,各元素的rank为0,size = 1,p = i
  • ~universe() 删除
  • int find(int x) 找x所在树的祖先节点
  • void join(int x, int y):合并结点x,y所在的树

3 第三个文件 segment-graph.h

3.1 包含头文件"disjoint-set.h"

3.2 内容

3.2.1 结构体edge:{ 权值,顶点编号}

3.2.2 阈值函数THRESHOLD

3.2.3 重载:重载运算符 < 比较结构体a,b的权值

3.2.4 定义函数segment_graph(int num_vertices,int num_edges, edge *edges, float c) 返回universe类的对象

  函数实现的操作:

  • 将edges按权值从小到大排序
  • 生成num_vertices数量的不相交森林
  • 初始化一个阈值数组,对应森林的每棵树的阈值
  • 循环遍历每个边
    • 判断当前边是否属于一棵树
    • 若不在一棵树上,判断边的权值是否小于两端节点的阈值
      • 若均小于,则融合两端节点所在的两颗树,更新根结点,更新根节点的阈值

4 imutil.h

包含了两个函数

min_max :返回输入图像的最大值和最小值

threshold :返回一个新的图像,其值为原图像对应位置与阈值t比较后的结果

5 convolve.h

5.1 介绍

        这个文件里定义了两种图像卷积的方法,实现卷积操作

5.2 定义函数

函数convolve_even 卷积(对像素):相当于以pixel为起点,沿x的方向放置滤波器,朝左和朝右都要放置(对称),然后对应位置先相乘,再两个方向相乘的值相加,起点位置只乘一次,所有的乘积和相加作为新的图像相应位置的像素值

函数convolve_odd 卷积:同理,只不过两个方向的相乘和是相减的(朝左方向的乘积和减去朝右方向的)

6 imconv.h

6.1 介绍

定义了一堆函数,其功能都是对图像的数据类型的转换,太多了就不列述了

6.2 部分函数

函数imageRGBtoGRAY:将RGB图像转换成灰度图像,RGB权值分别为0.299 0.587 0.114

函数imageGRAYtoRGB:灰度图像转RGB

函数imageUCHARtoFLOAT:图像中像素值的数据类型的转换

7 filter.h

7.1 介绍

这个头文件就正式对图像进行卷积操作了,定义的都是实现滤波操作的方法

7.2 内容

A 函数normalize:归一化

B 宏定义建立一个filter函数:可以指定函数名以及滤波器使用的函数fun,

代码里定义了一个高斯滤波器make_gaussian,用的是fun= \exp(-0.5*\sqrt{\frac{i}{\sigma}})

【滤波器输入参数\sigma

len = (int)\lceil \sigma*WIDTH \rceil + 1

        文件中默认WIDTH = 4.0 

【函数的作用】返回一个vector类型的mask,长度为len,每个位置对应的值为由输入的fun计算

C smooth函数

对输入函数卷积两次,得到一个滤波后的相同尺寸的图像

D laplacian函数

解释太累了 ,就是逐像素做上述计算操作

来到了最重点的一个头文件

8 segment-image.h

8.1 介绍

这个头文件就是综合了上述各文件中的函数方法,开始实现对图像的分割操作

8.2 内容

A 函数diff:像素的不相似度的计算

计算两个点的r,g,b三通道分别的差值的平方和的均方根

B segment_image(rgb图像

实现的操作:

  • 对每个通道用参数为sigma的滤波器滤波
  • 建立一个无向图edges,遍历全像素,链接相邻的像素(方向为向右,向下,右下,右上),存储每个边,权值由diff函数计算
  • 用segment_graph分割无向图
  •  处理一些小的components,合并小于min_size的树
  • 给处理后的图像各个component随机选一个颜色上色
  • 就可以输出啦!

三 补充说明

1 开源代码里还有两个文件misc.h和pnmfile.h 前者是重载了很多运算符,后者是文件操作,用来读写图片的,就不细细学习了。

2 运行代码包里的segment.cpp 就能实现分割

猜你喜欢

转载自blog.csdn.net/weixin_45581089/article/details/119968046
今日推荐