Loading [MathJax]/jax/output/HTML-CSS/config.js

[scikit-image] 72. バイナリ画像で対象物の輪郭を検出して表示(skimage.measure find_contours)

python

はじめに

skimage.measureのfind_contoursで、バイナリ画像上で対象物の輪郭を検出して画像中に表示する方法について説明する。

コード

import numpy as np
import matplotlib.pyplot as plt
from scipy import ndimage
from skimage import measure
from skimage.segmentation import clear_border
from skimage.filters import threshold_otsu
plt.rcParams['font.size']=12
plt.rcParams['font.family'] = 'sans-serif'
#make data
n = 6
s = 256
im = np.zeros((s, s))
points = (s*np.random.random((2, n**2))).astype(int)
im[points[0], points[1]] = 1
im = ndimage.gaussian_filter(im, sigma=s/(5.*n))
thresh = threshold_otsu(im)
im = im >thresh
im = clear_border(im)
contours = measure.find_contours(im, 0.5)
fig, ax = plt.subplots(figsize=(6,6))
for n, contour in enumerate(contours):
ax.plot(contour[:, 1], contour[:, 0], linewidth=4)
ax.text(contour[0, 1], contour[0, 0],n)
ax.imshow(im, cmap=plt.cm.cividis, interpolation='gaussian',origin='lower',alpha=0.5)
plt.savefig('findcontours05.png',dpi=100)
plt.show()

解説

モジュールのインポートなど

import numpy as np
import matplotlib.pyplot as plt
from scipy import ndimage
from skimage import measure
from skimage.segmentation import clear_border
from skimage.filters import threshold_otsu
plt.rcParams['font.size']=12
plt.rcParams['font.family'] = 'sans-serif'

画像の生成

#make data
n = 6
s = 256
im = np.zeros((s, s))
points = (s*np.random.random((2, n**2))).astype(int)
im[points[0], points[1]] = 1
im = ndimage.gaussian_filter(im, sigma=s/(5.*n))

最初にnp.zeros((s, s))で256 x 256のデータを生成する。
次に、(s*np.random.random((2, n**2))).astype(int)で0〜255のランダムな整数を(2,36)の形状で生成する。そして、imの[points[0], points[1]]に該当する部分の要素を1として、ndimage.gaussian_filterでぼかすことで画像データを生成した。

thresh = threshold_otsu(im)
im = im >thresh
im = clear_border(im)

threshold_otsuで大津の方法によるしきい値を取得する。
このしきい値で画像を2値化し、境界に接している部分をclear_borderで除去する。

輪郭を検出

contours = measure.find_contours(im, 0.5)

find_contoursで輪郭を検出する。
バイナリ画像の場合、検出に用いる値は0と1の中間の0.5とする。

結果の表示

fig, ax = plt.subplots(figsize=(6,6))
for n, contour in enumerate(contours):
ax.plot(contour[:, 1], contour[:, 0], linewidth=4)
ax.text(contour[0, 1], contour[0, 0],n)
ax.imshow(im, cmap=plt.cm.cividis, interpolation='gaussian',origin='lower',alpha=0.5)
plt.savefig('findcontours05.png',dpi=100)
plt.show()

figを作成した後に、coutoursの中に格納されている輪郭データをenumerateを用いて一つずつプロットしていく。データの開始点の座標にax.text()でインデックスを表示する。
ax.imshowで画像を表示すれば、輪郭に線を描写した画像が得られる。

find_contoursの値を変えたときの変化

