Loading [MathJax]/extensions/tex2jax.js

[scikit-image] 22. ハフ変換による楕円の検出(transform.hough_ellipse)

python

はじめに

ここでは、skimage.transform の hough_ellipseを用いた楕円の検出について説明する。

コード

import matplotlib.pyplot as plt
from skimage import data, color, img_as_ubyte
from skimage.feature import canny
from skimage.transform import hough_ellipse
from skimage.draw import ellipse_perimeter
from skimage.color import rgb2gray,rgba2rgb
# Load picture, convert to grayscale and detect edges
image_rgb = plt.imread('https://1.bp.blogspot.com/-jp5SUi6-w9A/UZMszsKm4cI/AAAAAAAASHw/fL-ySyFKg8w/s800/fabric_mark_ellipse.png')
image_gray = rgb2gray(rgba2rgb(image_rgb))
edges = canny(image_gray,sigma=1.0,low_threshold=0.1, high_threshold=0.8)
fig, ax = plt.subplots(dpi=140)
ax.imshow(edges, cmap=plt.cm.gray)
plt.savefig('fabric_mark_ellipse_edge.jpg',dpi=130)
# Perform a Hough Transform
# The accuracy corresponds to the bin size of a major axis.
# The value is chosen in order to get a single high accumulator.
# The threshold eliminates low accumulators
result = hough_ellipse(edges, accuracy=4, threshold=1,
min_size=300, max_size=240)
result.sort(order='accumulator')
result
array([(2, 154. , 357. , 300.28153456, 58.78775383, 1.61410257),
(2, 154. , 358. , 300.37476592, 54.69917732, 1.62075472),
(2, 154.5, 351. , 300.0337481 , 100. , 1.5857952 ), ...,
(9, 242. , 348. , 214.40149253, 328.07468662, 3.12025443),
(9, 242.5, 348.5, 217.14511277, 327.61028677, 3.11564428),
(9, 244. , 348. , 327. , 220.68982759, 1.57079633)],
dtype=[('accumulator', '<i8'), ('yc', '<f8'), ('xc', '<f8'), ('a', '<f8'), ('b', '<f8'), ('orientation', '<f8')])
# Estimated parameters for the ellipse
best = list(result[-1])
yc, xc, a, b = [int(round(x)) for x in best[1:5]]
orientation = best[5]
# Draw the ellipse on the original image
cy, cx = ellipse_perimeter(yc, xc, a, b, orientation)
# Draw the edge (white) and the resulting ellipse (red)
edges = color.gray2rgb(img_as_ubyte(edges))
edges[cy, cx] = (250, 0, 0)
fig2, (ax1, ax2) = plt.subplots(ncols=2, nrows=1, figsize=(8, 4),
sharex=True, sharey=True,dpi=150)
ax1.set_title('Original picture')
ax1.imshow(image_rgb)
ax2.set_title('Edge (white) and result (red)')
ax2.imshow(edges)
plt.savefig("daen_hough.jpg",dpi=100)
plt.show()

解説

モジュールのインポート

import matplotlib.pyplot as plt
from skimage import data, color, img_as_ubyte
from skimage.feature import canny
from skimage.transform import hough_ellipse
from skimage.draw import ellipse_perimeter
from skimage.color import rgb2gray,rgba2rgb

バージョン

#version
import matplotlib
print(matplotlib.__version__)
3.3.4
import skimage
print(skimage.__version__)
0.18.1

画像データの読み込み

image_rgb = plt.imread('https://1.bp.blogspot.com/-jp5SUi6-w9A/UZMszsKm4cI/AAAAAAAASHw/fL-ySyFKg8w/s800/fabric_mark_ellipse.png')
image_gray = rgb2gray(rgba2rgb(image_rgb))

rgb2grayでグレースケールに変換する。画像は下記サイトの楕円を採用した。

