【Python・OpenCV】画像内の円を検出(cv2.HoughCircles)

※当サイトではアフィリエイト広告を利用しています

Python プログラミング 画像処理

【Python・OpenCV】画像内の円を検出(cv2.HoughCircles)

はじめに

この記事では画像の中に隠された円を見つけ出す方法について解説します。

工場製品の不良検出や、医療画像の解析など、様々な分野で「画像内の円を検出したい!」という場面があるかもしれません。
OpenCVのcv2.HoughCircles関数は簡単な設定で円を検出することができます。

cv2.HoughCircles関数の基本的な原理から、実際の使い方、引数調整のコツまでを詳しく解説します。

(広告) OpenCV関連書籍をAmazonで探す

基本原理

cv2.HoughCircles関数は、「Hough(ハフ)変換」という数学的手法を応用して、画像中の円を検出します。
画像内のエッジ情報を基に、円の中心座標と半径を検出するアルゴリズムです。
以下にその基本的な流れを簡単にまとめます。

  1. エッジ検出
    はじめに、画像からエッジを検出します(通常、Cannyエッジ検出器が使われます)。
  2. 投票処理
    画像上の各エッジ点に対して、円の中心となり得る点に「投票」します。
  3. 円の特定
    投票数がしきい値を超えた点を円の中心とし、そこからの距離が一定の点群から円の半径を決定します

この手法により、画像中の円を効率的に特定できます。

利用例

cv2.HoughCircles関数は以下のような場面で活用されています:

  • スポーツ分析(ボールの追跡)
  • 製造業での部品検査(円形部品の位置決めや欠陥検査)
  • 医療画像診断(細胞や病変の検出)
  • 顔認識(目や鼻などの円形の器官を検出)

cv2.HoughCircles関数

cv2.HoughCircles関数の引数と戻り値について説明します。

cv2.HoughCircles(入力画像, method, dp, minDist[, circles[, param1[, param2[, minRadius[, maxRadius]]]]] )

引数

