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进行前景分割和提取的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用Python,OpenCV和Scik
- 下一篇: 惠字开头的成语有哪些啊?