在圖像處理中,我們經常會遇到帶有不必要邊界的圖片,特別是從掃描文件或某些繪圖軟體導出的二值化圖像。手動裁切固然可行,但當你需要處理大量圖像時,自動化就變得至關重要。
本文將介紹如何利用 OpenCV 的 cv2.floodFill
函數,高效且準確地移除二值化圖像中的白色邊界。
結果圖

核心概念:cv2.floodFill
洪水填充演算法
cv2.floodFill
是一種洪水填充演算法,它的基本思想是從一個指定的種子點(seed point)開始,向其周圍顏色相近的像素點蔓延,並將這些點填充為新的顏色。
在這份教學中,我們將利用這個特性來實現一個巧妙的技巧:
- 假設我們的圖像背景是白色的(像素值 255),而主要內容是黑色的(像素值 0)。
- 我們從圖像的四個邊界開始,尋找所有接觸到邊界的白色像素。
- 對於每一個找到的白色像素,我們將它作為種子點,啟動
floodFill
演算法。 - 將這些白色區域填充為黑色(像素值 0)。
- 因為洪水填充只會蔓延到顏色相近的區域,所以它會將所有與邊界相連的白色區域變成黑色,而不會影響到圖像內部被黑色像素包圍的白色區域。
程式碼解析
接下來,我們來詳細解析你提供的 Python 函式。
def remove_border_white(self, img):
# 假設 img 是二值化圖,白色為255,黑色為0
h, w = img.shape[:2]
# floodFill 運算需要一個比原圖長寬各多2的 mask
mask = np.zeros((h + 2, w + 2), np.uint8)
# 從四個邊界做 floodFill
# 遍歷頂部和底部邊界
for x in range(w):
if img[0, x] == 255:
cv2.floodFill(img, mask, (x, 0), 0)
if img[h - 1, x] == 255:
cv2.floodFill(img, mask, (x, h - 1), 0)
# 遍歷左側和右側邊界
for y in range(h):
if img[y, 0] == 255:
cv2.floodFill(img, mask, (0, y), 0)
if img[y, w - 1] == 255:
cv2.floodFill(img, mask, (w - 1, y), 0)
return img
h, w = img.shape[:2]
: 取得圖像的高度和寬度。mask = np.zeros((h + 2, w + 2), np.uint8)
:cv2.floodFill
函式需要一個額外的 mask 來標記已經被填充的區域。這個 mask 的尺寸必須比原始圖像的長寬都大 2,這是 OpenCV 函式規定的。for x in range(w):
: 這兩個for
迴圈是整個演算法的關鍵。它會遍歷圖像的所有邊界像素(img[0, x]
、img[h-1, x]
、img[y, 0]
和img[y, w-1]
)。if img[0, x] == 255:
: 如果邊界像素是白色(值為 255),就表示這個點是與外部白色邊界相連的點。cv2.floodFill(img, mask, (x, 0), 0)
: 呼叫floodFill
函式。- img: 要操作的圖像。
- mask: 洪水填充的掩碼。
- (x, 0): 洪水填充的起始點(種子點)。
- 0: 新填充的顏色值(我們將白色邊界填充為黑色)。
- 最後,函式返回處理後的圖像。
完整的 Python 範例
現在我們將這個函式應用到一個完整的範例中。這個範例會:
- 使用
numpy
和cv2
建立一個帶有白色邊界的簡單圖像。 - 呼叫
remove_border_white
函式來移除邊界。 - 使用
matplotlib.pyplot
顯示處理前後的對比圖,讓你清楚看到效果。
import cv2
import numpy as np
import matplotlib.pyplot as plt
def remove_border_white(img):
"""
使用 floodFill 演算法移除圖像中的白色邊界。
參數:
img (numpy.ndarray): 灰階或二值化圖像。假設白色為 255,黑色為 0。
回傳:
numpy.ndarray: 移除白邊後的圖像。
"""
# 確保圖像為灰階,如果不是則轉換
if len(img.shape) > 2:
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
h, w = img.shape[:2]
# floodFill 需要一個比原圖大2的 mask
mask = np.zeros((h + 2, w + 2), np.uint8)
# 複製圖像,以便在不修改原圖的情況下操作
img_filled = img.copy()
# 從四個邊界做 floodFill
# 遍歷頂部和底部邊界
for x in range(w):
if img_filled[0, x] == 255:
cv2.floodFill(img_filled, mask, (x, 0), 0)
if img_filled[h - 1, x] == 255:
cv2.floodFill(img_filled, mask, (x, h - 1), 0)
# 遍歷左側和右側邊界
for y in range(h):
if img_filled[y, 0] == 255:
cv2.floodFill(img_filled, mask, (0, y), 0)
if img_filled[y, w - 1] == 255:
cv2.floodFill(img_filled, mask, (w - 1, y), 0)
return img_filled
def create_image_with_white_border():
"""
建立一個帶有白色邊界的範例圖像。
"""
height, width = 400, 600
# 建立一個全黑的圖像
image = np.zeros((height, width), dtype=np.uint8)
# 繪製一個白色矩形作為內部的白色區域
cv2.rectangle(image, (100, 100), (500, 300), 255, -1)
# 繪製一個黑色的圓形來分離內部和外部的白色
cv2.circle(image, (300, 200), 50, 0, -1)
# 在圖像周圍添加白色邊框
image[:, 10:20] = 255 # 左邊
image[:, -20:-10] = 255 # 右邊
image[10:20, :] = 255 # 上邊
image[-20:-10, :] = 255 # 下邊
return image
if __name__ == "__main__":
# 建立一個帶有白色邊界的範例圖像
original_image = create_image_with_white_border()
# 複製圖像以保留原始版本
image_to_process = original_image.copy()
# 移除白邊
processed_image = remove_border_white(image_to_process)
# 使用 matplotlib 顯示處理前後的圖像對比
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.imshow(cv2.cvtColor(original_image, cv2.COLOR_BGR2RGB))
plt.title('org')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.imshow(cv2.cvtColor(processed_image, cv2.COLOR_BGR2RGB))
plt.title('after')
plt.axis('off')
plt.tight_layout()
plt.show()
這段程式碼首先會創建一個內部為白色、周圍有白色邊界、且中間有一個黑色圓圈的圖像。然後,它會調用 remove_border_white
函數來處理這張圖片。最後,利用 matplotlib
將原始圖和處理後的圖並排顯示,讓你清楚地看到白色邊界被移除,而圖像內部被黑色圓圈包圍的白色區域則保持不變。
希望這份教學能幫助你更好地理解並應用 cv2.floodFill
函數來解決圖像處理中的實際問題。