OpenCV-Python 交互式前景提取使用GrabCut算法 | 三十五
目标
在本章中,
- 我们将看到GrabCut算法来提取图像中的前景
- 我们将为此创建一个交互式应用程序。
理论
GrabCut算法由英国微软研究院的Carsten Rother,Vladimir Kolmogorov和Andrew Blake设计。在他们的论文“GrabCut”中:使用迭代图割的交互式前景提取。需要用最少的用户交互进行前景提取的算法,结果是GrabCut。
从用户角度来看,它是如何工作的?最初,用户在前景区域周围绘制一个矩形(前景区域应完全位于矩形内部)。然后,算法会对其进行迭代分割,以获得最佳结果。做完了但在某些情况下,分割可能不会很好,例如,可能已将某些前景区域标记为背景,反之亦然。在这种情况下,需要用户进行精修。只需在图像错误分割区域上画些笔画。笔画基本上说 “嘿,该区域应该是前景,你将其标记为背景,在下一次迭代中对其进行校正”或与背景相反。然后在下一次迭代中,你将获得更好的结果。
参见下图。第一名球员和橄榄球被封闭在一个蓝色矩形中。然后用白色笔划(表示前景)和黑色笔划(表示背景)进行最后的修饰。而且我们得到了不错的结果。

那么背景发生了什么呢?
- 用户输入矩形。此矩形外部的所有内容都将作为背景(这是在矩形应包含所有对象之前提到的原因)。矩形内的所有内容都是未知的。同样,任何指定前景和背景的用户输入都被视为硬标签,这意味着它们在此过程中不会更改。
- 计算机根据我们提供的数据进行初始标记。它标记前景和背景像素(或对其进行硬标记),现在使用高斯混合模型(GMM)对前景和背景进行建模。
- 根据我们提供的数据,GMM可以学习并创建新的像素分布。也就是说,未知像素根据颜色统计上与其他硬标记像素的关系而被标记为可能的前景或可能的背景(就像聚类一样)。
- 根据此像素分布构建图形。图中的节点为像素。添加了另外两个节点,即“源”节点和“接收器”节点。每个前景像素都连接到源节点,每个背景像素都连接到接收器节点。
- 通过像素是前景/背景的概率来定义将像素连接到源节点/末端节点的边缘的权重。像素之间的权重由边缘信息或像素相似度定义。如果像素颜色差异很大,则它们之间的边缘将变低。
- 然后使用mincut算法对图进行分割。它将图切成具有最小成本函数的两个分离的源节点和宿节点。成本函数是被切割边缘的所有权重的总和。剪切后,连接到“源”节点的所有像素都变为前景,而连接到“接收器”节点的像素都变为背景。
- 继续该过程,直到分类收敛为止。
如下图所示(图片提供:http://www.cs.ru.ac.za/research/g02m1682/)

示例
现在我们使用OpenCV进行抓取算法。OpenCV为此具有功能cv.grabCut(),我们将首先看到其参数:
- img - 输入图像
- mask - 这是一个掩码图像,在其中我们指定哪些区域是背景,前景或可能的背景/前景等。这是通过以下标志完成的:cv.GC_BGD,cv.GC_FGD, cv.GCPRBGD,cv.GCPRFGD,或直接将
0,1,2,3传递给图像。 - rect - 它是矩形的坐标,其中包括前景对象,格式为
(x,y,w,h) - bdgModel, fgdModel - 这些是算法内部使用的数组。你只需创建两个大小为
(1,65)的np.float64类型零数组。 - iterCount - 算法应运行的迭代次数。
- model - 应该是cv.GCINITWITH_RECT或cv.GCINITWITH_MASK或两者结合,决定我们要绘制矩形还是最终的修饰笔触。
首先让我们看看矩形模式。我们加载图像,创建类似的mask图像。我们创建fgdModel和bgdModel。我们给出矩形参数。一切都是直截了当的。让算法运行5次迭代。模式应为cv.GCINITWITH_RECT, 因为我们使用的是矩形。然后运行grabcut。修改mask图像。在新的mask图像中,像素将被标记有四个标记,分别表示上面指定的背景/前景。因此,我们修改mask,使所有0像素和2像素都置为0(即背景),而所有1像素和3像素均置为1(即前景像素)。现在,我们的最终mask已经准备就绪。只需将其与输入图像相乘即可得到分割的图像。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('messi5.jpg')
mask = np.zeros(img.shape[:2],np.uint8)
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
rect = (50,50,450,290)
cv.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv.GC_INIT_WITH_RECT)
mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
img = img*mask2[:,:,np.newaxis]
plt.imshow(img),plt.colorbar(),plt.show()
查看以下结果:
糟糕,梅西的头发不见了。谁会喜欢没有头发的梅西?我们需要把它找回来。因此,我们将使用1像素(确保前景)进行精细修饰。同时,一些不需要的地面也出现在图片里。我们需要删除它们。在那里,我们给出了一些0像素的修饰(确保背景)。因此,如现在所说,我们在以前的情况下修改生成的mask。
我实际上所做的是,我在paint应用程序中打开了输入图像,并在图像中添加了另一层。使用画笔中的画笔工具,我在新图层上用白色标记了错过的前景(头发,鞋子,球等),而用白色标记了不需要的背景(例如logo,地面等)。然后用灰色填充剩余的背景。然后将该mask图像加载到OpenCV中,编辑我们在新添加的mask图像中具有相应值的原始mask图像。
检查以下代码:
#newmask是我手动标记过的mask图像
newmask = cv.imread('newmask.png',0)
# 标记为白色(确保前景)的地方,更改mask = 1
# 标记为黑色(确保背景)的地方,更改mask = 0
mask[newmask == 0] = 0
mask[newmask == 255] = 1
mask, bgdModel, fgdModel = cv.grabCut(img,mask,None,bgdModel,fgdModel,5,cv.GC_INIT_WITH_MASK)
mask = np.where((mask==2)|(mask==0),0,1).astype('uint8')
img = img*mask[:,:,np.newaxis]
plt.imshow(img),plt.colorbar(),plt.show()
查看以下结果:

