Pynote

Python、機械学習、画像処理について

OpenCV - ハフ変換 (Hough Transform) で円を検出する方法 (HoughCircles)

概要

OpenCV の関数 HoughCircles で画像から円を検出する方法について紹介する。

関連記事

pynote.hatenablog.com

HoughCircles

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

サンプルコード

入力画像

import cv2

img = cv2.imread("sample.jpg")

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

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

# 検出結果を描画する。
if circles is not None:
    circles = circles.squeeze(axis=0)  # (1, NumCircles, 3) -> (NumCircles, 3)
    for cx, cy, r in circles:
        # 円の円周を描画する。
        cv2.circle(img, (cx, cy), r, (0, 255, 0), 2)
        # 円の中心を描画する。
        cv2.circle(img, (cx, cy), 2, (0, 255, 0), 2)

# 結果を保存する。
cv2.imwrite("result.png", img)


ipywidget

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 houghline(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:
        circles = circles.squeeze(axis=0)
        for cx, cy, r in circles:
            # 円の円周を描画する。
            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("test.jpg")

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