OpenCV uses GrabCut algorithm for interactive foreground extraction

OpenCV uses GrabCut algorithm for interactive foreground extraction

This blog will show how to use Python, GrabCut algorithm in OpenCV to extract foreground in an image and create an interactive application for this.

1. Rendering

The official example - lena original image VS grabcut foreground setting VS foreground matting effect is as follows:

Use the right mouse button to draw a rectangle, press the '0' left button to click the marker to determine the background, press the '1' left button to determine the foreground, press the 'r' key to reset the rectangle, etc., press the 's' key to save the resulting graph, and then get the comparison Clean and friendly renderings;
insert image description here
insert image description here
my favorite Yingbao, the original image VS GrabCut image VS cutout effect is as follows:
insert image description here

2. Source code

# 使用GrabCut进行交互式的前景提取(Interactive Image Segmentation using GrabCut algorithm)
# USAGE:
# python grabcut.py ml2.jpg

# 运行会呈现俩个窗口,输入图像及输出图像窗口
# 首先用鼠标右键绘制一个前景的矩形框,按下 'n' 键进行前景提取
# 然后按下鼠标左键绘制任何区域,点、线等不规则区域,按下 'n' 键进行前景提取

# 按下键 '0' - 选择确定背景
# 按下键 '1' - 选择确定前景
# 按下键 '2' - 选择可能背景
# 按下键 '3' - 选择可能前景
# 按下键 'n' - 更新分割结果
# 按下键 'r' - 重置分割
# 按下键 's' - 保存分割的结果
# 按下键 'ESC' - 退出分割

from __future__ import print_function

import sys

import cv2 as cv
import numpy as np


