【youcans 的 OpenCV 例程200篇】177.图像分割之图割法 GraphCuts

OpenCV 例程200篇 总目录-202205更新


【youcans 的 OpenCV 例程200篇】177.图像分割之图割法 GraphCuts


6. 图像分割之图割法

基于图论的图像分割技术的基本思想是:将图像映射为带权的无向图,把像素视为节点,两个节点之间的边的权重对应于两个像素之间相似性的度量,割的容量就对应于能量函数;使用最大流最小割算法对图进行切割,得到的最小割就对应于最优图像分割。

6.1 图割分割算法 Graph cuts

将图像表示为无向图后,基于网络流优化技术求解最小图割,以获得图像分割结果。

一个图或网络的割表示一个切面或切线,将图或网络分为分别包含源点和汇点的两个子集,该切线或切面与网络相交的楞或边的集合,称为图像的割。最小割(Minimum cut),是指边的容量(权重)之和最小的割。

图论中的最大流最小割定理指出,从源点到汇点的最大流等于最小割,因此最小割问题等价于最大流问题。求解最大流问题有很多高效的多项式时间算法, 可以应用于图像分割。

包含源点和汇点的两个子集,对应于图像中的两个分区; 边的权重,对应于图像中两个像素之间的相似性。

对于给定的全无向图 G = ( V , E ) G=(V,E) G=(V,E),图的切割将节点 V 分为两个子集 A、B: A ∪ B = V A \cup B= V AB=V A ∩ B = ϕ A \cap B= \phi AB=ϕ

连接边的权重 w ( i , j ) w(i,j) w(i,j) 是节点 i , j i, j i,j 之间相似性的函数:
w ( i , j ) = 1 / ( ∣ I ( n i ) − I ( n j ) ∣ + η ) w(i,j)=1 / \big( |I(n_i) - I(n_j)| +\eta \big) w(i,j)=1/(I(ni)I(nj)+η)

最小割定义为割集的边的最小总权重:

c u t ( A , B ) = ∑ u ∈ A , v ∈ B w ( u , v ) cut(A,B) = \sum_{u \in A, v \in B} w(u,v) cut(A,B)=uA,vBw(u,v)

一种改进方法,使用归一化切割(Ncut)的测度定义:

N c u t ( A , B ) = c u t ( A , B ) a s s o c ( A , V ) + c u t ( A , B ) a s s o c ( B , V ) N a s s o c ( A , B ) = a s s o c ( A , A ) a s s o c ( A , V ) + a s s o c ( B , B ) a s s o c ( B , V ) a s s o c ( A , V ) = ∑ u ∈ A , z ∈ V w ( u , z ) a s s o c ( B , V ) = ∑ v ∈ B , z ∈ V w ( v , z ) \begin{aligned} Ncut(A,B) &= \frac{cut(A,B)}{assoc(A,V)} + \frac{cut(A,B)}{assoc(B,V)} \\ Nassoc(A,B) &= \frac{assoc(A,A)}{assoc(A,V)} + \frac{assoc(B,B)}{assoc(B,V)} \\ assoc(A,V) &= \sum_{u \in A, z \in V} w(u,z) \\ assoc(B,V) &= \sum_{v \in B, z \in V} w(v,z) \end{aligned} Ncut(A,B)Nassoc(A,B)assoc(A,V)assoc(B,V)=assoc(A,V)cut(A,B)+assoc(B,V)cut(A,B)=assoc(A,V)assoc(A,A)+assoc(B,V)assoc(B,B)=uA,zVw(u,z)=vB,zVw(v,z)

另一种常用的方法,使用基于区域分布直方图和边界形状的能量函数 E(L):

E ( L ) = α R ( L ) + B ( L ) E(L) = \alpha R(L) + B(L) E(L)=αR(L)+B(L)

其中,R(L) 是区域分布项(regional term),B(L) 是边界项(boundary term),E(L) 是能量函数。图割的目标是最小化能量函数。

图割分割算法(Graph Cut)运用最小割最大流算法进行图像的分割,将图像分割为前景和背景。

GraphCut 算法需要用户在前景和背景处各画几笔作为输入,由此建立各个像素点与前景背景相似度的赋权图,并通过求解最小割进行图像的前景和背景分割。