名称説明
入力画像(必須)・入力画像。8ビット単一チャンネルの画像である必要があります。
numpy.ndarrayオブジェクト
method(必須)HoughModes を参照してください。使用できるのはcv2.HOUGH_GRADIENTcv2.HOUGH_GRADIENT_ALTです。
・int型
dp(必須・入力画像に対する蓄積面の解像度の逆比
cv2.HOUGH_GRADIENT_ALTの時、dp=1 の場合、アキュムレータの解像度は入力画像と同じです。 dp=2 の場合、アキュムレータの幅と高さは半分になります。cv2.HOUGH_GRADIENT_ALTの場合、非常に小さな円を検出する必要がない限り、推奨値は dp=1.5 です。
・大きな値:処理は速くなるが精度は低下、小さな値:精度は向上するが処理時間が増加
・float型
minDist(必須)・検出された円の中心間の最小距離(単位:ピクセル)
・パラメータが小さすぎると、実際の円に加えて複数の隣接する円が誤って検出される可能性あり。パラメータが大きすぎると、一部の円が検出できない可能性あり。
・推奨値は検出したい円の直径の0.5〜1.0倍
・float型
circles(オプション)・検出された円が格納されます。
numpy.ndarrayオブジェクト
param1(オプション)・最初のmethod固有のパラメータ
・Cannyエッジ検出器の高い方のしきい値で、低い方のしきい値は自動的にparam1の半分に設定
cv2.HOUGH_GRADIENT_ALTの時、しきい値は通常は 300 くらいの高い値となる。
・デフォルト:100
・奨値は検出したい円の直径の0.5〜1.0倍
・int型
param2(オプション)・2番目のmethod固有のパラメータ
・円の中心を検出する際の閾値
cv2.HOUGH_GRADIENT_ALTの場合、param2は必須の引数となります。
・値が小さいほど誤検出が増加、値が大きいほど検出される円は減少
・デフォルト:100
・int型
minRadius(オプション)・検出する円の最小半径(単位:ピクセル)
・予め円のおおよその大きさが分かっている場合に設定
・処理時間の短縮に効果的
・デフォルト:0
・int型
maxRadius(オプション)・検出する円の最大半径(単位:ピクセル)
・予め円のおおよその大きさが分かっている場合に設定
・"0"の場合は画像の寸法から自動計算
・大きすぎる値は処理時間の増加につながる
・デフォルト:0
・int型

methodに指定できるのはcv2.HOUGH_GRADIENTcv2.HOUGH_GRADIENT_ALTです。
これらの違いと使い分けのポイントを以下に示します。

cv2.HOUGH_GRADIENT・OpenCV 3.x以降に導入
・標準的なハフ勾配検出アルゴリズム
・計算速度が速い
・比較的単純な円検出
・ノイズに対してやや敏感
・向いているケース:きれいな画像、コントラストが明確な円、リアルタイム処理が必要な場合など
cv2.HOUGH_GRADIENT_ALT・OpenCV 4.5.1以降に導入
・従来法の精度と検出能力を改善
・計算速度はcv2.HOUGH_GRADIENTより遅い
・より高精度な円検出
・ノイズに対するロバスト性が向上
・複雑な背景でも検出性能が高い
・向いているケース:ノイズの多い画像、コントラストが低い画像、高精度な検出が必要な場合など

画像の特性に応じて最適な手法の選択が必要ですが、どちらの手法も完璧でないということを理解しておいてください。

戻り値

データ型はnumpy.ndarrayで、検出された円ごとに3つの値がセットになった配列を返します。

[[[x1, y1, r1],
  [x2, y2, r2],
  ...
  [xn, yn, rn]]]

x:円の中心のx座標
y:円の中心のy座標
r:円の半径

ポイント

  • すべての値は浮動小数点数
  • 円が検出されなかった場合はNone

使い方

サンプルコードと入力画像は以下の通りです。

円形のコインを検出するサンプルコードがこちら。

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 画像の読み込み
image = cv2.imread("image1.jpg")
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# Matplotlibで表示するためにRGBの並びに変更する
rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# ノイズ除去のためにぼかしを適用
blurred = cv2.GaussianBlur(gray_image, (9, 9), 2)

# 円の検出
circles = cv2.HoughCircles(
    blurred,
    cv2.HOUGH_GRADIENT,
    dp=1,
    minDist=150
)

# 検出された円の描画
if circles is not None:
    circles = np.round(circles[0, :]).astype("int")
    for (x, y, r) in circles:
        cv2.circle(image, (x, y), r, (0, 255, 0), 2)
        cv2.circle(image, (x, y), 2, (0, 0, 255), 3)

# Matplotlibで表示するためにRGBの並びに変更する
rgb_detected_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# 結果を表示
plt.rcParams["figure.figsize"] = [7,5]                                  # ウィンドウサイズを設定
title = "cv2.HoughCircles: codevace.com"
plt.figure(title)                                                       # ウィンドウタイトルを設定
plt.subplots_adjust(left=0.05, right=0.95, bottom=0.03, top=0.95)       # 余白を設定

plt.subplot(121)                                                        # 1行2列の1番目の領域にプロットを設定
plt.imshow(rgb_image)                                                   # 入力画像を表示
plt.title("Original Image")                                             # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す

plt.subplot(122)                                                        # 1行2列の2番目の領域にプロットを設定
plt.imshow(rgb_detected_image)                                          # ノイズ除去の結果
plt.title("Detected Circles Image")                                     # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す

plt.show()

実行結果は次のようになります。
円を緑、円の中心を赤い点で描画しています。

円の描画については、円を描画するcv2.circle関数について解説した記事が参考になります。

引数はds=1、minDist=150を設定しました。
コインが隣接しているので、写真からおおよそのコインの直径を読み取り、少し小さめの値としてminDist=150を設定しています。
期待通りに円を検出できました。

次に、別の入力画像を試してみます。

同じように、写真のサイズからおおよそのパイプ断面の大きさを推測してminDistを決定し、引数はds=1、minDist=20を設定しました。
最初の画像と同じ考え方でminDistを決定しましたが、検出率はよくありませんでした。
全く存在しない円をいくつも誤検出しています。

試行錯誤の結果、良い結果を得ることができました。
上のサンプルコードのcv2.HoughCircles関数の部分のみ、次の値を引数として設定しました。

circles = cv2.HoughCircles(
    blurred,
    cv2.HOUGH_GRADIENT_ALT,
    dp=1.5,
    minDist=20,
    param2=0.9
)

結果がこちら。

実際にcv2.HoughCircles関数を使ってみるとわかりますが、引数のチューニングが難しくなかなか期待する結果を得るのが難しい関数だと感じます。

引数決定ガイド

引数決定のポイントを以下にまとめました。
筆者の経験的な目安であり、必ずうまくいくとは限らないことをご承知おきいただければと思います。

cv2.HoughCircles関数の引数決定の目安

dp

  • 1〜1.5から大きく変えない。

minDist

  • 小さすぎると複数の円が同じ場所で検出される
  • 大きすぎると検出漏れ増加
  • 推奨値は検出したい円の直径の0.5〜1.0倍

param1

  • 小さすぎるとノイズを拾いやすい
  • 推奨値は50〜100

param2

  • 小さすぎると誤検出が増加
  • 大きすぎると検出漏れ増加
  • 推奨値はcv2.HOUGH_GRADIENTの場合 25〜30cv2.HOUGH_GRADIENT_ALTの場合 0.9前後

minRadius, maxRadius

  • 円を見つけられない時に円のサイズが分かっている場合は、これらを指定してを関数を補助できます。
  • cv2.HOUGH_GRADIENTの場合はmaxRadius を負の数に設定して半径検索を行わずに中心のみを返し、追加の手順を使用して正しい半径を見つけるということもできます。

上記はあくまで目安です。画像の条件によって、これらに当てはまらない場合があります。

以下のポイントにも注意を払ってください。

  1. 前処理の重要性
    • ガウシアンブラーなどでノイズを除去
    • コントラスト調整を行い、円のエッジを強調
  2. 画像の特性に応じた調整
    • 明るさやコントラストの違いに注意
    • 円のサイズに応じてminRadius、maxRadiusを調整
  3. 処理速度とのバランス
    • パラメータによって処理時間が大きく変化
    • リアルタイム処理の場合は特に注意

おわりに

cv2.HoughCircles関数は、適切なパラメータ設定と前処理を行うことで、非常に強力な円検出ツールとなります。
本記事で解説した原理とチューニングのコツを参考に、アプリケーションに最適な円検出を実装してみてください。

また、引数の調整は試行錯誤が必要ですが、それは同時に画像処理の深い理解につながる良い機会でもあります。ぜひ、実際の画像で実験を重ねながら、最適な設定を見つけ出してください。

ご質問や取り上げて欲しい内容などがありましたら、コメントをお願いします。
最後までご覧いただきありがとうございました。

■(広告)OpenCVの参考書としてどうぞ!■

参考リンク

-Python, プログラミング, 画像処理
-