OpenCV Python Grabcut分割

【目标】

  • Grabcut 算法
  • 创建一个交互程序

【理论】

从用户角度是如何工作的呢?用户在需要的目标上初始绘制一个矩形,前景目标必须完全在矩形内部,算法迭代的去分割然后得到更好的效果,但是有些情况下,分割效果不是很好,例如:会将部分前景标记为背景,反之亦然。这个时候,就需要用户做一些交互,告诉分割结果,哪些是前景哪些是背景,这样下次迭代就会得到更好的效果。

背景发生了哪些动作?

  • 所有在矩形外的部分被标记为背景,在矩形框里面的都是未知。相似的所有用户给定的前景和背景都是 硬标签
  • 计算机不会初始化标签,主要取决于用户给定的数据,标记前景和背景像素。
  • GMM(混合高斯模型)被用户前景和背景
  • 根据给定的数据,GMM学习和创造一些新的像素分布,位置像素会被标记为可能前景或背景,取决于其他硬标签像素(有点类似聚类)
  • 图通过像素分布创建,图中的节点是像素,增加两个节点,一个是源节点,一个是汇节点,每个前景像素与源节点连接,每个背景像素与汇节点连接。
  • 连接像素直接边的权重主要根据像素属于前景或背景的概率而定,像素之间的权重根据边缘信息和像素相似度而定。如果像素颜色有很大的不同,那么他们之间的边缘权重会小一些。
  • mincut算法用于分割图,它以最小代价函数将图分成两个分离的源节点和汇节点。代价函数是所有边权重之和。cut之后,所有连接到源节点的像素变成了前景,连接到汇节点的像素变成了背景。
  • 这个过程持续到分类收敛。

【代码】

