邊緣式與區域式分割比較#

在此範例中,我們將了解如何從背景中分割物件。我們使用 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')
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()
coins > 100, coins > 150

邊緣式分割#

接下來,我們嘗試使用邊緣式分割來描繪硬幣的輪廓。為此,我們首先使用 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()
Canny detector

然後使用數學形態學填充這些輪廓。

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()
filling the holes

透過為有效物件設定最小大小,可以輕鬆移除小的雜散物件。

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()
removing small objects

但是,此方法不是很穩健,因為未完全閉合的輪廓無法正確填充,例如上面一個未填充的硬幣。

區域式分割#

因此,我們嘗試使用分水嶺轉換的區域式方法。首先,我們使用影像的 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()
elevation map

接下來,我們根據灰度值直方圖的極端部分尋找背景和硬幣的標記。

markers

最後,我們使用分水嶺轉換,從上面確定的標記開始填充高程圖的區域

segmentation

最後一種方法效果更好,並且可以單獨分割和標記硬幣。

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()
plot coins segmentation

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

由 Sphinx-Gallery 產生的圖庫