例程 11.34: 图割分割算法 Graph Cuts

# 11.34 GraphCuts 交互式图割分割算法 
'''
说明:
  (1) 用鼠标左键标记前景,鼠标右键标记背景;
  (2) 可以重复标记,不断优化;
  (3) 按 Esc 键退出,完成分割。
'''

# Copyright 2022 Youcans, XUPT
# Crated:2021-12-27

import cv2
import numpy as np
from matplotlib import pyplot as plt

drawing = False
mode = False

class GraphCutXupt:
    def __init__(self, t_img):
        self.img = t_img
        self.img_raw = img.copy()
        self.img_width = img.shape[0]
        self.img_height = img.shape[1]
        self.scale_size = 640 * self.img_width // self.img_height
        if self.img_width > 640:
            self.img = cv2.resize(self.img, (640, self.scale_size), interpolation=cv2.INTER_AREA)
        self.img_show = self.img.copy()
        self.img_gc = self.img.copy()
        self.img_gc = cv2.GaussianBlur(self.img_gc, (3, 3), 0)
        self.lb_up = False
        self.rb_up = False
        self.lb_down = False
        self.rb_down = False
        self.mask = np.full(self.img.shape[:2], 2, dtype=np.uint8)
        self.firt_choose = True

# 鼠标的回调函数
def mouse_event2(event, x, y, flags, param):
    global drawing, last_point, start_point
    # 左键按下:开始画图
    if event == cv2.EVENT_LBUTTONDOWN:
        drawing = True
        last_point = (x, y)
        start_point = last_point
        param.lb_down = True
        print('mouse lb down')
    elif event == cv2.EVENT_RBUTTONDOWN:
        drawing = True
        last_point = (x, y)
        start_point = last_point
        param.rb_down = True
        print('mouse rb down')
    # 鼠标移动,画图
    elif event == cv2.EVENT_MOUSEMOVE:
        if drawing:
            if param.lb_down:
                cv2.line(param.img_show, last_point, (x, y), (0, 0, 255), 2, -1)
                cv2.rectangle(param.mask, last_point, (x, y), 1, -1, 4)
            else:
                cv2.line(param.img_show, last_point, (x, y), (255, 0, 0), 2, -1)
                cv2.rectangle(param.mask, last_point, (x, y), 0, -1, 4)
            last_point = (x, y)
    # 左键释放:结束画图
    elif event == cv2.EVENT_LBUTTONUP:
        drawing = False
        param.lb_up = True
        param.lb_down = False
        cv2.line(param.img_show, last_point, (x, y), (0, 0, 255), 2, -1)
        if param.firt_choose:
            param.firt_choose = False
        cv2.rectangle(param.mask, last_point, (x, y), 1, -1, 4)
        print('mouse lb up')
    elif event == cv2.EVENT_RBUTTONUP:
        drawing = False
        param.rb_up = True
        param.rb_down = False
        cv2.line(param.img_show, last_point, (x, y), (255, 0, 0), 2, -1)
        if param.firt_choose:
            param.firt_choose = False
            param.mask = np.full(param.img.shape[:2], 3, dtype=np.uint8)
        cv2.rectangle(param.mask, last_point, (x, y), 0, -1, 4)
        print('mouse rb up')

if __name__ == '__main__':
    img = cv2.imread("../images/imgDeer.png", flags=1)  # 读取彩色图像(Youcans)
    g_img = GraphCutXupt(img)

    cv2.namedWindow('image')
    # 定义鼠标的回调函数
    cv2.setMouseCallback('image', mouse_event2, g_img)
    while (True):
        cv2.imshow('image', g_img.img_show)
        if g_img.lb_up or g_img.rb_up:
            g_img.lb_up = False
            g_img.rb_up = False
            bgdModel = np.zeros((1, 65), np.float64)
            fgdModel = np.zeros((1, 65), np.float64)
            rect = (1, 1, g_img.img.shape[1], g_img.img.shape[0])
            print(g_img.mask)
            mask = g_img.mask
            g_img.img_gc = g_img.img.copy()
            cv2.grabCut(g_img.img_gc, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_MASK)
            mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')  # 0和2做背景
            g_img.img_gc = g_img.img_gc * mask2[:, :, np.newaxis]  # 使用蒙板来获取前景区域
            cv2.imshow('youcans', g_img.img_gc)
        # 按下ESC键退出
        if cv2.waitKey(20) == 27:
            break

    plt.figure(figsize=(10, 7))
    plt.subplot(221), plt.axis('off'), plt.title("xupt")
    plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))  # 显示 img(RGB)
    plt.subplot(222), plt.axis('off'), plt.title("mask")
    plt.imshow(mask, 'gray')
    plt.subplot(223), plt.axis('off'), plt.title("mask2")
    plt.imshow(mask2, 'gray')
    plt.subplot(224), plt.axis('off'), plt.title("Grab Cut")
    plt.imshow(cv2.cvtColor(g_img.img_gc, cv2.COLOR_BGR2RGB))
    plt.tight_layout()
    plt.show()

