注記
完全なサンプルコードをダウンロードするには、最後まで移動してください。 または、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')
閾値処理#
コインをセグメント化する簡単な方法は、グレースケール値のヒストグラムに基づいて閾値を選択することです。しかし、この画像に対して閾値処理を行うと、コインの重要な部分が欠落したり、背景の一部がコインとマージされたりする2値画像が生成されます。
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()

しかし、完全に閉じられていない輪郭は正しく塗りつぶされません(上の塗りつぶされていないコインのように)、この方法はそれほど堅牢ではありません。
領域ベースセグメンテーション#
そこで、watershed変換を用いた領域ベースの方法を試してみます。まず、画像の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()

最後に、watershed変換を使用して、上記で決定されたマーカーから始めて標高マップの領域を塗りつぶします。
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秒)