contours0 = measure.find_contours(im, 0)
contours05 = measure.find_contours(im, 0.5)
contours1 = measure.find_contours(im, 0.99)
fig, ax = plt.subplots(ncols=3,figsize=(9,3))
ax=ax.ravel()
for n, contour in enumerate(contours0):
ax[0].plot(contour[:, 1], contour[:, 0], linewidth=1)
for n, contour in enumerate(contours05):
ax[1].plot(contour[:, 1], contour[:, 0], linewidth=1)
for n, contour in enumerate(contours1):
ax[2].plot(contour[:, 1], contour[:, 0], linewidth=1)
title = ['find_contours=0','find_contours=0.5','find_contours=0.99']
for i in range(3):
ax[i].imshow(im, cmap=plt.cm.cividis, interpolation='gaussian',origin='lower',alpha=0.5)
ax[i].axis('off')
ax[i].set_title(title[i])
plt.savefig('findcontours.png',dpi=100)
plt.show()

左の図から順にfind_contoursの値を0,0.5,0.99としたときの結果を示した。見た目の変化は見られないが、輪郭データの配列の形状を比較すると以下のようになる。

contours0[2].shape,contours05[2].shape,contours1[2].shape
#((61, 2), (85, 2), (85, 2))

0でfind_contours したcontours0のみ要素数が少ないことがわかる。
さらに0.99と0.5でfind_contoursしたcontours1とcontours05の差を取ると以下のようになる。値が僅かに異なっていることがわかる。

contours1[2]-contours05[2]
'''
array([[-0.49, 0. ],
[-0.49, 0. ],
[-0.49, 0. ],
[-0.49, 0. ],
[-0.49, 0. ],
[ 0. , 0.49],
[-0.49, 0. ],
[-0.49, 0. ],
[-0.49, 0. ],
[ 0. , 0.49],
[-0.49, 0. ],
[ 0. , 0.49],
[-0.49, 0. ],
[ 0. , 0.49],
[-0.49, 0. ],
[ 0. , 0.49],
[-0.49, 0. ],
[ 0. , 0.49],
[ 0. , 0.49],
[ 0. , 0.49],
[-0.49, 0. ],
[ 0. , 0.49],
[ 0. , 0.49],
[ 0. , 0.49],
[ 0. , 0.49],
[ 0. , 0.49],
[ 0.49, 0. ],
[ 0. , 0.49],
[ 0. , 0.49],
[ 0. , 0.49],
[ 0.49, 0. ],
[ 0. , 0.49],
[ 0.49, 0. ],
[ 0. , 0.49],
[ 0.49, 0. ],
[ 0. , 0.49],
[ 0.49, 0. ],
[ 0. , 0.49],
[ 0.49, 0. ],
[ 0.49, 0. ],
[ 0.49, 0. ],
[ 0. , 0.49],
[ 0.49, 0. ],
[ 0.49, 0. ],
[ 0.49, 0. ],
[ 0.49, 0. ],
[ 0.49, 0. ],
[ 0. , -0.49],
[ 0.49, 0. ],
[ 0.49, 0. ],
[ 0.49, 0. ],
[ 0. , -0.49],
[ 0.49, 0. ],
[ 0. , -0.49],
[ 0.49, 0. ],
[ 0. , -0.49],
[ 0.49, 0. ],
[ 0. , -0.49],
[ 0.49, 0. ],
[ 0. , -0.49],
[ 0. , -0.49],
[ 0. , -0.49],
[ 0.49, 0. ],
[ 0. , -0.49],
[ 0. , -0.49],
[ 0. , -0.49],
[ 0. , -0.49],
[ 0. , -0.49],
[-0.49, 0. ],
[ 0. , -0.49],
[ 0. , -0.49],
[ 0. , -0.49],
[-0.49, 0. ],
[ 0. , -0.49],
[-0.49, 0. ],
[ 0. , -0.49],
[-0.49, 0. ],
[ 0. , -0.49],
[-0.49, 0. ],
[ 0. , -0.49],
[-0.49, 0. ],
[-0.49, 0. ],
[-0.49, 0. ],
[ 0. , -0.49],
[-0.49, 0. ]])
'''
コードをダウンロード(.pyファイル)

コードをダウンロード(.ipynbファイル)

参考

skimage.measure — skimage 0.26.0rc0.dev0 documentation

コメント