利用图分割算法对图片进行分割是目前比较流行的算法之一。利用这种方法对图片进行分割时需要对相应的能量函数进行最小值搜索,当能量函数在定义域中属于凸函数时利用梯度下降法、EM等经典算法可以得到唯一的极值解,该解便是全局最小解。但是对于能量函数在定义域中不是凸函数,利用上面的经典算法很容易陷入局部极小值问题,从而很难得到理想效果。虽然采用模拟退火方法可以减轻陷入局部极小值可能性,但是这并不是最优的解法。
利用图模型求解能量函数最优解给这类问题的解答带来了新的途径,而mincut算法就是求解图模型的一种有效的方法。下面就直观的介绍mincut算法最终目的是什么?它的一种求解算法max-flow其基本思路是什么?并给出一个mincut算法的例子
mincut算法最终目的是解决能量函数问题
利用能量函数构建等价的图模型结构,将能量函数的优化求解转化为等价直观的图模型分割问题。mincut算法就是分割图模型的方法,从而得到能量函数的最优解。下面介绍能量函数和图模型的关系。假设有如下的能量函数,右边第一项是单节点势能函数,第二项是节点之间的势能函数,在本例中x在{0,1}中取值。要解决的问题是如何分配n个x的值使得能量函数取值最小。
下面是图模型及其分割的示例图,s节点和平面上灰色节点之间权重为lambda,t节点和平面上灰色节点之间权重为-lambda,平面灰色节点之间的权重值取beta。通过mincut算法得到下图右边绿线的分割结果。该结果表明绿线上面p、q一遍的灰色节点归于s,另一面归于t,这样分割的结果就是元素{s}和{t}之间权重的综合最小,也就是对应目标能量函数的能量最小。具体的过程最后给出一个具体的实现例子,这里的解释可以见文献“Exact
maximum a posteriori estimation for binary images”该文献是第一个提出利用图模型解决能量函数优化问题的。
max-flow算法基本思想
从max-flow字面意思便可以看出该算法和流量有关系,下面是一个说明该算法基本思想的一个示例图。将下图s看成一个水源,t是水源的归属地,中间是边缘是一系列的管道,管道的最大流量由边缘上面的数据(x,y)中x规定,y表示当该管道系统达到最大流量时每个管道的实际流量。例如v0和v3之间管道最大流量是5,当管道系统达到最大流量时该管道的实际流量为4。由图示可以看出,当该管道系统达到最大流量时,一共有3根管道流量达到满载荷状态,这3根管道是s->v0,s->v1,v2->v5这3根管道决定了该图模型的分割结果,该结果如途中虚线所示。系统达到最大流量时对应能量函数的最小值这两者是等价关系,并不矛盾。
一个mincut算法的例子
这里有一个2*2的图模型,利用mincut算法对其进行分割,该算法的源代码可以在该网站下载http://pub.ist.ac.at/~vnk/software.html。
add_edge(node_id _i, node_id _j, captype cap, captype rev_cap)
该函数定义节点i和j之间的权重,cap是i->j的权重,rev_cap是j->i的权重。这个是有方向的。
add_tweights(node_id i, tcaptype cap_source, tcaptype cap_sink)
该函数定义节点i和s、t之间的权重,cap_source是i和s的权重,cap_sink是i和t的权重。这个没有方向。
下面是整个图模型的描述代码运行结果如下图所示,结果表明节点0,1归于t集、节点2,3归于s集,并显示目前系统最大流量为8。
以上分析的具体过程如下:
现在有{2,3,s}、{0,1,t}这两种分类下面是得出{2,3,s}->{0,1,t}权重和的过程,权重求和只计算有边缘连接的情况,如s->t不存在边缘所以不计算这两者的权重。
s到0和1的权重和为1+3=4;
2到1和t的权重和为1+1=2;因为2和3不相邻所以没有权重或者权重为0;
3到0和t的权重和为1+1=2;
C=4+2+2=8;
假如另一种分割方法:0属于s,1,2,3属于t此时的C=18,比理想分割方法要大,这种方法虽然C值大但是实际流量并不会这么大,因为从1,2,3到t的管道不能实现这么大的流量。
s到1,2,3权重和为3+5+4=12;
0到1,3,t的权重和为1+1+4=6;
C=12+6=18;
int main() { typedef Graph<int,int,int> GraphType; GraphType *g = new GraphType(/*estimated # of nodes*/ 2, /*estimated # of edges*/ 1); g -> add_node(); g -> add_node(); g -> add_node(); g -> add_node(); g -> add_tweights( 0, /* capacities */ 1, 4 );//表示节点0和s的权重为1,节点0和t的权重为4 g -> add_tweights( 1, /* capacities */ 3, 4 ); g -> add_tweights( 2, /* capacities */ 5, 1 ); g -> add_tweights( 3, /* capacities */ 4, 1 ); g -> add_edge( 0, 1, /* capacities */ 1, 1 );//表示节点0->1的权重为1,<span style="line-height: 1.5; font-family: SimSun;">表示节点1->0的权重为1</span> g -> add_edge( 1, 2, /* capacities */ 1, 1); g -> add_edge( 2, 3, /* capacities */ 1, 1 ); g -> add_edge( 3, 0, /* capacities */ 1, 1 ); int flow = g -> maxflow(); printf("Flow = %d\n", flow); printf("Minimum cut:\n"); if (g->what_segment(0) == GraphType::SOURCE) printf("node0 is in the SOURCE set\n"); else printf("node0 is in the SINK set\n"); if (g->what_segment(1) == GraphType::SOURCE) printf("node1 is in the SOURCE set\n"); else printf("node1 is in the SINK set\n"); if (g->what_segment(2) == GraphType::SOURCE) printf("node2 is in the SOURCE set\n"); else printf("node2 is in the SINK set\n"); if (g->what_segment(3) == GraphType::SOURCE) printf("node3 is in the SOURCE set\n"); else printf("node3 is in the SINK set\n"); delete g; system("pause"); return 0; }