はじめに
skimage.measureのfind_contoursで、バイナリ画像上で対象物の輪郭を検出して画像中に表示する方法について説明する。
コード
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()

解説
モジュールのインポートなど
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'
画像の生成
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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でぼかすことで画像データを生成した。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
thresh = threshold_otsu(im)
im = im >thresh
im = clear_border(im)
threshold_otsuで大津の方法によるしきい値を取得する。
このしきい値で画像を2値化し、境界に接している部分をclear_borderで除去する。
輪郭を検出
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
contours = measure.find_contours(im, 0.5)
find_contoursで輪郭を検出する。
バイナリ画像の場合、検出に用いる値は0と1の中間の0.5とする。
結果の表示
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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の値を変えたときの変化
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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としたときの結果を示した。見た目の変化は見られないが、輪郭データの配列の形状を比較すると以下のようになる。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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の差を取ると以下のようになる。値が僅かに異なっていることがわかる。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
コメント