更新於 2024/10/02閱讀時間約 12 分鐘

[OpenCV][Python]使用GrabCut 來去背

iPhone也有去背的功能,那麼OpenCV能不能做到這件事呢?,答案是可以的

  • 如果圖像背景簡單且與前景有明顯的顏色區分,可以使用 色彩空間轉換閥值分割
  • 如果背景較為複雜一點點,但你可以提供一個大致的前景位置,則可以使用 GrabCut

結果圖

但在背景相當複雜的情況下,結果就不太好,若要好又要設很多條件才能完美切割。


GrabCut 是一種強大的前景提取演算法,特別適合處理具有複雜背景的圖像分割問題。它基於圖割(Graph Cut)的技術進行迭代優化,能夠在給定前景物體的大致區域後,自動分割出前景。

原文連結


GrabCut 演算法的核心原理

GrabCut 演算法基於 最大流最小割 的圖論技術。圖像中的每個像素被視為圖中的一個節點,而前景和背景被視為兩個特殊的節點:源點(Source)匯點(Sink)。演算法通過最大化像素到這兩個特殊點的連接來進行優化。這意味著,演算法會嘗試尋找一個切割線,將圖像中的節點劃分為前景和背景。

  • 數學模型:GrabCut 將前景和背景的像素顏色分布建模為 高斯混合模型(GMM)。在每次迭代中,算法會根據當前的分割結果不斷更新 GMM,並進一步優化分割。

cv2.grabCut 函數的語法:

cv2.grabCut(img, mask, rect, bgdModel, fgdModel, iterCount, mode)

各個參數說明:

  1. img
    • 類型:np.ndarray(輸入圖像)
    • 說明:這是需要進行分割的原始圖像。它應該是一個彩色圖像(通常是 BGR 格式)。演算法將根據這個圖像來計算前景和背景的分割。
    • 用途:分割目標圖像。
  2. mask
    • 類型:np.ndarray(遮罩)
    • 說明:這是一個與圖像大小相同的單通道遮罩圖像,表示哪些像素是前景、背景或不確定的區域。它通常在初始執行時全為 0。
    • 當使用 cv2.GC_INIT_WITH_RECT 模式時,矩形內的像素會被初始化為可能的前景,矩形外的像素會被初始化為背景。
    • 遮罩的值可以為以下幾個:
      cv2.GC_BGD(0):確定的背景像素。
      cv2.GC_FGD(1):確定的前景像素。
      cv2.GC_PR_BGD(2):可能的背景像素。
      cv2.GC_PR_FGD(3):可能的前景像素。
    • 用途:分割過程中會不斷更新這個遮罩,將像素標識為前景或背景
  3. rect
    • 類型:tuple(矩形邊界,格式為 (x, y, w, h))
    • 說明:用來定義包含前景物體的大致區域。矩形內的像素被初始化為前景,矩形外的像素被初始化為背景。該矩形不需要完全精確包圍前景,但要盡量包含整個前景,避免太多背景。此參數僅在 cv2.GC_INIT_WITH_RECT 模式下使用。
    • 用途:指定初始前景和背景區域。
  4. bgdModel(背景模型):
    • 類型:np.ndarray,大小為 (1, 65),浮點數類型。
    • 說明:這是背景模型,它是由 GrabCut 演算法內部計算並用來描述背景顏色分布的數據結構。該陣列在運行之前需要初始化為全 0。演算法在運行過程中會更新此模型。
    • 用途:演算法內部用來表示背景的顏色統計模型,外部不需要干預。
  5. fgdModel(前景模型):
    • 類型:np.ndarray,大小為 (1, 65),浮點數類型。
    • 說明:這是前景模型,它是由 GrabCut 演算法內部計算並用來描述前景顏色分布的數據結構。該陣列在運行之前需要初始化為全 0。演算法在運行過程中會更新此模型。
    • 用途:演算法內部用來表示前景的顏色統計模型,外部不需要干預。
  6. iterCount
    • 類型:整數
    • 說明:表示演算法應該進行的迭代次數。演算法是一個迭代優化過程,每次迭代會優化分割結果。通常 5-10 次迭代已經能夠產生滿意的結果。如果結果不理想,增加迭代次數可能會改善結果。
    • 用途:控制演算法的收斂過程。
  7. mode
    • 類型:int,表示初始化模式。
    • 說明:這個參數指定了 GrabCut 的初始化方式,有兩種模式:cv2.GC_INIT_WITH_RECT:使用矩形來初始化前景和背景。這是最常用的模式,矩形內的像素被初始化為前景,矩形外的像素被初始化為背景。cv2.GC_INIT_WITH_MASK:使用遮罩來初始化前景和背景。如果遮罩已經部分標定了前景和背景區域,可以使用這種模式進行更精確的初始化。
    • 用途:指定 GrabCut 的初始化方法,是使用矩形還是掩膜進行初始分割。

