HOWTO · Python
OpenCV 擴張
在本文中,我們將解釋形態變換概念,例如侵蝕和擴張,我們使用它們在 OpenCV 中的影象中從給定物件的邊界新增和刪除畫素。
本頁內容
使用 cv2.dilate 擴大影像中較亮的前景區域。在 OpenCV 中,膨脹會檢查每個像素周圍的鄰域,並依照結構核心擴展前景。灰階影像與二值影像都能使用相同的操作;下方範例以只有一個亮像素的小型灰階影像示範,讓效果更容易觀察。
OpenCV 膨脹的作用
形態學操作會使用核心(也稱為結構元素)處理影像。對二值影像而言,前景像素通常以白色 (255) 表示,背景則以黑色 (0) 表示。膨脹會以核心鄰域中的最大值設定每個輸出像素。因此,較亮的區域會擴大,彼此接近的亮色元件也可能連在一起。侵蝕則使用鄰域中的最小值,因此亮色區域會縮小。
鄰域會在影像上逐一移動,方式類似二維卷積所使用的局部視窗;但膨脹取的是鄰域最大值,而不是計算加權總和。輸入為灰階影像時,這個最大值操作也會使局部區域變亮,並不限於只有兩種值的遮罩。
使用 Python 和 OpenCV 執行膨脹
這個獨立範例使用一張只有一個亮像素的小型灰階影像,以及一個 3 × 3 的矩形鄰域。如果已安裝 OpenCV 和 NumPy,範例會使用 cv2.dilate;否則會改用標準函式庫的備用實作,套用相同的鄰域最大值規則,讓範例仍可執行。
本節中的程式碼以及顯示的標籤或輸出仍為英文。
import importlib.util
image = [
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 255, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
]
if importlib.util.find_spec("cv2") and importlib.util.find_spec("numpy"):
import cv2
import numpy as np
kernel = np.ones((3, 3), dtype=np.uint8)
dilated = cv2.dilate(np.array(image, dtype=np.uint8), kernel, iterations=1).tolist()
else:
dilated = [
[
max(image[y][x] for y in range(max(0, row - 1), min(5, row + 2))
for x in range(max(0, col - 1), min(5, col + 2)))
for col in range(5)
]
for row in range(5)
]
print(dilated)
[[0, 0, 0, 0, 0], [0, 255, 255, 255, 0], [0, 255, 255, 255, 0], [0, 255, 255, 255, 0], [0, 0, 0, 0, 0]]
若要處理影像檔,請使用 cv2.imread("input.png", cv2.IMREAD_GRAYSCALE) 載入灰階來源,並在呼叫 cv2.dilate 前確認回傳值不是 None。路徑錯誤或檔案格式不受支援時,可能會得到 None;對這個值執行形態學操作無法產生影像。若之後需要比較輸入與輸出,請將兩個陣列分開保留。
核心與迭代次數如何影響膨脹
核心會定義鄰域及其形狀。較大的核心會檢查較廣的範圍,通常也會讓亮色特徵擴展得更遠。矩形核心適合均勻地向水平方向與垂直方向擴展;橢圓形或十字形核心則會改變鄰域的幾何形狀。請依照想要影響的間隙與特徵選擇核心的尺寸和形狀,再用具有代表性的影像檢視結果。
iterations 參數會重複執行膨脹。增加迭代次數會累積擴展效果,可能使狹窄間隙閉合,或讓相鄰區域合併。建議先從一個迭代次數和較小的核心開始,再一次調整一項設定。視覺上更強烈的結果不一定比較好:過度擴展可能會抹去細節。
比較膨脹、侵蝕、開運算與閉運算
侵蝕會縮小亮色前景區域;膨脹則會擴大它們。開運算先侵蝕再膨脹,視核心與影像而定,能移除小型亮色細節,同時保留較大的結構。閉運算先膨脹再侵蝕,能填補亮色結構中的小型暗色間隙。這些操作序列不保證還原每個原始像素;實際效果取決於結構元素和影像中的特徵。
若要處理小型亮色雜訊,可使用開運算;若要處理亮色前景中的小型暗孔或斷裂處,可使用閉運算。若影像中的物件是深色、背景是亮色,請先判斷哪種極性應視為前景;套用相同的判斷方式前,可能需要先反轉影像。
了解前景極性與輸出結果
OpenCV 的基本膨脹會擴展像素值較大的區域。在亮色位於暗色背景上的二值影像中,白色前景會向附近的黑色背景擴展。在白紙上的深色文字影像中,亮色紙張是像素值較大的區域,因此膨脹會擴大紙張區域,可能讓深色文字看起來變細。這就是為什麼結果有時會和直覺上「擴大物件」的想法相反。
若要擴大深色文字本身,請先反轉灰階影像,再對反轉後的影像執行膨脹,最後將結果反轉回來。對彩色影像而言,每個色彩通道都會作為影像資料處理;若要處理的是形狀或標籤,而不是色彩強度,請使用單通道灰階或二值遮罩。
檢查常見的輸入與顯示問題
處理前請確認來源影像已成功載入,並確認核心尺寸適合影像中的細節。核心過寬或迭代次數過多,可能使不同元件合併,或移除狹窄間隙。如果輸出看起來沒有變化,請確認選定的前景是否比周圍區域亮,以及核心的鄰域是否包含與變化相關的附近像素。核心的尺寸和形狀會決定要合併哪些局部像素值,因此核心也能影響比核心本身更大的特徵。
OpenCV 會依 BGR 順序讀取彩色影像的通道,而 Matplotlib 會將三通道陣列解讀為 RGB。使用 matplotlib.pyplot.imshow 繪圖前,請用 cv2.cvtColor(image, cv2.COLOR_BGR2RGB) 轉換 BGR 影像;若使用灰階陣列,請傳入 cmap="gray",讓顯示結果使用灰階色盤。顯示顏色不一致會影響視覺呈現,不會影響膨脹運算本身。