はじめに
この記事では画像の中に隠された円を見つけ出す方法について解説します。
工場製品の不良検出や、医療画像の解析など、様々な分野で「画像内の円を検出したい!」という場面があるかもしれません。
OpenCVのcv2.HoughCircles
関数は簡単な設定で円を検出することができます。
cv2.HoughCircles
関数の基本的な原理から、実際の使い方、引数調整のコツまでを詳しく解説します。
基本原理
cv2.HoughCircles
関数は、「Hough(ハフ)変換」という数学的手法を応用して、画像中の円を検出します。
画像内のエッジ情報を基に、円の中心座標と半径を検出するアルゴリズムです。
以下にその基本的な流れを簡単にまとめます。
- エッジ検出
はじめに、画像からエッジを検出します(通常、Cannyエッジ検出器が使われます)。 - 投票処理
画像上の各エッジ点に対して、円の中心となり得る点に「投票」します。 - 円の特定
投票数がしきい値を超えた点を円の中心とし、そこからの距離が一定の点群から円の半径を決定します
この手法により、画像中の円を効率的に特定できます。
利用例
cv2.HoughCircles
関数は以下のような場面で活用されています:
- スポーツ分析(ボールの追跡)
- 製造業での部品検査(円形部品の位置決めや欠陥検査)
- 医療画像診断(細胞や病変の検出)
- 顔認識(目や鼻などの円形の器官を検出)
cv2.HoughCircles関数
cv2.HoughCircles
関数の引数と戻り値について説明します。
引数
名称 | 説明 |
---|---|
入力画像(必須) | ・入力画像。8ビット単一チャンネルの画像である必要があります。 ・ numpy.ndarray オブジェクト |
method(必須) | ・HoughModes を参照してください。使用できるのはcv2.HOUGH_GRADIENT とcv2.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_GRADIENT
とcv2.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:円の半径
使い方
サンプルコードと入力画像は以下の通りです。
円形のコインを検出するサンプルコードがこちら。
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
関数を使ってみるとわかりますが、引数のチューニングが難しくなかなか期待する結果を得るのが難しい関数だと感じます。
引数決定ガイド
引数決定のポイントを以下にまとめました。
筆者の経験的な目安であり、必ずうまくいくとは限らないことをご承知おきいただければと思います。
以下のポイントにも注意を払ってください。
- 前処理の重要性
- ガウシアンブラーなどでノイズを除去
- コントラスト調整を行い、円のエッジを強調
- 画像の特性に応じた調整
- 明るさやコントラストの違いに注意
- 円のサイズに応じてminRadius、maxRadiusを調整
- 処理速度とのバランス
- パラメータによって処理時間が大きく変化
- リアルタイム処理の場合は特に注意
おわりに
cv2.HoughCircles
関数は、適切なパラメータ設定と前処理を行うことで、非常に強力な円検出ツールとなります。
本記事で解説した原理とチューニングのコツを参考に、アプリケーションに最適な円検出を実装してみてください。
また、引数の調整は試行錯誤が必要ですが、それは同時に画像処理の深い理解につながる良い機会でもあります。ぜひ、実際の画像で実験を重ねながら、最適な設定を見つけ出してください。
ご質問や取り上げて欲しい内容などがありましたら、コメントをお願いします。
最後までご覧いただきありがとうございました。
■(広告)OpenCVの参考書としてどうぞ!■
参考リンク
OpenCV: HoughCircles()
Finds circles in a grayscale image using the Hough transform. The function finds circles in a grayscale image using a modification of the Hough transform.
Hough Circle Transform
We will learn to use Hough Transform to find circles in an image. We will see these functions: cv.HoughCircles()