Python OpenCV GrabCut进行前景分割和提取
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)一个与分割近似的蒙版
并迭代以下步骤:
- 通过高斯混合模型(GMM)估算前景和背景的颜色分布
- 在像素标签上构建Markov随机字段(即前景与背景)
- 应用图割优化最终细分结果
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进行前景分割和提取相关推荐
- OpenCV GrabCut算法前景分割和提取
目录 一.OpenCv Grabcut算法:前景提取与分割(Foreground segmentation and extraction) (一)算法工作原理 (二)opencv函数cv2.grabC ...
- OpenCV GrabCut算法:前景分割和提取
目录 一.OpenCv Grabcut算法:前景提取与分割(Foreground segmentation and extraction) (一)算法工作原理 (二)opencv函数cv2.grabC ...
- python 黑白tif提取边界像素坐标_OpenCV GrabCut算法:前景分割和提取
点击上方"蓝色小字"关注我呀 文章翻译自光头哥哥的博客,原文链接: https://www.pyimagesearch.com/2020/07/27/opencv-grabcut- ...
- Python+OpenCV利用KNN背景分割器进行静态场景行人检测与轨迹跟踪
前言 视频图像中的目标检测与跟踪,是计算机视觉的基础课题,同时具有广泛的应用价值.视觉目标(单目标)跟踪任务就是在给定某视频序列初始帧的目标大小与位置的情况下,预测后续帧中该目标的大小与位置.本篇文章 ...
- Python + OpenCV 把书法里的单字提取出来
搭建这个OpneCV 在 Python 的环境,人们推荐用Python2,我作死用的是VSCode编译器,(⊙o⊙)- ,支持不太好,没有智能补全,都是自己上网查函数,装的时候我是用pip insta ...
- python opencv PIL 视频分割成图片 图片合成为视频 修改图片大小(抗锯齿)
Python代码将原有的视频分割成图片,我的例子是一帧一帧的分割 用python+opencv完成视频的分割 import cv2 #导入opencv模块 print(2) #测试是否运行 vc=cv ...
- python opencv旋转_Python opencv实现与rotatedrect类似的矩形旋转,pythonopencv,RotatedRect
本文原理:先旋转矩形到指定角度,然后提取矩形外轮廓,从而获取旋转后的矩形坐标点. #!/usr/bin/env python3 # -*- coding: utf-8 -*- # @Author: t ...
- Python+OpenCV:交互式图像前景提取(Interactive Foreground Extraction using GrabCut Algorithm)
Python+OpenCV:交互式图像前景提取(Interactive Foreground Extraction using GrabCut Algorithm) ################# ...
- OpenCV的视频背景/前景分割(背景建模/前景提取)类cv::bgsegm::BackgroundSubtractorGSOC的使用示例代码及运行效果
关于类cv::bgsegm::BackgroundSubtractorCNT的大概情况,我已在博文https://blog.csdn.net/wenhao_ir/article/details/125 ...
最新文章
- Java项目:无库版银行管理系统(java+Gui+文档)
- 使用Node.js写一个简单的api接口
- java试用(1)hello world
- 官宣丨中国移动云能力中心新增5项可信云认证,斩获2项大奖!
- C语言关系运算符及其表达式
- VI命令详解(大全)
- 数据库系统-数据库设计
- 数值计算(四)——插值法(3)Hermite插值法
- 技术江湖--讲座笔记
- centos7+ 安装RabbitMQ
- 单位提前解除劳动关系怎么赔偿
- 小马哥-----高仿三星G9006(G900S G900H G900W)拆机主板图与开机界面图 6582芯片主板为S105
- 免费WAP改变自我现状 谈四个赢利途
- 浅谈巫师2的战斗难度策略
- 信号建模-呼吸心跳信号检测方法(三)
- 把手账打印成书 把回忆装订成册
- 非虫 android应用逆向,android逆向-ARM汇编基础-非虫笔记
- 【滤波器】各种滤波器的理解与学习
- Modbus-RTU功能码
- 再来一篇,看jdk源码大师亲自操刀编写的集合源码