布のマークのイラスト「矢印・丸・クローバー・スペード・楕円・花・ハート・四角・スペード・星・三角」
いらすとやは季節のイベント・動物・子供などのかわいいイラストが沢山見つかるフリー素材サイトです。

キャニー法によるエッジの検出と表示

edges = canny(image_gray,sigma=1.0,low_threshold=0.1, high_threshold=0.8)
fig, ax = plt.subplots(dpi=140)
ax.imshow(edges, cmap=plt.cm.gray)
plt.savefig('fabric_mark_ellipse_edge.jpg',dpi=130)

canny法でエッジを検出し、ax.imshow(edges, cmap=plt.cm.gray)で表示する。

canny法については、下記で解説した。

[scikit-image] 12. Canny法によるエッジ検出 (feature.canny)
skimage.feature の canny を用いた、Canny法によるエッジ検出

ハフ変換による楕円の検出

result = hough_ellipse(edges, accuracy=4, threshold=1,
min_size=300, max_size=240)
result.sort(order='accumulator')
result
array([(2, 154. , 357. , 300.28153456, 58.78775383, 1.61410257),
(2, 154. , 358. , 300.37476592, 54.69917732, 1.62075472),
(2, 154.5, 351. , 300.0337481 , 100. , 1.5857952 ), ...,
(9, 242. , 348. , 214.40149253, 328.07468662, 3.12025443),
(9, 242.5, 348.5, 217.14511277, 327.61028677, 3.11564428),
(9, 244. , 348. , 327. , 220.68982759, 1.57079633)],
dtype=[('accumulator', '<i8'), ('yc', '<f8'), ('xc', '<f8'), ('a', '<f8'), ('b', '<f8'), ('orientation', '<f8')])

hough_ellipse()により、ハフ変換による楕円の検出ができる。

edgesはハフ変換したい画像とする。
accuracyは、精度のことで、短軸方向のaccumulatorのビンサイズとなる。
thresholdはaccumulatorのしきい値で、min_sizeは、楕円の長い方の半径の最小値、max_sizeは楕円の短い方の半径の最大値となっている。
resultをaccumulatorの順にソートして表示している。
結果は、 (‘accumulator’,’yc’, ‘xc’, ‘a’, ‘b’, ‘orientation’)で返ってきて、(yc, xc)は中心の座標、 (a, b)はそれぞれ長軸と短軸のことである。orientationは楕円の向きを示す。

より誤差が少なく近似されている楕円の選出

best = list(result[-1])
yc, xc, a, b = [int(round(x)) for x in best[1:5]]
orientation = best[5]

accumulatorの順にソートしたので最後列のデータが最も誤差が少なく近似されている楕円だと思われる。
結果をリスト形式として、それぞれのデータをスライスで取り出す。

検出した楕円の表示のための準備

# Draw the ellipse on the original image
cy, cx = ellipse_perimeter(yc, xc, a, b, orientation)
# Draw the edge (white) and the resulting ellipse (red)
edges = color.gray2rgb(img_as_ubyte(edges))
edges[cy, cx] = (250, 0, 0)

ellipse_perimeter()により画像中に楕円を生成する。座標と各方向の半径、方向を設定することで楕円を生成できる。
生成した楕円に色をつけるために、gray2rgb(img_as_ubyte)により [0〜255]で色を指定できるRGB形式に変換する。
edges[cy, cx] = (250, 0, 0)により、楕円部分のみを赤色にすることができる。

図の表示

fig2, (ax1, ax2) = plt.subplots(ncols=2, nrows=1, figsize=(8, 4),
sharex=True, sharey=True,dpi=150)
ax1.set_title('Original picture')
ax1.imshow(image_rgb)
ax2.set_title('Edge (white) and result (red)')
ax2.imshow(edges)
plt.savefig("daen_hough.jpg",dpi=100)
plt.show()
コードをダウンロード(.pyファイル)

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

参考

Circular and Elliptical Hough Transforms — skimage 0.25.2 documentation
skimage.draw — skimage 0.26.0rc0.dev0 documentation

コメント