注意
前往結尾以下載完整的範例程式碼。或透過 Binder 在您的瀏覽器中執行此範例
邊緣式與區域式分割比較#
在此範例中,我們將了解如何從背景中分割物件。我們使用 skimage.data
中的 coins
影像,該影像顯示多個硬幣在較暗的背景下勾勒出輪廓。
import numpy as np
import matplotlib.pyplot as plt
from skimage import data
from skimage.exposure import histogram
coins = data.coins()
hist, hist_centers = histogram(coins)
fig, axes = plt.subplots(1, 2, figsize=(8, 3))
axes[0].imshow(coins, cmap=plt.cm.gray)
axes[0].set_axis_off()
axes[1].plot(hist_centers, hist, lw=2)
axes[1].set_title('histogram of gray values')

Text(0.5, 1.0, 'histogram of gray values')
閾值處理#
分割硬幣的一個簡單方法是根據灰度值的直方圖選擇閾值。不幸的是,對此影像進行閾值處理會產生一個二元影像,該影像不是遺漏了硬幣的顯著部分,就是將背景的部分與硬幣合併在一起
fig, axes = plt.subplots(1, 2, figsize=(8, 3), sharey=True)
axes[0].imshow(coins > 100, cmap=plt.cm.gray)
axes[0].set_title('coins > 100')
axes[1].imshow(coins > 150, cmap=plt.cm.gray)
axes[1].set_title('coins > 150')
for a in axes:
a.set_axis_off()
fig.tight_layout()

邊緣式分割#
接下來,我們嘗試使用邊緣式分割來描繪硬幣的輪廓。為此,我們首先使用 Canny 邊緣偵測器取得特徵的邊緣。
from skimage.feature import canny
edges = canny(coins)
fig, ax = plt.subplots(figsize=(4, 3))
ax.imshow(edges, cmap=plt.cm.gray)
ax.set_title('Canny detector')
ax.set_axis_off()

然後使用數學形態學填充這些輪廓。
from scipy import ndimage as ndi
fill_coins = ndi.binary_fill_holes(edges)
fig, ax = plt.subplots(figsize=(4, 3))
ax.imshow(fill_coins, cmap=plt.cm.gray)
ax.set_title('filling the holes')
ax.set_axis_off()

透過為有效物件設定最小大小,可以輕鬆移除小的雜散物件。
from skimage import morphology
coins_cleaned = morphology.remove_small_objects(fill_coins, 21)
fig, ax = plt.subplots(figsize=(4, 3))
ax.imshow(coins_cleaned, cmap=plt.cm.gray)
ax.set_title('removing small objects')
ax.set_axis_off()

但是,此方法不是很穩健,因為未完全閉合的輪廓無法正確填充,例如上面一個未填充的硬幣。
區域式分割#
因此,我們嘗試使用分水嶺轉換的區域式方法。首先,我們使用影像的 Sobel 梯度尋找高程圖。
from skimage.filters import sobel
elevation_map = sobel(coins)
fig, ax = plt.subplots(figsize=(4, 3))
ax.imshow(elevation_map, cmap=plt.cm.gray)
ax.set_title('elevation map')
ax.set_axis_off()

接下來,我們根據灰度值直方圖的極端部分尋找背景和硬幣的標記。
markers = np.zeros_like(coins)
markers[coins < 30] = 1
markers[coins > 150] = 2
fig, ax = plt.subplots(figsize=(4, 3))
ax.imshow(markers, cmap=plt.cm.nipy_spectral)
ax.set_title('markers')
ax.set_axis_off()

最後,我們使用分水嶺轉換,從上面確定的標記開始填充高程圖的區域
from skimage import segmentation
segmentation_coins = segmentation.watershed(elevation_map, markers)
fig, ax = plt.subplots(figsize=(4, 3))
ax.imshow(segmentation_coins, cmap=plt.cm.gray)
ax.set_title('segmentation')
ax.set_axis_off()

最後一種方法效果更好,並且可以單獨分割和標記硬幣。
from skimage.color import label2rgb
segmentation_coins = ndi.binary_fill_holes(segmentation_coins - 1)
labeled_coins, _ = ndi.label(segmentation_coins)
image_label_overlay = label2rgb(labeled_coins, image=coins, bg_label=0)
fig, axes = plt.subplots(1, 2, figsize=(8, 3), sharey=True)
axes[0].imshow(coins, cmap=plt.cm.gray)
axes[0].contour(segmentation_coins, [0.5], linewidths=1.2, colors='y')
axes[1].imshow(image_label_overlay)
for a in axes:
a.set_axis_off()
fig.tight_layout()
plt.show()

腳本的總執行時間:(0 分鐘 2.096 秒)