Cv2.HoughCircles の使い方【OpenCV/C#(OpenCvSharp)】〜ハフ変換で円を検出する〜
Cv2.HoughCircles は、ハフ変換を用いて画像中の円を検出する関数です。コインや穴、工業部品の円形パターンなど、形状が既知の円を画像から探す処理に幅広く使われます。最小限の呼び出し例は次のとおりです。
CircleSegment[] circles = Cv2.HoughCircles(gray, HoughModes.Gradient, dp: 1, minDist: 30, param1: 100, param2: 30, minRadius: 10, maxRadius: 100);
動作環境
- OpenCV 4.x(OpenCvSharp4)
- .NET 8 コンソールアプリ
- NuGet:
OpenCvSharp4+OpenCvSharp4.runtime.win(Windows 実行時)
環境構築がまだの方は環境構築ガイドを先にどうぞ。
基本の使い方
using OpenCvSharp;
// 入力画像の読み込み
using var src = Cv2.ImRead("input.png", ImreadModes.Color);
if (src.Empty()) throw new Exception("画像を読み込めませんでした");
// グレースケール変換
using var gray = new Mat();
Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);
// ガウシアンブラーでノイズを除去(HoughCircles の前処理として推奨)
using var blurred = new Mat();
Cv2.GaussianBlur(gray, blurred, new Size(9, 9), 2);
// 円検出
CircleSegment[] circles = Cv2.HoughCircles(
blurred,
HoughModes.Gradient,
dp: 1.0, // 累積配列の解像度比(1 = 入力と同解像度)
minDist: 30, // 検出円の中心間の最小距離
param1: 100, // Canny エッジ検出の高閾値
param2: 30, // 円の中心検出に必要な投票数(小さいほど誤検出が増える)
minRadius: 10, // 検出する円の最小半径
maxRadius: 100 // 検出する円の最大半径
);
// 結果を元画像に描画
using var result = src.Clone();
foreach (var c in circles)
{
// 円周を緑で描画
Cv2.Circle(result, (Point)c.Center, (int)c.Radius, Scalar.Green, 2);
// 中心点を赤で描画
Cv2.Circle(result, (Point)c.Center, 3, Scalar.Red, -1);
}
Cv2.ImWrite("output.png", result);
Console.WriteLine($"検出した円の数: {circles.Length}");
実行結果:
検出した円の数: 5
output.png に円周(緑)と中心点(赤)が描画された画像が出力されます。
コード解説
| 行の内容 | 意味 |
|---|---|
CvtColor → BGR2GRAY |
HoughCircles はグレースケール画像を受け取る |
GaussianBlur |
ノイズによる誤検出を抑制する前処理。カーネルサイズと sigma は要チューニング |
HoughModes.Gradient |
OpenCV 4.x で使える標準的なモード(21HT) |
dp: 1.0 |
1 にすると入力と同解像度の累積配列。2 にするとハーフサイズで高速になるが精度が落ちる |
minDist |
これが小さすぎると同一円を複数検出しやすい |
param1 / param2 |
エッジの鋭さ・投票数。画像によって大きく変わる最重要パラメータ |
(Point)c.Center |
Point2f → Point の明示キャスト |
引数と戻り値
CircleSegment[] Cv2.HoughCircles(
InputArray image,
HoughModes method,
double dp,
double minDist,
double param1 = 100,
double param2 = 100,
int minRadius = 0,
int maxRadius = 0
)
| 引数 | 型 | 説明 |
|---|---|---|
image |
InputArray |
8ビットグレースケール画像 |
method |
HoughModes |
検出メソッド。通常は HoughModes.Gradient |
dp |
double |
累積配列と入力画像の解像度比。1 〜 2 が一般的 |
minDist |
double |
検出された円の中心同士の最小距離(ピクセル) |
param1 |
double |
Canny エッジ検出の上側閾値(内部で自動適用) |
param2 |
double |
投票数閾値。小さいと誤検出増・大きいと見逃し増 |
minRadius |
int |
検出する最小半径(0 = 制限なし) |
maxRadius |
int |
検出する最大半径(0 = 制限なし) |
戻り値: CircleSegment[]。各要素は Center(Point2f)と Radius(float)を持つ構造体。
実践例
コイン検数・ボルト穴など「大きさがほぼ既知」の円を検出する
工業検査や計測では、対象の円の大きさがある程度わかっていることが多いです。minRadius と maxRadius を絞ることで精度が上がります。
using OpenCvSharp;
using var src = Cv2.ImRead("coins.png", ImreadModes.Color);
if (src.Empty()) throw new Exception("画像を読み込めませんでした");
using var gray = new Mat();
Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);
using var blurred = new Mat();
Cv2.GaussianBlur(gray, blurred, new Size(7, 7), 1.5);
// コインの半径が 40〜80px 程度と事前にわかっている想定
CircleSegment[] circles = Cv2.HoughCircles(
blurred,
HoughModes.Gradient,
dp: 1.2,
minDist: 60,
param1: 80,
param2: 35,
minRadius: 40,
maxRadius: 80
);
using var result = src.Clone();
for (int i = 0; i < circles.Length; i++)
{
var c = circles[i];
Cv2.Circle(result, (Point)c.Center, (int)c.Radius, Scalar.LimeGreen, 2);
// 番号をラベル表示
Cv2.PutText(result, $"{i + 1}", (Point)c.Center,
HersheyFonts.HersheySimplex, 0.8, Scalar.Yellow, 2);
}
Cv2.ImWrite("coins_result.png", result);
Console.WriteLine($"コイン枚数: {circles.Length}");
コイン枚数: 8
検出結果を外接矩形でトリミングする
検出した円を個別にクロップして後段処理に渡すケースもよくあります。BoundingRect と組み合わせると便利です(参考: Cv2.BoundingRect / Cv2.MinAreaRect の使い方)。
using OpenCvSharp;
using var src = Cv2.ImRead("input.png", ImreadModes.Color);
if (src.Empty()) throw new Exception("画像を読み込めませんでした");
using var gray = new Mat();
Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);
using var blurred = new Mat();
Cv2.GaussianBlur(gray, blurred, new Size(9, 9), 2);
CircleSegment[] circles = Cv2.HoughCircles(
blurred, HoughModes.Gradient,
dp: 1.0, minDist: 40,
param1: 100, param2: 30,
minRadius: 20, maxRadius: 120
);
for (int i = 0; i < circles.Length; i++)
{
var c = circles[i];
int r = (int)c.Radius;
// 円の外接矩形を計算してクロップ
var rect = new Rect((int)c.Center.X - r, (int)c.Center.Y - r, r * 2, r * 2);
// 画像範囲内にクランプ
rect = Rect.Intersect(rect, new Rect(0, 0, src.Width, src.Height));
if (rect.Width <= 0 || rect.Height <= 0) continue;
using var roi = new Mat(src, rect);
Cv2.ImWrite($"circle_{i:D3}.png", roi);
}
Console.WriteLine($"切り出し完了: {circles.Length} 件");
切り出し完了: 5 件
つまずきポイント
⚠️ グレースケール画像を渡さないと例外が出る
HoughCircles はグレースケール(8ビット、1チャネル)画像しか受け付けません。BGR カラー画像をそのまま渡すと実行時例外が発生します。必ず CvtColor でグレースケール変換してから渡してください。
// NG: BGR 画像をそのまま渡す
Cv2.HoughCircles(src, ...);
// OK: グレースケールに変換してから渡す
Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);
Cv2.HoughCircles(gray, ...);
⚠️ param2 の調整で結果が激変する
param2(投票数閾値)は画像・対象物によって適切な値が大きく異なります。小さくすると誤検出が増え、大きくすると本来検出すべき円を見逃します。デバッグ時は小さい値(20〜30)から始めて徐々に上げていくのが実践的な進め方です。minDist と minRadius / maxRadius で候補を絞ってから param2 を詰めると効率よく調整できます。
⚠️ c.Center は Point2f なので Point への明示キャストが必要
CircleSegment.Center は Point2f(浮動小数点)型です。Cv2.Circle や Cv2.PutText は Point(整数)を引数に取るため、(Point)c.Center と明示的にキャストしてください。暗黙変換はコンパイルエラーになります。
// NG: そのまま渡すとコンパイルエラー
Cv2.Circle(result, c.Center, ...);
// OK: 明示キャスト
Cv2.Circle(result, (Point)c.Center, (int)c.Radius, Scalar.Green, 2);
関連する関数
- Cv2.HoughLines / Cv2.HoughLinesP — 同じハフ変換ファミリーの直線検出関数
- Cv2.Canny —
HoughCircles内部でも使われるエッジ検出。param1の挙動理解に役立つ - Cv2.GaussianBlur — 円検出前のノイズ除去に必須
- Cv2.BoundingRect / Cv2.MinAreaRect の使い方【OpenCV/C#(OpenCvSharp)】〜外接矩形を求める〜 — 検出した円を矩形でクロップする際に組み合わせる
- Cv2.ApproxPolyDP の使い方【OpenCV/C#(OpenCvSharp)】〜輪郭を多角形近似する〜 — 輪郭ベースの形状認識と使い分けを検討する際の参考に
まとめ
Cv2.HoughCircles は HoughModes.Gradient モードを指定し、グレースケール + ガウシアンブラーを前処理として組み合わせるのが基本パターンです。param2・minDist・minRadius/maxRadius を対象に合わせて調整することが精度向上の鍵になります。戻り値の CircleSegment を使って中心座標と半径を直接取得できるため、後段の描画・クロップ処理とシームレスに連携できます。
🛠 画像処理のプロが開発するSDK/API
本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/APIを開発しています。SDK は C#/.NET 8 に正式対応しており、OpenCvSharp を使うプロジェクトからそのまま組み込めます。
→ 顔認証のなりすまし対策 設計チェックリスト(当ブログ)
→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード
