Maximum flow and minimum cut (Maxflow and Mincut)

Traditional image main segmentation algorithm:

  1. Threshold based segmentation

    (1) Fixed threshold segmentation

    (2) Histogram bimodal method

    (3) Iterative threshold image segmentation

    (4) Adaptive threshold image segmentation

    (5) Optimal threshold method

2. Edge-based segmentation

    (1) Canny edge detector

    (2) Harris corner detector

    (3) Sift detector

    (4) Surf detector

3. Region-based segmentation

    (1) Seed region growth method

    (2) Region splitting and merging method

    (3) Watershed method

4. Segmentation based on graph theory

    (1) GraphCut graph cut

    (2) GrabCut segmentation and matting

5. Segmentation based on energy functional

    (1) Parametric active contour model

    (2)ASM

    (3)AAM

    (4)CLM

    (5)GAC

      The maximum flow and minimum cut described in this article are actually the main ideas of graph theory segmentation. The image is mapped to a weighted undirected graph, the pixels are regarded as nodes, the image segmentation problem is regarded as the vertex division problem of the graph, and the optimal segmentation of the image is obtained by using the minimum shear criterion.

Maximum Flow Algorithm

       The maximum flow algorithm is like water flow in a water pipe, how to ensure the maximum water flow from point S to point T. Among them, the points between the points indicate the maximum capacity of the water pipe.

6acde457ba8c326ba08562c72e868674.png

       There are many maximum flow solutions, such as Ford-Fulkerson algorithm, Edmond-Karp algorithm, Dinic algorithm and so on.

Ford-Fulkerson Algorithm

639b1981f4a81a760d5e7e8c81df6112.png

 The original image

bd9e374f677e82999d1d07e82fbd46ba.png

residual flow diagram

       Randomly find a route from s to t in the remaining flow graph

e841f27fdd6b59269da4c90725440920.png

       minus minimum capacity

e37c7847b1c15c21df3529c0e6b4995f.png

       then construct the reverse arrow

002248843e703e7e99e8a19be06d5098.png

       Then find a route from s to t, repeat the above calculation

d0e0f561c43051f4ddcc7cf5ac38a67a.pngd18df00154b502898f5d88c09a383340.png

b7b8e0e38683f8bd48de36c8792964c9.png

       continue to find new routes

b73efb83040a38ac05bdfe2609f6dc5e.png94eefe924d5dd329faeb206dbc0b1c87.png

5f3aaf506416dd32bcbb930bb71179d2.png

       At this time, no new path can be found from s to t, and the process ends.

       Reuse the formula flow = capacity - residual

f257313302640437e54502c289849d2b.png - cf6611b0e07e02dc1f30f02dd17a7795.png=1014f592d2ac0b880ab1696fbe8a0474.png

       The maximum flow 5 can be obtained.

       Code:

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, flow


    def other_node(self, p):
        ''' 返回边中与p相对的另一顶点 '''
        return self.v if p == self.w else self.w


    def 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.flow


    def moddify_flow(self, p, x):
        ''' 将边的流量调整x '''
        if p == self.w:  # 如果 p=w,将v→w的流量增加x
            self.flow += x
        else:  # 否则将v→w的流量减少x
            self.flow -= x


    def __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, t


    def 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('该网络不符合守恒定律')
            return
        print('%-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 = G
        self.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, parent


    def dfs(self):
        ''' 获取网络中的一条增广路径 '''
        path = None
        visited = set()  # 被访问过的顶点
        visited.add(self.G.s)
        q = []
        q.append(self.Node(self.G.s, None, self.G.t))
        tempmaxflow = 1e10
        while len(q):
            node_v = q.pop(0)
            v = node_v.w
            label = 0
            for 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_w
                        label = 1
                        break
            if label == 1:  # 到达了汇点
                break
        if path is None:
            tempmaxflow = 0
            return tempmaxflow
        node = path
        while node.parent != self.G.t:  # 计算增广路径上的最小剩余量
            w, e = node.w, node.e
            tempmaxflow = min(tempmaxflow, e.residual_cap_to(w))
            node = node.parent
        node = path
        while node.parent != self.G.t:  # 修改残存网
            w, e = node.w, node.e
            e.moddify_flow(w, tempmaxflow)
            node = node.parent
        return tempmaxflow


    def start(self):
        ''' 增广路径最大流算法主体方法 '''
        while True:
            tempmaxflow = self.dfs()  # 找到一条增广路径
            if tempmaxflow ==0:
                break
            self.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()

       The Ford-Fulkerson algorithm has a high time complexity. Next, a Dinic algorithm that is widely used and has a low time complexity is described.

Dinic algorithm

       The Dinic algorithm first establishes a Level Graph based on the remaining flow graph, as follows:

ab52481e5aab6b9b5108acb64ca7be3e.pngafde0bcec87b9a60a7145ea64327af9a.png

       Then look for the blocking flow on the level graph, and map it back to the original residual graph.

46a49622852239e400a1ff5482ee4580.pngde1eaf5ee220532b3632069129aff064.png

       Then create the level graph again in the new residual graph.

b372c72226220f1530f5dafc58dd9d59.png5cb723c82e687c78b1ed96c7f489df89.png

       Then look for the blocking flow, and then map back to the residual graph. This loop continues until no blocking flow from s to t can be found in the newly generated level graph.

aedb517500475de6debc8290efcaf0b5.png70d2545d43b72c870bb94042191c3e95.png

6c6d830995370ff8e9a1339220520825.png

       Reuse the formula flow = capacity - residual

3173bee7d322b261b0f350f6447afe91.png-a99323567fcc0412694068209f5c829c.png=6619f5351369c028a6699497d4a40514.png

       The maximum flow is 19.

       Code

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, flow


    def other_node(self, p):
        ''' 返回边中与p相对的另一顶点 '''
        return self.v if p == self.w else self.w


    def 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.flow


    def moddify_flow(self, p, x):
        ''' 将边的流量调整x '''
        if p == self.w:  # 如果 p=w,将v→w的流量增加x
            self.flow += x
        else:  # 否则将v→w的流量减少x
            self.flow -= x


    def __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, t


    def 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('该网络不符合守恒定律')
            return
        print('%-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 = G
        self.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, parent


    def 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 = 0
        while len(q)>1:
            node_v = q.pop(0)
            if node_v=="end":
                q.append("end")
                visited = tempvisited.copy()
                continue
            v = node_v.w
            for e in self.G.edges(v):  # 遍历连接v的所有边
                w = e.other_node(v)  # 边的另一顶点,e的指向是v→w
                if 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 = 1


        return Network(NewE,self.G.s,self.G.t),label


    def dfs(self,NewNetwork):
        ''' 获取网络中的一条增广路径 '''
        path = None
        path2 = None
        visited = 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 = 1e10
        while len(q):
            node_v = q.pop(0)
            node_v2 = q2.pop(0)
            v = node_v.w
            label = 0
            for e in NewNetwork.edges(v):  # 遍历连接v的所有边
                w = e.other_node(v)  # 边的另一顶点,e的指向是v→w
                e2 = [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_w
                        path2 = node_w2
                        label = 1
                        break
            if label == 1:  # 到达了汇点
                break
        if path is None:
            tempmaxflow = 0
            return tempmaxflow
        node = path
        while node.parent != self.G.t:  # 计算增广路径上的最小剩余量
            w, e = node.w, node.e
            tempmaxflow = min(tempmaxflow, e.residual_cap_to(w))
            node = node.parent
        node = path2
        while node.parent != self.G.t:  # 修改残存网
            w, e = node.w, node.e
            e.moddify_flow(w, tempmaxflow)
            node = node.parent
        return tempmaxflow


    def start(self):
        while True:
            newnet,label = self.bfs()
            if label==0:
                break
            while True:
                tempmaxflow = self.dfs(newnet)  # 找到一条增广路径
                if tempmaxflow == 0:
                    break
                self.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()

Min-cut Algorithm

       The min-cut is actually the computation of the max-flow used.

       Conversion between the maximum flow and the minimum cut: draw the remaining capacity of the maximum flow, then start from the starting point s, set the one that can be connected together as s, and the other side is t

290c532e4853ea674215290b3d2ec0e2.png

db18b9bd1c670f2c228141c5e858eea2.png

Summarize:

       Maximum flow and minimum cut are the basis of graph theory segmentation. Next, we will continue to explore how the maximum flow and minimum cut can achieve segmentation on the image. For example graph cut etc.

cf5fec55d6e172b49e79c2fd6ed7724c.png

references:

https://www.its203.com/article/qq_35885429/107226535

https://github.com/wangshusen/AdvancedAlgorithms

Guess you like

Origin blog.csdn.net/weixin_41202834/article/details/123606605