在这里插入图片描述


(本节完)


版权声明:

OpenCV 例程200篇 总目录-202205更新
youcans@xupt 原创作品,转载必须标注原文链接:(https://blog.csdn.net/youcans/article/details/124723416)

Copyright 2022 youcans, XUPT
Crated:2022-5-10


欢迎关注 『youcans 的 OpenCV 例程 200 篇』 系列,持续更新中
欢迎关注 『youcans 的 OpenCV学习课』 系列,持续更新中

【youcans 的 OpenCV 例程200篇】147. 图像分割之孤立点检测
【youcans 的 OpenCV 例程200篇】148. 图像分割之线检测
【youcans 的 OpenCV 例程200篇】149. 图像分割之边缘模型
【youcans 的 OpenCV 例程200篇】150. 边缘检测梯度算子
【youcans 的 OpenCV 例程200篇】151. 边缘检测中的平滑处理
【youcans 的 OpenCV 例程200篇】152. 边缘检测之 LoG 算子
【youcans 的 OpenCV 例程200篇】153. 边缘检测之 DoG 算子
【youcans 的 OpenCV 例程200篇】154. 边缘检测之 Canny 算子
【youcans 的 OpenCV 例程200篇】155. 边缘连接的局部处理方法
【youcans 的 OpenCV 例程200篇】156. 边缘连接局部处理的简化算法
【youcans 的 OpenCV 例程200篇】157. 霍夫变换直线检测
【youcans 的 OpenCV 例程200篇】158. 阈值处理之固定阈值法
【youcans 的 OpenCV 例程200篇】159. 图像分割之全局阈值处理
【youcans 的 OpenCV 例程200篇】160. 图像处理之OTSU 方法
【youcans 的 OpenCV 例程200篇】161. OTSU 阈值处理算法的实现
【youcans 的 OpenCV 例程200篇】162. 全局阈值处理改进方法
【youcans 的 OpenCV 例程200篇】163. 基于边缘信息改进全局阈值处理
【youcans 的 OpenCV 例程200篇】164.使用 Laplace 边缘信息改进全局阈值处理
【youcans 的 OpenCV 例程200篇】165.多阈值 OTSU 处理方法
【youcans 的 OpenCV 例程200篇】166.自适应阈值处理
【youcans 的 OpenCV 例程200篇】167.基于移动平均的可变阈值处理
【youcans 的 OpenCV 例程200篇】168.图像分割之区域生长
【youcans 的 OpenCV 例程200篇】169.图像分割之区域分离
【youcans 的 OpenCV 例程200篇】170.图像分割之K均值聚类
【youcans 的 OpenCV 例程200篇】171.SLIC 超像素区域分割
【youcans 的 OpenCV 例程200篇】172.SLIC 超像素区域分割算法比较
【youcans 的 OpenCV 例程200篇】173.SEEDS 超像素区域分割
【youcans 的 OpenCV 例程200篇】174.LSC 超像素区域分割
【youcans 的 OpenCV 例程200篇】175.超像素区域分割方法比较
【youcans 的 OpenCV 例程200篇】176.图像分割之均值漂移算法
【youcans 的 OpenCV 例程200篇】177.图像分割之图割法 GraphCuts

猜你喜欢

转载自blog.csdn.net/youcans/article/details/124723416
今日推荐