使用 OpenCV-Python 進行交互式前景提取

2020-02-14     人工智慧遇見磐創

目標

在本章中,

  • 我們將看到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_RECTcv.GCINITWITH_MASK或兩者結合,決定我們要繪製矩形還是最終的修飾筆觸。

首先讓我們看看矩形模式。我們加載圖像,創建類似的mask圖像。我們創建fgdModel和bgdModel。我們給出矩形參數。一切都是直截了當的。讓算法運行5次疊代。模式應為cv.GCINITWITH_RECT, 因為我們使用的是矩形。然後運行grabcut。修改mask圖像。在新的mask圖像中,像素將被標記有四個標記,分別表示上面指定的背景/前景。因此,我們修改mask,使所有0像素和2像素都置為0(即背景),而所有1像素和3像素均置為1(即前景像素)。現在,我們的最終mask已經準備就緒。只需將其與輸入圖像相乘即可得到分割的圖像。

import numpy as npimport cv2 as cvfrom matplotlib import pyplot as pltimg = 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 = 0mask[newmask == 0] = 0mask[newmask == 255] = 1mask, 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功能。

練習

  1. OpenCV示例包含一個示例catchcut.py,這是一個使用grabcut的交互式工具。檢查。另請觀看有關如何使用它的youtube視頻。
  2. 在這裡,你可以通過繪製矩形和使用滑鼠筆觸使其成為交互式示例,創建軌跡欄以調整筆觸寬度等。
文章來源: https://twgreatdaily.com/zh-mo/48fdSXAB3uTiws8KnQDB.html