class App():
    BLUE = [255, 0, 0]  # 矩形颜色
    RED = [0, 0, 255]  # 可能背景
    GREEN = [0, 255, 0]  # 可能前景
    BLACK = [0, 0, 0]  # 确定背景
    WHITE = [255, 255, 255]  # 确定前景

    DRAW_BG = {
    
    'color': BLACK, 'val': 0}
    DRAW_FG = {
    
    'color': WHITE, 'val': 1}
    DRAW_PR_BG = {
    
    'color': RED, 'val': 2}
    DRAW_PR_FG = {
    
    'color': GREEN, 'val': 3}

    # 设置flags
    rect = (0, 0, 1, 1)
    drawing = False  # 是否绘制曲线
    rectangle = False  # 是否绘制矩形
    rect_over = False  # 是否矩形绘制完毕
    rect_or_mask = 100  # 选择矩形或者蒙版
    value = DRAW_FG  # 绘制初始化的前景
    thickness = 3  # 笔刷粗细

    def onmouse(self, event, x, y, flags, param):
        # 绘制矩形(按下鼠标右键)
        if event == cv.EVENT_RBUTTONDOWN:
            self.rectangle = True
            self.ix, self.iy = x, y

        elif event == cv.EVENT_MOUSEMOVE:
            if self.rectangle == True:
                self.img = self.img2.copy()
                cv.rectangle(self.img, (self.ix, self.iy), (x, y), self.BLUE, 2)
                self.rect = (min(self.ix, x), min(self.iy, y), abs(self.ix - x), abs(self.iy - y))
                self.rect_or_mask = 0

        elif event == cv.EVENT_RBUTTONUP:
            self.rectangle = False
            self.rect_over = True
            cv.rectangle(self.img, (self.ix, self.iy), (x, y), self.BLUE, 2)
            self.rect = (min(self.ix, x), min(self.iy, y), abs(self.ix - x), abs(self.iy - y))
            self.rect_or_mask = 0
            print(" Now press the key 'n' a few times until no further change \n")

        # 绘制接触曲线
        if event == cv.EVENT_LBUTTONDOWN:
            if self.rect_over == False:
                print("first draw rectangle \n")
            else:
                self.drawing = True
                cv.circle(self.img, (x, y), self.thickness, self.value['color'], -1)
                cv.circle(self.mask, (x, y), self.thickness, self.value['val'], -1)

        elif event == cv.EVENT_MOUSEMOVE:
            if self.drawing == True:
                cv.circle(self.img, (x, y), self.thickness, self.value['color'], -1)
                cv.circle(self.mask, (x, y), self.thickness, self.value['val'], -1)

        elif event == cv.EVENT_LBUTTONUP:
            if self.drawing == True:
                self.drawing = False
                cv.circle(self.img, (x, y), self.thickness, self.value['color'], -1)
                cv.circle(self.mask, (x, y), self.thickness, self.value['val'], -1)

    def run(self):
        # Loading images
        if len(sys.argv) == 2:
            filename = sys.argv[1]  # for drawing purposes
        else:
            print("No input image given, so loading default image, ml.jpg \n")
            print("Correct Usage: python grabcut.py <filename> \n")
            filename = 'ml.jpg'

        print(filename)
        self.img = cv.imread(filename)
        self.img2 = self.img.copy()  # 复制原始图像
        self.mask = np.zeros(self.img.shape[:2], dtype=np.uint8)  # mask初始化为PR_BG可能背景
        self.output = np.zeros(self.img.shape, np.uint8)  # 输出图像

        # 输入输出框
        cv.namedWindow('output')
        cv.namedWindow('input')
        cv.setMouseCallback('input', self.onmouse)
        cv.moveWindow('input', self.img.shape[1] + 10, 90)

        print(" Instructions: \n")
        print(" Draw a rectangle around the object using right mouse button \n")

        while (1):

            cv.imshow('output', self.output)
            cv.imshow('input', self.img)
            k = cv.waitKey(1)

            # 按键事件绑定
            if k == 27:  # 按ESC退出
                break
            elif k == ord('0'):  # 绘制背景
                print(" mark background regions with left mouse button \n")
                self.value = self.DRAW_BG
            elif k == ord('1'):  # 绘制前景
                print(" mark foreground regions with left mouse button \n")
                self.value = self.DRAW_FG
            elif k == ord('2'):  # 绘制可能背景
                self.value = self.DRAW_PR_BG
            elif k == ord('3'):  # 绘制可能前景
                self.value = self.DRAW_PR_FG
            elif k == ord('s'):  # 保存结果
                bar = np.zeros((self.img.shape[0], 10, 3), np.uint8)
                res = np.hstack((self.img2, bar, self.img, bar, self.output))
                cv.imwrite('ml_grabcut_output.jpg', res)
                print(" Result saved as image \n")
            elif k == ord('r'):  # 重置,重新进行选中前景背景
                print("resetting \n")
                self.rect = (0, 0, 1, 1)
                self.drawing = False
                self.rectangle = False
                self.rect_or_mask = 100
                self.rect_over = False
                self.value = self.DRAW_FG
                self.img = self.img2.copy()
                self.mask = np.zeros(self.img.shape[:2], dtype=np.uint8)  # mask initialized to PR_BG
                self.output = np.zeros(self.img.shape, np.uint8)  # output image to be shown
            elif k == ord('n'):  # 分离图片
                print(""" For finer touchups, mark foreground and background after pressing keys 0-3
                and again press 'n' \n""")
                try:
                    bgdmodel = np.zeros((1, 65), np.float64)
                    fgdmodel = np.zeros((1, 65), np.float64)
                    if (self.rect_or_mask == 0):  # 使用矩形进行grabcut
                        cv.grabCut(self.img2, self.mask, self.rect, bgdmodel, fgdmodel, 1, cv.GC_INIT_WITH_RECT)
                        self.rect_or_mask = 1
                    elif (self.rect_or_mask == 1):  # 使用mask进行grabcut
                        cv.grabCut(self.img2, self.mask, self.rect, bgdmodel, fgdmodel, 1, cv.GC_INIT_WITH_MASK)
                except:
                    import traceback
                    traceback.print_exc()

            mask2 = np.where((self.mask == 1) + (self.mask == 3), 255, 0).astype('uint8')
            self.output = cv.bitwise_and(self.img2, self.img2, mask=mask2)

        print('Done')


if __name__ == '__main__':
    print(__doc__)
    App().run()
    cv.destroyAllWindows()

refer to

Guess you like

Origin blog.csdn.net/qq_40985985/article/details/120920337