import numpy as np
import cv2 import sys COLOR_BLUE = [255, 0, 0]      # 矩形框颜色
COLOR_RED  = [0, 0, 255]      # 可能背景绘制颜色
COLOR_GREEN = [0, 255, 0]     # 可能前景绘制颜色
COLOR_BLACK = [0, 0, 0]       # 背景绘制颜色
COLOR_WHITE = [255, 255, 255] # 前景绘制颜色DRAW_BG = {'color': COLOR_BLACK, 'val': 0}    # 背景,标记为0
DRAW_FG = {'color': COLOR_WHITE, 'val': 1}    # 前景,标记为1
DRAW_PR_BG = {'color': COLOR_RED, 'val': 2}    # 可能背景,标记为2
DRAW_PR_FG = {'color': COLOR_GREEN, 'val': 3}  # 可能前景,标记为3class GrabCutApp():"""GrabCutApp 利用grabcut对图像进行前景提取USAGE:python grabcut.py <image_name>README FIRST:two windows will show up, one for input and one for output at first, in input window, right mouse buttom draw a rectangle around the object with blue. then, press 'n' to segment the object (once of a few times). if the effect is not so good, please prees key below what you want, the key concept is below:key '0' - select area of sure backgroundkey '1' - select area of sure foregroundkey '2' - select area of probable backgroundkey '3' - select area of probable foregroundkey 'r' - reset the setupkey 'n' - segment the objectkey 's' - save the segmented image to "image_name_grabcut.png"key 'q' - quitkey esc - quit"""# 初始化def __init__(self, imagename: str) -> None:self.img = cv2.imread(imagename)if self.img is None:print('图像读取失败')sys.exit(0)self.rect = (0, 0, 1, 1) # 矩形框初始化self.drawing = Falseself.rectangle = False # 是否开始绘制矩形框self.rect_over = False # 判断矩形是否结束self.rect_or_mask = 100 # 矩形框或者mask的种类self.value = DRAW_FGself.thickness = 3self.radius = 5# 鼠标回调def onmouse(self, event, x, y, flags, param) -> None:# 自定义鼠标回调函数if event == cv2.EVENT_RBUTTONDOWN:self.rectangle = Trueself.ix, self.iy = x, yelif event == cv2.EVENT_MOUSEMOVE:if self.rectangle == True:self.img = self.img2.copy()cv2.rectangle(self.img, (self.ix, self.iy), (x, y), COLOR_BLUE, self.thickness)self.rect = (min(self.ix, x), min(self.iy, y), abs(self.ix - x), abs(self.iy - y))self.rect_or_mask = 0elif event == cv2.EVENT_RBUTTONUP:self.rectangle = Falseself.rect_over = Truecv.rectangle(self.img, (self.ix, self.iy), (x, y), COLOR_BLUE, self.thickness)self.rect = (min(self.ix, x), min(self.iy, y), abs(self.ix - x), abs(self.iy - y))self.rect_or_mask = 0print(" Now press the key 'n' a few times until no further change \n")# 交互操作if event == cv2.EVENT_LBUTTONDOWN:if self.rect_over == False:print("draw object first \n")else:self.drawing = Truecv2.circle(self.img, (x, y), self.radius, self.value['color'], -1)cv2.circle(self.mask, (x, y), self.radius, self.value['val'], -1)elif event == cv2.EVENT_MOUSEMOVE:if self.drawing == True:cv2.circle(self.img, (x, y), self.radius, self.value['color'], -1)cv2.circle(self.mask, (x, y), self.radius, self.value['val'], -1)elif event == cv2.EVENT_LBUTTONUP:if self.drawing == True:self.drawing = Falsecv2.circle(self.img, (x, y), self.radius, self.value['color'], -1)cv2.circle(self.mask, (x, y), self.radius, self.value['val'], -1)def run(self):# 拷贝self.img2 = self.img.copy()# 初始化一个mask图像self.mask = np.zeros(self.img.shape[:2], dtype=np.uint8)self.output = np.zeros(self.img.shape, np.uint8)cv2.namedWindow('output')cv2.namedWindow('input')cv2.setMouseCallback('input', self.onmouse)cv2.moveWindow('input', self.img.shape[1] + 10, 0)print('draw a rectangle around the object use right mouse button to draw')while(1):cv2.imshow('output', self.output)cv2.imshow('input', self.img)k = cv2.waitKey(1)# if k == 27 or k == ord('q'): # esc or 'q' to quit breakelif k == ord('0'): # BG drawingprint("mark background regions with left mouse buttom \n")self.value = DRAW_BGelif k == ord('1'): # FG drawingprint("mark foreground regions with left mouse buttom \n")self.value = DRAW_FGelif k == ord('2'): # PR_BG drawingself.value = DRAW_PR_BGelif k == ord('3'): # PR_FG drawingself.value = DRAW_PR_FGelif k == ord('s'): # save imagebar = np.zeros((self.img.shape[0], 5, 3), np.uint8)res = np.hstack((self.img2, bar, self.img, bar, self.output))cv2.imwrite('grabcut_output_result.png', res)print('result saved as grabcut_output_result.png\n')elif k == ord('r'): # restore to original statusprint('reset all settings ...\n')self.rect = (0, 0, 1, 1)self.drawing = Falseself.rectangle = False self.rect_or_mask = 100self.rect_over = Falseself.value = DRAW_FGself.img = self.img2.copy()self.mask = np.zeros(self.img.shape[:2], dtype=np.uint8)self.output = np.zeros(self.img.shape, np.uint8)elif k == ord('n'):print("for finer touchups, mark foreground and background after pressing keys 0-3")try:bgdmodel = np.zeros((1, 65), np.float64)fgdmodel = np.zeros((1, 65), np.float64)if (self.rect_or_mask == 0): # original rectanglecv2.grabCut(self.img2, self.mask, self.rect, bgdmodel, fgdmodel, 1, cv2.GC_INIT_WITH_RECT)self.rect_or_mask = 1elif (self.rect_or_mask == 1): # grabcut after rectanglecv2.grabCut(self.img2, self.mask, self.rect, bgdmodel, fgdmodel, 5, cv2.GC_INIT_WITH_MASK)except:import tracebacktraceback.print_exc()mask2 = np.where((self.mask==1)+ (self.mask==3), 255, 0).astype('uint8')cv2.imshow('mask2', mask2)self.output = cv2.bitwise_and(self.img2, self.img2, mask=mask2)cv2.destroyAllWindows()if __name__ == '__main__':app = GrabCutApp('assets/messi5.jpg')print(app.__doc__)app.run()

【接口】

cv.grabCut(  img, mask, rect, bgdModel, fgdModel, iterCount[, mode]  ) -> mask, bgdModel, fgdModel

执行 grabcut 算法

  • img: 输入8位3通道图像
  • mask: 输入输出的8位单通道图像,用矩形初始化
  • rect: ROI矩形,在矩形外面的部分被认为是背景,只有当 mode = GC_INIT_WITH_RECT 时才有效
    bgdModel: 存储背景模型的参数,处理同一个图像时,不要修改该模型
    fgdModel: 存储前景模型的参数,处理同一个图像时,不要修改该模型
  • iterCount: 迭代次数
  • mode: 不同的模式 GrabCutModes
  • GrabCutModes

【参考】

  1. OpenCV 官方文档
  2. "GrabCut": interactive foreground extraction using iterated graph cuts
  3. GrabCut image segmentation algorithm.

