Python OpenCV GrabCut进行前景分割和提取

  • 1. 效果图
  • 1.1 边界框GrabCut效果图
    • 1.2 Mask GrabCut效果图
  • 2. GrabCut原理
    • 2.1 GrabCut是什么及步骤
    • 2.2 grabCut(img, mask, rect, bgdModel, fgdModel, iterCount)
    • 2.3 GrabCut与R-CNN对比
  • 3. 源代码
    • 3.1 根据边界框执行GrabCut
    • 3.2 根据Mask执行GrabCut
  • 参考

图像掩模Mask是图像处理的基本概念。这篇博客将介绍如何使用OpenCV和GrabCut算法执行前景分割和提取。Grabcut算法通过CV2.grabcut实现,可以通过以下任一方法初始化:

1)前景边界框,屏蔽近似图像中对象的像素的位置
2)使用近似前景掩码;

虽然基于深度学习的图像分割网络(例如,掩码R-CNN和U-NET)实际检测和近似图像中对象的掩码,但也并不完美,可能带有噪点;可以辅助使用Grabcut来清理这些细分网络返回的“杂乱”掩码!

1. 效果图

1.1 边界框GrabCut效果图

输入图像 VS GrabCut Mask VS 最终效果图

构建第一张输入图像可以使用Thresholding、Canny边缘检测,轮廓检测,基于深度学习的分割以及cv2.inRange(hsv,low_color,high_color)颜色范围

效果图2:输入图像 VS GrabCut Mask VS 最终效果图

1.2 Mask GrabCut效果图

原图与Mask叠加的粗糙效果图:

确定背景 VS 可能背景 VS 确定前景 VS 可能前景效果图:

原图 VS GrabCut 蒙版图 VS 叠加效果图如下: 可以看到右侧最终效果图比原始图还要干净,去掉了一些杂质噪点;

2. GrabCut原理

2.1 GrabCut是什么及步骤

  • 在深度学习和语义/实例分割如Mask-RCNN,U-Net之前,GrabCut是从背景准确分割图像前景的方法。
  • GrabCut 接受带有(1)一个边界框的输入图像,该边界框指定了要分割的图像中对象的位置;(2)一个与分割近似的蒙版

并迭代以下步骤:

  1. 通过高斯混合模型(GMM)估算前景和背景的颜色分布
  2. 在像素标签上构建Markov随机字段(即前景与背景)
  3. 应用图割优化最终细分结果

2.2 grabCut(img, mask, rect, bgdModel, fgdModel, iterCount)

1. 入参

  • img: 输入图像,无符号8位int的 BRG三通道图像;
  • mask:单通道无符号8位int,默认边界框初始化(即cv2.GC_INIT_WITH_RECT); 否则GrabCut假设执行掩码初始化(cv2.GC_INIT_WITH_MASK);
  • rect:包含要分割的区域的边界框矩形。仅在设置模式是cv2.GC_INIT_WITH_RECT时使用此参数;
  • bgModel:当对背景建模时,GrabCut内部使用的临时数组;
  • fgModel:对前景建模时,GrabCut使用的临时数组;
  • iterCount:对前景和背景建模时,GrabCut将执行的迭代次数。迭代次数越多,GrabCut运行时间将越长,结果会更好;
  • mode:使用初始化GrabCut的模式类型,cv2.GC_INIT_WITH_RECT或cv2.GC_INIT_WITH_MASK;

2. 返回值:一个三元组

  • mask:应用GrabCut后的输出蒙版
  • bgModel:用于背景建模的临时数组
  • fgModel:用于前景建模的临时数组

2.3 GrabCut与R-CNN对比

  • GrabCut是好的,但并不是完美的;和背景粘连色很相似的可能会丢失。
  • 虽然R-CNN和U-Net更快,但它们可能得出凌乱的Mask结果。可以使用grabcut来帮助清理Mask的部分污点。

3. 源代码

3.1 根据边界框执行GrabCut

# USAGE
# python grabcut_bbox.py --image images/06_output.jpgimport argparse
import os
import timeimport cv2
import imutils
# 导入必要的包
import numpy as np# 构建命令行参数及解析
# --images 输入图像
# --iter 要执行的grabcut迭代的数量,其中较小的值导致更快的总时间;更大的值导致较慢的运行,但更理想的分段结果;
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", type=str,default=os.path.sep.join(["images", "06_output.jpg"]),help="path to input image that we'll apply GrabCut to")
ap.add_argument("-c", "--iter", type=int, default=10,help="# of GrabCut iterations (larger value => slower runtime)")
args = vars(ap.parse_args())# 从磁盘加载输入图像,然后为grabcut输出的mask分配空间,将和输入图像拥有相同的空间维度
image = cv2.imread(args["image"])
width = image.shape[0]
image = imutils.resize(image, width=450)
cv2.imshow("Input", image)
cv2.waitKey(0)
mask = np.zeros(image.shape[:2], dtype="uint8")# 定义边界框坐标,大致包括脸和脖子区域
rect = (81, 262, 171, 168)# 为俩个数组分配内存,GrabCut算法从背景中分割前景时会用到
fgModel = np.zeros((1, 65), dtype="float")
bgModel = np.zeros((1, 65), dtype="float")# 使用GrabCut应用边界框分割方法
start = time.time()
(mask, bgModel, fgModel) = cv2.grabCut(image, mask, rect, bgModel,fgModel, iterCount=args["iter"], mode=cv2.GC_INIT_WITH_RECT)
end = time.time()
print("[INFO] applying GrabCut took {:.2f} seconds".format(end - start))# 输出mask有4种可能的结果:标记每一个像素为1、确定背景;2、确定前景;3、可能背景;4、可能前景
# 0、 2、 1、 3
values = (("Definite Background", cv2.GC_BGD),("Probable Background", cv2.GC_PR_BGD),("Definite Foreground", cv2.GC_FGD),("Probable Foreground", cv2.GC_PR_FGD),
)# 遍历可能的MaskGrab区域
for (name, value) in values:print(name, value)# 为当前值构建maskprint("[INFO] showing mask for '{}'".format(name))valueMask = (mask == value).astype("uint8") * 255# mask可视化cv2.imshow(name, valueMask)cv2.waitKey(0)# 设置所有确定背景和可能背景为0,设置所有确定前景和可能前景为1
outputMask = np.where((mask == cv2.GC_BGD) | (mask == cv2.GC_PR_BGD), 0, 1)# 缩放mask的像素值由[0,1] 到 [0,255]
outputMask = (outputMask * 255).astype("uint8")# 使用mask对图像进行按位与得到最终输出图像
output = cv2.bitwise_and(image, image, mask=outputMask)
# output_or = cv2.bitwise_or(image, image, mask=outputMask)
# output_xor = cv2.bitwise_xor(image, image, mask=outputMask)
# output_not = cv2.bitwise_not(image, image, mask=outputMask)# 展示输入图像,GrabCut mask, 原始图像的前景+ 通过Grabcut Mask得到的背景的迭加图
cv2.imshow("Input", image)
cv2.imshow("GrabCut Mask", outputMask)
cv2.imshow("GrabCut Output", output)
# cv2.imshow("GrabCut Output not", output_not)
# cv2.imshow("GrabCut Output xor", output_xor)
# cv2.imshow("GrabCut Output or", output_or)
# cv2.imwrite("D:\\py-demo\\20210331\\grab_cut\\images\\06_.jpg", imutils.resize(output_not, width=width))
cv2.waitKey(0)

3.2 根据Mask执行GrabCut

Mask可以通过:在照片编辑软件如如Photoshop,Gimp中手动创建,也可应用基本图像处理操作,例如阈值,边缘检测,轮廓滤波等,或者利用基于深度学习的分割网络(例如,掩码R-CNN和U-NET)。

# USAGE
# python grabcut_mask.py --image images/thl.jpg --mask images/thl_mask2.jpg# 导入必要的包
import numpy as np
import argparse
import time
import cv2
import os
import imutils# 构建命令行参数及解析
# --image 输入图像
# --mask 输入Mask
# --iter 迭代次数,非必选,默认10
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", type=str,default=os.path.sep.join(["images", "thl.jpg"]),help="path to input image that we'll apply GrabCut to")
ap.add_argument("-mask", "--mask", type=str,default=os.path.sep.join(["images", "thl_mask2.jpg"]),help="path to input mask")
ap.add_argument("-c", "--iter", type=int, default=10,help="# of GrabCut iterations (larger value => slower runtime)")
args = vars(ap.parse_args())# 从磁盘加载输入图像及Mask
image = cv2.imread(args["image"])
image = imutils.resize(image, width=400)
mask = cv2.imread(args["mask"], cv2.IMREAD_GRAYSCALE)
print(image.shape, mask.shape)# 应用按位与看直接合并得到的最粗糙的结果
roughOutput = cv2.bitwise_and(image, image, mask=mask)# 展示粗糙的,近似输出结果
cv2.imshow("Rough Output", roughOutput)
cv2.waitKey(0)# Mask中任意大于0的像素将被认为是可能的前景,为0的认为是确定的背景
mask[mask > 0] = cv2.GC_PR_FGD
mask[mask == 0] = cv2.GC_BGD# 为Grabcut的前景和背景模型分配内存
fgModel = np.zeros((1, 65), dtype="float")
bgModel = np.zeros((1, 65), dtype="float")# 使用近似掩模分段在图像上执行Grabcut算法
start = time.time()
(mask, bgModel, fgModel) = cv2.grabCut(image, mask, None, bgModel,fgModel, iterCount=args["iter"], mode=cv2.GC_INIT_WITH_MASK)
end = time.time()
print("[INFO] applying GrabCut took {:.2f} seconds".format(end - start))# 输出Mask有4种可能的值:1、确定背景 2、确定前景 3、可能背景 4、可能前景
values = (("Definite Background", cv2.GC_BGD),("Probable Background", cv2.GC_PR_BGD),("Definite Foreground", cv2.GC_FGD),("Probable Foreground", cv2.GC_PR_FGD),
)# 遍历GrabCut可能的结果值
for (name, value) in values:# 为当前值构建maskprint("[INFO] showing mask for '{}'".format(name))valueMask = (mask == value).astype("uint8") * 255# 可视化maskcv2.imshow(name, valueMask)cv2.waitKey(0)# 设置所有确定背景、可能背景像素值为0,设置确定前景、可能前景像素值为1,然后缩放像素值[0,1]到[0, 255]
# 相当于删除了背景,提取到了前景
outputMask = np.where((mask == cv2.GC_BGD) | (mask == cv2.GC_PR_BGD), 0, 1)
outputMask = (outputMask * 255).astype("uint8")# 使用mask应用按位与得到最终输出结果图
output = cv2.bitwise_and(image, image, mask=outputMask)# 展示输出图像,GrabCut前景Mask,GrabCut结果图
cv2.imshow("Input", image)
cv2.imshow("GrabCut Mask", outputMask)
cv2.imshow("GrabCut Output", output)
cv2.waitKey(0)

参考

  • https://www.pyimagesearch.com/2020/07/27/opencv-grabcut-foreground-segmentation-and-extraction/

