上一篇介紹了 Morphology。
經過 Erosion 和 Dilation 後,我們已經可以把二值化圖片中的雜訊去除,或是修補一些破損的區域。
不過,當圖片整理乾淨之後,又有另一個問題。
如果圖片裡有好幾個白色區域,電腦要怎麼知道它們分別代表幾個不同的物體?
例如一張細胞影像,電腦要知道有幾顆細胞;或是一張文件圖片,電腦要知道有幾個文字區塊。
對我們來說,這些物體一眼就能分辨。
但對電腦而言,它看到的仍然只是一張由許多 Pixel 組成的二值化圖片。
那它要怎麼判斷哪些 Pixel 屬於同一個物體?
這就是 Connected Component(連通元件分析) 要解決的問題。
Connected Component 的概念,就是把彼此相連的 Pixel 視為同一個物體,並替每個物體分配一個不同的 Label(標籤)。
舉例來說,如果圖片中有三個互不相連的白色區域,分析完成後,第一個區域可能被標記為 Label 1,第二個區域標記為 Label 2,第三個區域則標記為 Label 3。
之後,只要看到相同的 Label,電腦就知道這些 Pixel 屬於同一個物體。
看到這裡,問題來了:
如果兩個 Pixel 只有對角線接觸,也算是相連嗎?
其實,在影像處理中有兩種常見的定義:
4-Connected,只考慮上下左右四個方向。
8-Connected,除了上下左右之外,也把四個對角線算進來。
OpenCV 預設採用 8-Connected,因為它通常比較符合人眼對「連在一起」的直覺。
OpenCV 已經提供了 connectedComponents() 函式。
import cv2
import matplotlib.pyplot as plt
# 讀取圖片
img = cv2.imread("dog.jpeg", cv2.IMREAD_GRAYSCALE)
# 二值化
_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# Connected Component
num_labels, labels = cv2.connectedComponents(binary)
print(f"Number of labels: {num_labels}")
這個函式會回傳兩個值。
num_labels:圖片中總共有多少個 Label。labels:每個 Pixel 所屬的 Label。需要注意的是,背景也會被算作一個 Label。
因此,如果圖片中有三個物體,num_labels 的結果會是 4,其中 Label 0 代表背景。
labels 本身是一個矩陣,每個數字代表不同的物體。
我們可以利用 Matplotlib,把不同的 Label 顯示成不同顏色。
plt.figure(figsize=(10, 4))
plt.subplot(1, 2, 1)
plt.imshow(binary, cmap="gray")
plt.title("Binary")
plt.axis("off")
plt.subplot(1, 2, 2)
plt.imshow(labels, cmap="nipy_spectral")
plt.title("Connected Components")
plt.axis("off")
plt.tight_layout()
plt.show()
執行結果如下:

可以看到,每個物體都會以不同顏色顯示,因此很容易確認哪些 Pixel 被判定為同一個物體。
如果只是知道物體數量,其實還不夠。
OpenCV 另外提供了 connectedComponentsWithStats(),除了 Label 之外,還可以取得每個物體的統計資訊。
import cv2
img = cv2.imread("binary.png", cv2.IMREAD_GRAYSCALE)
_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(binary)
for label in range(1, num_labels):
x = stats[label, cv2.CC_STAT_LEFT]
y = stats[label, cv2.CC_STAT_TOP]
w = stats[label, cv2.CC_STAT_WIDTH]
h = stats[label, cv2.CC_STAT_HEIGHT]
area = stats[label, cv2.CC_STAT_AREA]
print(
f"Object {label}: "
f"Position=({x}, {y}), "
f"Size={w}×{h}, "
f"Area={area}"
)
透過這些資訊,我們可以知道每個物體的位置、大小以及面積。
例如,如果想把面積太小的區域視為雜訊,只要根據 area 進行篩選即可。
Connected Component 常出現在影像處理的最後幾個步驟。
例如:
可以發現,它是在物體已經分割完成後,進一步分析每一個物體。
今天介紹了 Connected Component。
它會把彼此相連的 Pixel 分配相同的 Label,讓電腦知道哪些 Pixel 屬於同一個物體。
搭配 connectedComponentsWithStats(),我們還可以取得每個物體的位置、大小、面積和中心點,方便進一步分析或篩選。
一路看到這裡,我們已經學會利用 Pixel 的亮度、鄰近關係以及形狀來分析圖片。
不過,這些方法都有一個共同點:
它們都是直接在圖片本身進行運算,也就是所謂的空間域(Spatial Domain)。
那如果換個角度,不再觀察每一個 Pixel,而是分析整張圖片包含哪些不同頻率的資訊,會發生什麼事?
下一篇,我們就來介紹 Fourier Transform(傅立葉轉換),看看如何把圖片從空間域轉換到另一個完全不同的世界。