最大流与最小割(Maxflow与Mincut)
传统图像主要分割算法:
基于阈值的分割
(1)固定阈值分割
(2)直方图双峰法
(3)迭代阈值图像分割
(4)自适应阈值图像分割
(5)最佳阈值法
2.基于边缘的分割
(1)Canny边缘检测器
(2)Harris角点检测器
(3)Sift检测器
(4)Surf检测器
3.基于区域的分割
(1)种子区域生长法
(2)区域分裂合并法
(3)分水岭法
4.基于图论的分割
(1)GraphCut图割
(2)GrabCut分割和抠图
5.基于能量泛函的分割
(1)参数主动轮廓模型
(2)ASM
(3)AAM
(4)CLM
(5)GAC
该文章讲述的最大流与最小割实际上就是图论分割的主要思想。将图像映射为带权无向图,把像素视作节点,将图像分割问题看作是图的顶点划分问题,利用最小剪切准则得到图像的最佳分割。
最大流算法
最大流算法就像水管水流,如何保证S点到T点的水流量最大。其中点与点之间的表示水管最大容量。
最大流解法有很多,例如Ford-Fulkerson算法,Edmond-Karp 算法,Dinic算法等。
Ford-Fulkerson算法
原始图
剩余流量图
在剩余流量图中随便找一条从s到t的路线
减去最小容量
然后构建反向箭头
接着再找一条从s到t的路线,重复上面的计算
继续找新路线
此时已经找不到新的路径从s到t,结束进程。
再利用公式flow = capacity - residual
- =
既可以得出最大流5。
代码实现:
class Edge():''' 流网络中的边 '''def __init__(self, v, w, cap, flow=0):'''定义一条边 v→w:param v: 起点:param w: 终点:param cap: 容量:param flow: v→w上的流量'''self.v, self.w, self.cap, self.flow = v, w, cap, flowdef other_node(self, p):''' 返回边中与p相对的另一顶点 '''return self.v if p == self.w else self.wdef residual_cap_to(self, p):'''计算残存边的剩余容量如果p=w,residual_cap_to(p)返回 v→w 的剩余容量如果p=v,residual_cap_to(p)返回 w→v 的剩余容量'''return self.cap - self.flow if p == self.w else self.flowdef moddify_flow(self, p, x):''' 将边的流量调整x '''if p == self.w: # 如果 p=w,将v→w的流量增加xself.flow += xelse: # 否则将v→w的流量减少xself.flow -= xdef __str__(self):return str(self.v) + '→' + str(self.w)class Network():''' 流网络 '''def __init__(self, E: list, s: int, t: int):''':param E: 边集:param s: 原点:param t: 汇点:return:'''self.E, self.s, self.t = E, s, tdef edges_from(self, v):''' 从v顶点流出的边 '''return [edge for edge in self.E if edge.v == v]def edges_to(self, v):''' 流入v顶点的边 '''return [edge for edge in self.E if edge.w == v]def edges(self, v):''' 连接v顶点的所有边 '''return self.edges_from(v) + self.edges_to(v)def flows_from(self, v):'''v顶点的流出量 '''edges = self.edges_from(v)return sum([e.flow for e in edges])def flows_to(self, v):''' v顶点的流入量 '''edges = self.edges_to(v)return sum([e.flow for e in edges])def check(self):''' 源点的流出是否等于汇点的流入 '''return self.flows_from(self.s) == self.flows_to(self.t)def display(self):if self.check() is False:print('该网络不符合守恒定律')returnprint('%-10s%-8s%-8s' % ('边', '容量', '流'))for e in self.E:print('%-10s%-10d%-8s' %(e, e.cap, e.flow if e.flow < e.cap else str(e.flow) + '*'))class FordFulkerson():def __init__(self, G: Network):self.G = Gself.max_flow = 0 # 最大流class Node:''' 用于记录路径的轨迹 '''def __init__(self, w, e: Edge, parent):''':param w: 顶点:param e: 从上一顶点流入w的边:param parent: 上一顶点'''self.w, self.e, self.parent = w, e, parentdef dfs(self):''' 获取网络中的一条增广路径 '''path = Nonevisited = set() # 被访问过的顶点visited.add(self.G.s)q = []q.append(self.Node(self.G.s, None, self.G.t))tempmaxflow = 1e10while len(q):node_v = q.pop(0)v = node_v.wlabel = 0for e in self.G.edges(v): # 遍历连接v的所有边w = e.other_node(v) # 边的另一顶点,e的指向是v→w# v→w有剩余容量且w没有被访问过if e.residual_cap_to(w) > 0 and w not in visited:visited.add(w)node_w = self.Node(w, e, node_v)q.append(node_w)if w == self.G.t: # 到达了汇点path = node_wlabel = 1breakif label == 1: # 到达了汇点breakif path is None:tempmaxflow = 0return tempmaxflownode = pathwhile node.parent != self.G.t: # 计算增广路径上的最小剩余量w, e = node.w, node.etempmaxflow = min(tempmaxflow, e.residual_cap_to(w))node = node.parentnode = pathwhile node.parent != self.G.t: # 修改残存网w, e = node.w, node.ee.moddify_flow(w, tempmaxflow)node = node.parentreturn tempmaxflowdef start(self):''' 增广路径最大流算法主体方法 '''while True:tempmaxflow = self.dfs() # 找到一条增广路径if tempmaxflow ==0:breakself.max_flow += tempmaxflow # 扩充最大流def display(self):print('最大网络流 = ', self.max_flow)print('%-10s%-8s%-8s' % ('边', '容量', '流'))for e in self.G.E:print('%-10s%-10d%-8s' %(e, e.cap, e.flow if e.flow < e.cap else str(e.flow) + '*'))E = [Edge(1, 2, 4), Edge(1, 3, 2), Edge(2, 4, 2), Edge(2, 5, 4),Edge(2, 3, 1), Edge(3, 5, 2), Edge(4, 6, 3), Edge(5, 6, 3)]
s, t = 1, 6
G = Network(E, s, t)
ford_fullkerson = FordFulkerson(G)
ford_fullkerson.start()
ford_fullkerson.display()
Ford-Fulkerson算法的时间复杂度较高,接下来讲述一种使用较为广泛,时间复杂度低的Dinic算法。
Dinic算法
Dinic算法首先根据剩余流量图建立一个Level Graph,如下:
然后在level graph上寻找阻塞流,再将其映射回原先的residual graph中。
然后在新的residual graph中再次建立level graph图。
再寻找阻塞流,然后再映射回residual graph。如此不断循环,直到在新生成的level graph中找不到从s到t的阻塞流。
再利用公式flow = capacity - residual
-=
最大流量为19。
代码实现
class Edge():''' 流网络中的边 '''def __init__(self, v, w, cap, flow=0):'''定义一条边 v→w:param v: 起点:param w: 终点:param cap: 容量:param flow: v→w上的流量'''self.v, self.w, self.cap, self.flow = v, w, cap, flowdef other_node(self, p):''' 返回边中与p相对的另一顶点 '''return self.v if p == self.w else self.wdef residual_cap_to(self, p):'''计算残存边的剩余容量如果p=w,residual_cap_to(p)返回 v→w 的剩余容量如果p=v,residual_cap_to(p)返回 w→v 的剩余容量'''return self.cap - self.flow if p == self.w else self.flowdef moddify_flow(self, p, x):''' 将边的流量调整x '''if p == self.w: # 如果 p=w,将v→w的流量增加xself.flow += xelse: # 否则将v→w的流量减少xself.flow -= xdef __str__(self):return str(self.v) + '→' + str(self.w)class Network():''' 流网络 '''def __init__(self, E: list, s: int, t: int):''':param E: 边集:param s: 原点:param t: 汇点:return:'''self.E, self.s, self.t = E, s, tdef edges_from(self, v):''' 从v顶点流出的边 '''return [edge for edge in self.E if edge.v == v]def edges_to(self, v):''' 流入v顶点的边 '''return [edge for edge in self.E if edge.w == v]def edges(self, v):''' 连接v顶点的所有边 '''return self.edges_from(v) + self.edges_to(v)def flows_from(self, v):'''v顶点的流出量 '''edges = self.edges_from(v)return sum([e.flow for e in edges])def flows_to(self, v):''' v顶点的流入量 '''edges = self.edges_to(v)return sum([e.flow for e in edges])def check(self):''' 源点的流出是否等于汇点的流入 '''return self.flows_from(self.s) == self.flows_to(self.t)def display(self):if self.check() is False:print('该网络不符合守恒定律')returnprint('%-10s%-8s%-8s' % ('边', '容量', '流'))for e in self.E:print('%-10s%-10d%-8s' %(e, e.cap, e.flow if e.flow < e.cap else str(e.flow) + '*'))class FordFulkerson():def __init__(self, G: Network):self.G = Gself.max_flow = 0 # 最大流class Node:''' 用于记录路径的轨迹 '''def __init__(self, w, e: Edge, parent):''':param w: 顶点:param e: 从上一顶点流入w的边:param parent: 上一顶点'''self.w, self.e, self.parent = w, e, parentdef bfs(self):visited = {self.G.s}tempvisited = {self.G.s}q = []q.append(self.Node(self.G.s, None, self.G.t))q.append("end")NewE = []label = 0while len(q)>1:node_v = q.pop(0)if node_v=="end":q.append("end")visited = tempvisited.copy()continuev = node_v.wfor e in self.G.edges(v): # 遍历连接v的所有边w = e.other_node(v) # 边的另一顶点,e的指向是v→wif e.residual_cap_to(w) > 0 and w not in visited and e not in NewE:tempvisited.add(w)node_w = self.Node(w, e, node_v)q.append(node_w)NewE.append(e)if w == self.G.t: # 到达了汇点label = 1return Network(NewE,self.G.s,self.G.t),labeldef dfs(self,NewNetwork):''' 获取网络中的一条增广路径 '''path = Nonepath2 = Nonevisited = set() # 被访问过的顶点visited.add(self.G.s)q = []q.append(self.Node(self.G.s, None, self.G.t))q2 = []q2.append(self.Node(self.G.s, None, self.G.t))tempmaxflow = 1e10while len(q):node_v = q.pop(0)node_v2 = q2.pop(0)v = node_v.wlabel = 0for e in NewNetwork.edges(v): # 遍历连接v的所有边w = e.other_node(v) # 边的另一顶点,e的指向是v→we2 = [edge for edge in self.G.E if edge==e][0]# v→w有剩余容量且w没有被访问过if e.residual_cap_to(w) > 0 and w not in visited:visited.add(w)node_w = self.Node(w, e, node_v)q.append(node_w)node_w2 = self.Node(w, e2, node_v2)q2.append(node_w2)if w == self.G.t: # 到达了汇点path = node_wpath2 = node_w2label = 1breakif label == 1: # 到达了汇点breakif path is None:tempmaxflow = 0return tempmaxflownode = pathwhile node.parent != self.G.t: # 计算增广路径上的最小剩余量w, e = node.w, node.etempmaxflow = min(tempmaxflow, e.residual_cap_to(w))node = node.parentnode = path2while node.parent != self.G.t: # 修改残存网w, e = node.w, node.ee.moddify_flow(w, tempmaxflow)node = node.parentreturn tempmaxflowdef start(self):while True:newnet,label = self.bfs()if label==0:breakwhile True:tempmaxflow = self.dfs(newnet) # 找到一条增广路径if tempmaxflow == 0:breakself.max_flow += tempmaxflow # 扩充最大流def display(self):print('最大网络流 = ', self.max_flow)print('%-10s%-8s%-8s' % ('边', '容量', '流'))for e in self.G.E:print('%-10s%-10d%-8s' %(e, e.cap, e.flow if e.flow < e.cap else str(e.flow) + '*'))E = [Edge(1, 2, 10), Edge(1, 3, 10), Edge(2, 4, 4), Edge(2, 5, 8),Edge(2, 3, 2), Edge(3, 5, 9), Edge(4, 6, 10), Edge(5, 6, 10),Edge(5, 4, 6)]
s, t = 1, 6
G = Network(E, s, t)
ford_fullkerson = FordFulkerson(G)
ford_fullkerson.start()
ford_fullkerson.display()
最小割算法
最小割实际上就是使用的最大流的计算结果。
最大流与最小割之间转化:将最大流的剩余容量图画出来,然后重起点s出发,将可以连在一块的设为s,另一边即为t
总结:
最大流与最小割是图论分割的基础。接下来我们将继续探讨最大流与最小割如何在图像上实现分割。例如graph cut等。
参考文献:
https://www.its203.com/article/qq_35885429/107226535
https://github.com/wangshusen/AdvancedAlgorithms
最大流与最小割(Maxflow与Mincut)相关推荐
- 网络流:最大流,最小割 基本概念及算法
原文:http://www.cnblogs.com/Booble/archive/2011/03/04/1970453.html 参考:http://community.topcoder.com/tc ...
- 网络流中最大流和最小割算法
学习顺序按照下图的改进历程 问题转化:寻找初始解,提升,达到条件停止 求解优化,加回溯边,画残差图,在残差图中寻找一条s可到t的路径 看最大流最小割问题,证明 除 ST外每个点的出度和入度相等,S和T ...
- 网络流——最大流和最小割
最小割问题 ststst割:将节点划分为A,BA,BA,B两个集合,其中源节点s∈As\in As∈A且宿节点t∈Bt\in Bt∈B ststst割的容量:由集合AAA到集合BBB所有出边容量之和. ...
- 如何快速理解最大流和最小割
摘要: 割从哪来-------->最大流和最小割之间的等价关系的阐述. 1.问题引入 1.1思考这样一个问题:在给定的图中,如何判断一个源点s到终点t是否有路径存在呢? 我们首先想到的是用BFS ...
- BZOJ 1001: [BeiJing2006]狼抓兔子【最大流/SPFA+最小割,多解】
1001: [BeiJing2006]狼抓兔子 Time Limit: 15 Sec Memory Limit: 162 MB Submit: 23822 Solved: 6012 [Submit ...
- Forethought Future Cup - Elimination Round G. Zoning Restrictions 最大流(最小割)
题目链接: https://codeforces.com/contest/1146/problem/G 题意: 你现在要给 nnn 个位置制定高度 hi(1<=hi<=H)h_i (1&l ...
- 网络流之最大流和最小割
最大流问题 最大流:给定有向图中每条边的最大流量(容量),求从源点到汇点的最大流量. 容量网络: 括号左边代表容量,右边代表流量. 残留网络:流网络中剩余可增加的流量 增广路:满足容量条件的一条流量不 ...
- 最大流,最小割刷题记录
T1 P2057 [SHOI2007] 善意的投票 题目 https://www.luogu.com.cn/blog/OnMyOwn/OnMyOwn https://www.luogu.com.cn/ ...
- 最大流最小割经典例题_算法: 最大流与最小割
什么是最大流 最大流要解决的问题是从 S 到 T 怎么才能最大地将数据运到另一边.这个"数据"可以是水,或者网络数据包.举个例子 在上面这个图中将数据从 S 运到 T,其中边的权值 ...
最新文章
- (011) java后台开发之泛型类
- python 基础学习 正则表达式1(规则)
- day13 Python数据基本类型
- int main中char** argv与char *argv[]区别?(main函数)
- 机器学习术语_机器学习术语神秘化。
- 计算机史上首篇教你从算法问题提炼算法思想的文章
- 阿里主管通知我试用期延期……
- php path separator,php 非常有用的高级函数PATH_SEPARATOR常量和set_include_path
- Python | 面试必问,线程与进程的区别,Python中如何创建多线程?
- Gsview裁剪EPS文件
- 【路径规划】基于matalb穷举法机器人栅格地图避障路径规划【含Matlab源码 1675期】
- 程序员小技巧-----如何提升电脑速度
- 【每天更新】2022年最新WordPress主题下载(2022-5-12)
- 计算机硬盘中病毒吗,硬盘中病毒能格式化吗
- HDU6069	Counting Divisors
- 根号3表白html,数字表白公式 表白套路情话
- 计算机文字录入培训大纲,计算机文字录入处理员教学大纲.doc
- 服务器域名绑定公网IP地址
- 在线查看相机快门次数_我是亲民_新浪博客
- 音视频基础学习之【07.仿优酷界面的UI设计】