Python OpenCV GrabCut进行前景分割和提取相关推荐

  1. OpenCV GrabCut算法前景分割和提取

    目录 一.OpenCv Grabcut算法:前景提取与分割(Foreground segmentation and extraction) (一)算法工作原理 (二)opencv函数cv2.grabC ...

  2. OpenCV GrabCut算法:前景分割和提取

    目录 一.OpenCv Grabcut算法:前景提取与分割(Foreground segmentation and extraction) (一)算法工作原理 (二)opencv函数cv2.grabC ...

  3. python 黑白tif提取边界像素坐标_OpenCV GrabCut算法:前景分割和提取

    点击上方"蓝色小字"关注我呀 文章翻译自光头哥哥的博客,原文链接: https://www.pyimagesearch.com/2020/07/27/opencv-grabcut- ...

  4. Python+OpenCV利用KNN背景分割器进行静态场景行人检测与轨迹跟踪

    前言 视频图像中的目标检测与跟踪,是计算机视觉的基础课题,同时具有广泛的应用价值.视觉目标(单目标)跟踪任务就是在给定某视频序列初始帧的目标大小与位置的情况下,预测后续帧中该目标的大小与位置.本篇文章 ...

  5. Python + OpenCV 把书法里的单字提取出来

    搭建这个OpneCV 在 Python 的环境,人们推荐用Python2,我作死用的是VSCode编译器,(⊙o⊙)- ,支持不太好,没有智能补全,都是自己上网查函数,装的时候我是用pip insta ...

  6. python opencv PIL 视频分割成图片 图片合成为视频 修改图片大小(抗锯齿)

    Python代码将原有的视频分割成图片,我的例子是一帧一帧的分割 用python+opencv完成视频的分割 import cv2 #导入opencv模块 print(2) #测试是否运行 vc=cv ...

  7. python opencv旋转_Python opencv实现与rotatedrect类似的矩形旋转,pythonopencv,RotatedRect

    本文原理:先旋转矩形到指定角度,然后提取矩形外轮廓,从而获取旋转后的矩形坐标点. #!/usr/bin/env python3 # -*- coding: utf-8 -*- # @Author: t ...

  8. Python+OpenCV:交互式图像前景提取(Interactive Foreground Extraction using GrabCut Algorithm)

    Python+OpenCV:交互式图像前景提取(Interactive Foreground Extraction using GrabCut Algorithm) ################# ...

  9. OpenCV的视频背景/前景分割(背景建模/前景提取)类cv::bgsegm::BackgroundSubtractorGSOC的使用示例代码及运行效果

    关于类cv::bgsegm::BackgroundSubtractorCNT的大概情况,我已在博文https://blog.csdn.net/wenhao_ir/article/details/125 ...

最新文章

  1. Java项目:无库版银行管理系统(java+Gui+文档)
  2. 使用Node.js写一个简单的api接口
  3. java试用(1)hello world
  4. 官宣丨中国移动云能力中心新增5项可信云认证,斩获2项大奖!
  5. C语言关系运算符及其表达式
  6. VI命令详解(大全)
  7. 数据库系统-数据库设计
  8. 数值计算(四)——插值法(3)Hermite插值法
  9. 技术江湖--讲座笔记
  10. centos7+ 安装RabbitMQ
  11. 单位提前解除劳动关系怎么赔偿
  12. 小马哥-----高仿三星G9006(G900S G900H G900W)拆机主板图与开机界面图 6582芯片主板为S105
  13. 免费WAP改变自我现状 谈四个赢利途
  14. 浅谈巫师2的战斗难度策略
  15. 信号建模-呼吸心跳信号检测方法(三)
  16. 把手账打印成书 把回忆装订成册
  17. 非虫 android应用逆向,android逆向-ARM汇编基础-非虫笔记
  18. 【滤波器】各种滤波器的理解与学习
  19. Modbus-RTU功能码
  20. 再来一篇,看jdk源码大师亲自操刀编写的集合源码

热门文章

  1. CVPR2020行人重识别算法论文解读
  2. web站点的欢迎页面
  3. Python 实现九九乘法表
  4. Android Studio – Cannot resolve symbol ‘R’
  5. MySQL☞dual虚拟表
  6. PASCAL VOC工具包解读
  7. Redis创建高可用集群教程【Windows环境】
  8. 微信小程序----map组件实现(获取定位城市天气或者指定城市天气数据)
  9. Go 学习笔记(29)— range 作用于字符串、数组、切片、字典、通道
  10. 【性格心理学】为什么我在关键时刻总是紧张?