【OpenCV-Python】教程:3-16 利用Grabcut交互式前景提取相关推荐

  1. OpenCV使用 GrabCut 算法进行交互式前景提取

    OpenCV使用 GrabCut 算法进行交互式前景提取 1. 效果图 2. 源码 参考 这篇博客将介绍如何使用Python,OpenCV中的GrabCut 算法来提取图像中的前景,并为此创建一个交互 ...

  2. OpenCV Python教程(2、图像元素的访问、通道分离与合并)

    OpenCV Python教程之图像元素的访问.通道分离与合并 转载请详细注明原作者及出处,谢谢! 访问像素 像素的访问和访问numpy中ndarray的方法完全一样,灰度图为: [python] v ...

  3. opencv python教程-OpenCV4 Python 最新中文版官方教程来了(附下载)

    教程简介 OpenCV 是计算机视觉中经典的专用库,然而其中文版官方教程久久不来.近日,一款最新 OpenCV4.1 版本的完整中文版官方教程出炉,读者朋友可以更好的学习了解 OpenCV 相关细节. ...

  4. OpenCV Python教程(3)(4)(5): 直方图的计算与显示 形态学处理 初级滤波内

    OpenCV Python教程(3.直方图的计算与显示) 本篇文章介绍如何用OpenCV Python来计算直方图,并简略介绍用NumPy和Matplotlib计算和绘制直方图 直方图的背景知识.用途 ...

  5. OpenCV中的图像处理 —— 霍夫线 / 圈变换 + 图像分割(分水岭算法) + 交互式前景提取(GrabCut算法)

    OpenCV中的图像处理 -- 霍夫线 / 圈变换 + 图像分割(分水岭算法) + 交互式前景提取(GrabCut算法)

  6. OpenCV系列之交互式前景提取使用GrabCut算法 | 三十五

    目标 在本章中, 我们将看到GrabCut算法来提取图像中的前景 我们将为此创建一个交互式应用程序. 理论 GrabCut算法由英国微软研究院的Carsten Rother,Vladimir Kolm ...

  7. 图像分割与提取:交互式前景提取(附OpenCV代码实现)

    一.简介 经典的前景提取技术主要使用纹理(颜色)信息,如魔术棒工具,或根据边缘(对比度)信息,如智能剪刀等完成.2004 年,微软研究院(剑桥)的 Rother 等人在论文 GrabCut: Inte ...

  8. opencv29:使用Grabcut算法的交互式前景提取

    目标 在这一章当中 看到 GrabCut算法来提取图像中的前景 为此创建一个交互式应用程序 理论 GrabCut 算法由英国剑桥微软研究院的 Carsten Rother.Vladimir Kolmo ...

  9. Python OpenCV 交互式前景提取 自动抠图

    这是需要抠图的原图像,文件名为 "messi5.jpg" 使用矩形方式处理: # -*- coding: utf-8 -*-import numpy as np import cv ...

最新文章

  1. SpringBoot+MDC实现全链路调用日志跟踪,这才叫优雅!
  2. android listview分页显示,Android应用中使用ListView来分页显示刷新的内容
  3. ServiceMix中文教程
  4. Ubuntu添加开机自动启动程序的方法 / 修改 Ubuntu 启动顺序
  5. Redis-HyperLogLog
  6. 服务器配置织梦系统,DedeCMS织梦系统设置说明:核心设置
  7. 为ESXi 4.x / 5.x / 6.x / 7.x创建持久暂存位置(1033696)
  8. 674. Longest Continuous Increasing Subsequence最长连续递增子数组
  9. linux内核内存分配(三、虚拟内存管理)
  10. 高内聚低耦合_拉锥耦合器最强粘接方案
  11. vb.net 读写cookie
  12. linux usb有线网卡驱动_Linux系统下安装USB无线网卡驱动方法
  13. 硕思闪客精灵怎么导出flash(gif)动画,flash游戏源文件疑难问题解答(注册码)
  14. 虚拟仿真实验室管理系统
  15. 我的c盘为多余的java.exejavaw.exe_java.exe,javac.exe,javaw.exe 是什么进程?
  16. 设计模式——工厂方法模式
  17. uniapp的分享到朋友圈和朋友(APP)
  18. OSPF多区域与配置
  19. python怎样按某一列值拆分Excel表格
  20. 面试成功一个公司,微信上HR和我谈好了薪资和入职日期。却不发offer。这种企业值得去吗?

热门文章

  1. 2022 VeLO: Training Versatile Learned Optimizers by Scaling Up
  2. Thymeleaf 变量输出与字符串操作
  3. 把object强制转换成int
  4. 邦纳传感器M18TUP8Q
  5. Python就业行情和前景分析之一
  6. Linux Ubuntu系统设置成中文语言
  7. 微信小程序滴滴服务器报错,如何使用mpx框架(滴滴)给微信小程序分包
  8. 【STM32H7】第2章 ThreadX FileX文件系统介绍
  9. vue中组件之间调用方法——子组件调用父组件的方法 父组件调用子组件的方法
  10. 【OAI】【debug】分立机器基站接入核心网并调试可用解决之路