OpenCV – cv2.HoughCircles で円を検出する方法

目次

概要

OpenCV の cv2.HoughCircles で円を検出する方法について紹介します。

cv2.HoughCircles

circles = cv2.HoughCircles(image, method, dp, minDist[, circles[, param1[, param2[, minRadius[, maxRadius]]]]])

公式リファレンス: cv2.HoughCircles

引数
名前 デフォルト値
image ndarray
入力画像 (1チャンネル)
method HoughModes
ハフ変換の手法。現在、選択できる手法は cv2.HoughModes のみである。
dp float
投票器の解像度
minDist float
検出される円同士が最低限離れていなければならない距離。同じ円に対して重複して検出されるのを防ぐ役割がある。
param1 float
Canny 法のヒステリシス処理の上限。ヒステリシス処理の下限はこの値の半分に設定される。
param2 float
円の中心を検出する際の閾値。低い値にすると、円の誤検出が増え、高い値にすると未検出が増える可能性がある。
minRadius int
検出する円の半径の下限を $[0, maxRadius]$ の範囲で指定する。
maxRadius int
検出する円の半径の上限を $minRadius$ 以上の値で指定する。
返り値
名前 説明
circles 検出された円の一覧。各要素は (中心の $x$ 座標, 中心の $y$ 座標, 半径) のタプル。

サンプルコード

sample.jpg

cv2.HoughLine() と異なり、cv2.HoughCircles() 内で Canny 法も行うため、グレースケール画像を入力とします。

In [1]:
import cv2
from IPython.display import Image, display


def imshow(img):
    """ndarray 配列をインラインで Notebook 上に表示する。"""
    ret, encoded = cv2.imencode(".jpg", img)
    display(Image(encoded))
In [2]:
import cv2
import numpy as np


# 画像を読み込む。
img = cv2.imread("sample.jpg")

# グレースケールに変換する。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# ハフ変換で円検出する。
circles = cv2.HoughCircles(
    gray, cv2.HOUGH_GRADIENT, dp=1.0, minDist=10, param1=80, param2=100
)
circles = circles.squeeze(axis=1)

検出された円の一覧を形状が (NumCircles, 1, 3) の ndarray で返します。 各要素は検出された円の中心 $(x, y)$ 及び円の半径 $r$ を表します。

In [3]:
# 検出結果を描画する。
if circles is not None:
    for cx, cy, r in circles.astype(int):
        # 円の円周を描画する。
        cv2.circle(img, (cx, cy), r, (0, 255, 0), 2)
        # 円の中心を描画する。
        cv2.circle(img, (cx, cy), 2, (0, 255, 0), 2)
imshow(img)

ipywidgets でパラメータ調整する

ハフ変換はパラメータ調整が必須です。ipywidgets を使って GUI 上でパラメータ調整を行う方法について記載します。

In [4]:
import cv2
from IPython.display import Image, display
from ipywidgets import widgets


def imshow(img):
    """画像を Notebook 上に表示する。"""
    ret, encoded = cv2.imencode(".png", img)
    display(Image(encoded))


def hough_circle(img, dp, minDist, param1, param2, radius):
    """ハフ変換で円検出を行い、結果を表示する。"""
    minRadius, maxRadius = radius

    # グレースケールに変換する。
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # ハフ変換で円検出する。
    circles = cv2.HoughCircles(
        gray,
        cv2.HOUGH_GRADIENT,
        dp=dp,
        minDist=minDist,
        param1=param1,
        param2=param2,
        minRadius=minRadius,
        maxRadius=maxRadius,
    )

    # 検出した円を描画する。
    copied = img.copy()

    if circles is not None:
        for cx, cy, r in circles.squeeze(axis=0).astype(int):
            # 円の円周を描画する。
            cv2.circle(copied, (cx, cy), r, (0, 255, 0), 2)
            # 円の中心を描画する。
            cv2.circle(copied, (cx, cy), 2, (0, 255, 0), 2)

    imshow(copied)


# パラメータ「dp」を設定するスライダー
dp_slider = widgets.FloatSlider(
    min=0.1, max=10.0, step=0.1, value=1.0, description="dp: "
)
dp_slider.layout.width = "400px"

# パラメータ「minDist」を設定するスライダー
min_dist_slider = widgets.IntSlider(
    min=1, max=500, step=1, value=10, description="minDist: "
)
min_dist_slider.layout.width = "400px"

# パラメータ「param1」を設定するスライダー
param1_slider = widgets.IntSlider(
    min=1, max=300, value=80, step=1, description="param1:"
)
param1_slider.layout.width = "400px"

# パラメータ「param2」を設定するスライダー
param2_slider = widgets.IntSlider(
    min=1, max=300, value=100, step=1, description="param2:"
)
param2_slider.layout.width = "400px"

# パラメータ「radius」を設定するスライダー
radius_slider = widgets.IntRangeSlider(
    min=0, max=500, value=[0, 500], step=1, description="radius:"
)
radius_slider.layout.width = "400px"

# 画像を読み込む。
img = cv2.imread("sample.jpg")

# ウィジェットを表示する。
widgets.interactive(
    hough_circle,
    img=widgets.fixed(img),
    dp=dp_slider,
    minDist=min_dist_slider,
    param1=param1_slider,
    param2=param2_slider,
    radius=radius_slider,
)

コメント

コメントする

目次