就是这样了。在这里,你无需直接在rect模式下初始化,而可以直接进入mask模式。只需用2像素或3像素(可能的背景/前景)标记mask图像中的矩形区域。然后像在第二个示例中一样,将我们的sure_foreground标记为1像素。然后直接在mask模式下应用grabCut功能。
练习
- OpenCV示例包含一个示例catchcut.py,这是一个使用grabcut的交互式工具。检查。另请观看有关如何使用它的youtube视频。
- 在这里,你可以通过绘制矩形和使用鼠标笔触使其成为交互式示例,创建轨迹栏以调整笔触宽度等。
欢迎关注磐创博客资源汇总站:http://docs.panchuang.net/
欢迎关注PyTorch官方中文教程站:http://pytorch.panchuang.net/
OpenCV中文官方文档:http://woshicver.com/
OpenCV-Python 交互式前景提取使用GrabCut算法 | 三十五的更多相关文章
- Python进阶(三十五)-Fiddler命令行和HTTP断点调试
Python进阶(三十五)-Fiddler命令行和HTTP断点调试 一. Fiddler内置命令 上一节(使用Fiddler进行抓包分析)中,介绍到,在web session(与我们通常所说的se ...
- 孤荷凌寒自学python第三十五天python的文件操作之针对文件操作的os模块的相关内容
孤荷凌寒自学python第三十五天python的文件操作之针对文件操作的os模块的相关内容 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 一.打开文件后,要务必记得关闭,所以一般的写法应当 ...
- Python学习之旅(三十五)
Python基础知识(34):电子邮件(Ⅰ) 几乎所有的编程语言都支持发送和接收电子邮件 在使用Python收发邮件前,请先准备好至少两个电子邮件,如xxx@163.com,xxx@sina.com, ...
- python数据结构与算法第十五天【二叉树】
1.树的特点 (1)每个节点有零个或多个子节点: (2)没有父节点的节点称为根节点: (3)每一个非根节点有且只有一个父节点: (4)除了根节点外,每个子节点可以分为多个不相交的子树: 2.树的种类 ...
- OpenCV-Python 图像分割与Watershed算法 | 三十四
目标 在本章中, 我们将学习使用分水岭算法实现基于标记的图像分割 我们将看到:cv.watershed() 理论 任何灰度图像都可以看作是一个地形表面,其中高强度表示山峰,低强度表示山谷.你开始用不同 ...
- 第三十五节,目标检测之YOLO算法详解
Redmon, J., Divvala, S., Girshick, R., Farhadi, A.: You only look once: Unified, real-time object de ...
- python接口自动化(三十五)-封装与调用--流程类接口关联(详解)
简介 流程相关的接口,主要用 session 关联,如果写成函数(如上篇),s 参数每个函数都要带,每个函数多个参数,这时候封装成类会更方便.在这里我们还是以博客园为例,带着小伙伴们实践一下. 接口封 ...
- Java数据结构和算法(十五)——无权无向图
前面我们介绍了树这种数据结构,树是由n(n>0)个有限节点通过连接它们的边组成一个具有层次关系的集合,把它叫做“树”是因为它看起来像一棵倒挂的树,包括二叉树.红黑树.2-3-4树.堆等各种不同的 ...
- Python之路(第三十五篇) 并发编程:操作系统的发展史、操作系统的作用
一.操作系统发展史 第一阶段:手工操作 —— 真空管和穿孔卡片 第一代之前人类是想用机械取代人力,第一代计算机的产生是计算机由机械时代进入电子时代的标志,从Babbage失败之后一直到第二次世界大 ...
随机推荐
- How to Write a README on GitHub
最近在寫 GitHub 上的 README,發現這個東西好像每個人的寫法都不太一樣,於是稍微整理了一下自己覺得大概要包含哪些內容. Motivation 顧名思義就是簡介一下為什麼會有這個專案,以及這 ...
- 在博客中显示图片_Mac版
主要是防止自己忘掉 为了解决一开始自己想在写入的博客中添加本地图片,直接链接的话在自己的电脑倒是可以显示图片,但是在别人的电脑上就没办法加载图片了,问各路大神也没人愿意解答,百度也没有想要的答案,只好 ...
- Leetcode 239题 滑动窗口最大值(Sliding Window Maximum) Java语言求解
题目链接 https://leetcode-cn.com/problems/sliding-window-maximum/ 题目内容 给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧 ...
- 丰富图文详解B-树原理,从此面试再也不慌
本文始发于个人公众号:TechFlow,原创不易,求个关注 本篇原计划在上周五发布,由于太过硬核所以才拖到了这周五.我相信大家应该能从标题当中体会到这个硬核. 周五的专题是大数据和分布式,我最初的打算 ...
- node跨域方法
第一种:jsonp 参看用nodejs实现json和jsonp服务 第二种:res.wirteHeadnode部分 var http = require('http') var url = requi ...
- OpenCV图像增强(python)
为了得到更加清晰的图像我们需要通过技术对图像进行处理,比如使用对比度增强的方法来处理图像,对比度增强就是对图像输出的灰度级放大到指定的程度,获得图像质量的提升.本文主要通过代码的方式,通过OpenCV ...
- Ubuntu16.04下安装nvidia-docker2
若docker-ce.nvidia.CUDA等都安装完成之后,开启docker服务时,能够正常运行,并有预测结果,那表示服务开启没问题:若都安装成功之后,用docker命令开启服务时,一直报错,可能表 ...
- java算法--循环队列
循环队列 我们再用队列得时候不知道发没发现这样一个问题. 这是一个只有三个位置得队列,在进行三次加入(addqueue)操作和三次取出(get)操作之后再进行加入操作时候的样子.明显可以看到,队列已经 ...
- 《ASP.NET Core 3框架揭秘》博文汇总
在过去一段时间内,写了一系列关于ASP.NET Core 3相关的文章,其中绝大部分来源于即将出版的<ASP.NET Core 3框架揭秘>(博文只能算是"初稿",与书 ...
- C语言程序设计(十一) 指针和数组
第十一章 指针和数组 一旦给出数组的定义,编译系统就会为其在内存中分配固定的存储单元,相应的,数组的首地址也就确定了 C语言中的数组名有特殊的含义,它代表存放数组元素的连续存储空间的首地址 //L11 ...