使用矩形來初始化前景和背景

範例

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 顯示圖像的函數
def show_image(image, title="Image"):
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.title(title)
plt.axis('off') # 不顯示座標軸
plt.show()

# 讀取圖像
image_path = 'your_image_path.png' # 替換成你的圖像路徑
img = cv2.imread(image_path)

# 初始化掩膜
mask = np.zeros(img.shape[:2], np.uint8)

# 背景和前景模型(由 GrabCut 需要)
bgdModel = np.zeros((1, 65), np.float64)
fgdModel = np.zeros((1, 65), np.float64)

# 定義一個矩形來包含前景物體
rect = (350, 200, 300, 300) # 根據圖像大小進行調整 x,y,w,h
# 在原圖上畫出矩形框
img_with_rect = img.copy()
cv2.rectangle(img_with_rect, (rect[0], rect[1]), (rect[0] + rect[2], rect[1] + rect[3]), (0, 255, 0), 2)

# 顯示畫出矩形框的圖像
show_image(img_with_rect, "Image with Rectangle")
# 執行 GrabCut 算法
cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 10, cv2.GC_INIT_WITH_RECT)

# 修改掩膜以提取前景
mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
grabcut_result = img * mask2[:, :, np.newaxis]

# 顯示原圖和 GrabCut 結果
show_image(img, "Original Image")
show_image(grabcut_result, "GrabCut Foreground Extraction")

原圖

矩形位置

框選想保留的物件位置

GrabCut 結果

使用遮罩來初始化前景和背景

先標示前景與背景的區域,設定一個矩形範圍。

範例

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 顯示圖像的函數
def show_image(image, title="Image"):
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.title(title)
plt.axis('off') # 不顯示座標軸
plt.show()

# 讀取圖像
image_path = 'your_image_path.png' # 替換成你的圖像路徑
img = cv2.imread(image_path)

# 初始化掩膜
mask = np.zeros(img.shape[:2], np.uint8)

# 背景和前景模型(由 GrabCut 需要)
bgdModel = np.zeros((1, 65), np.float64)
fgdModel = np.zeros((1, 65), np.float64)

# 定義一個矩形來包含前景物體
rect = (350, 200, 300, 300) # 根據圖像大小進行調整 x,y,w,h

# 手動調整掩膜來改善前景檢測
mask[200:500, 350:650] = cv2.GC_FGD # 標記這部分是確定的前景
mask[800:1000, 40:300] = cv2.GC_BGD # 標記這部分是確定的背景

# 再次執行 GrabCut
cv2.grabCut(img, mask, None, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_MASK)

# 修改掩膜以提取前景
mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
grabcut_result = img * mask2[:, :, np.newaxis]

# 顯示原圖和 GrabCut 結果
show_image(img, "Original Image")
show_image(grabcut_result, "GrabCut Foreground Extraction")

遮罩的進階用法

設定符合顏色範圍的部分,設定為前景,以下範例設定為紅色的部分,就可只截取氣球的部分了

# 初始化掩膜
mask = np.zeros(img.shape[:2], np.uint8)
# 將圖像轉換為 RGB 色彩空間
RGB_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# 定義顏色範圍
lower_color = np.array([150, 0, 0]) # 低閥值
upper_color = np.array([255, 70, 70]) # 高閥值

# 創建遮罩,篩選出符合顏色範圍的部分
mask_red = cv2.inRange(RGB_img, lower_color, upper_color)

# 初始化 GrabCut 所需的掩膜
mask_grabcut = np.zeros(img.shape[:2], np.uint8)

# 使用篩選出的顏色區域更新掩膜,將篩選出的部分設為"可能前景"(cv2.GC_PR_FGD
mask_grabcut[mask_red > 0] = cv2.GC_PR_FGD













分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.