Cv2.HoughLinesP の使い方【OpenCV/C#(OpenCvSharp)】〜直線を検出する(ハフ変換)〜
Cv2.HoughLinesP は確率的ハフ変換を使って画像中の直線(線分)を検出する関数です。通常の HoughLines と異なり、検出結果が「端点を持つ線分」として返るため、描画や幾何計算にそのまま使えます。
先出しの呼び出し形式はこちらです。
LineSegmentPoint[] lines = Cv2.HoughLinesP(src, rho, theta, threshold, minLineLength, maxLineGap);
動作環境
- OpenCvSharp4(OpenCV 4.x 時点)
- NuGet:
OpenCvSharp4+OpenCvSharp4.runtime.win(Windows 実行時) - .NET 8 コンソールアプリ想定
環境構築がまだの方は環境構築ガイドを先にどうぞ。
基本の使い方
入力画像にエッジ検出を適用してから HoughLinesP で直線を検出し、検出した線分を描画して保存するサンプルです。
using OpenCvSharp;
// 入力画像の読み込み(グレースケール)
using var src = Cv2.ImRead("input.png", ImreadModes.Grayscale);
if (src.Empty()) throw new Exception("画像の読み込みに失敗しました");
// エッジ検出(HoughLinesP はエッジ画像を入力とする)
using var edges = new Mat();
Cv2.Canny(src, edges, threshold1: 50, threshold2: 150);
// 確率的ハフ変換で直線検出
LineSegmentPoint[] lines = Cv2.HoughLinesP(
edges,
rho: 1, // 距離分解能(ピクセル単位)
theta: Math.PI / 180, // 角度分解能(ラジアン)
threshold: 80, // 投票数のしきい値
minLineLength: 50, // 検出する線分の最小長さ
maxLineGap: 10 // 同一直線とみなす最大ギャップ
);
// 検出結果をカラー画像に描画
using var dst = new Mat();
Cv2.CvtColor(src, dst, ColorConversionCodes.GRAY2BGR);
foreach (var line in lines)
{
// LineSegmentPoint は .P1 / .P2 で端点を参照できる
Cv2.Line(dst, line.P1, line.P2, Scalar.Red, thickness: 2);
}
Console.WriteLine($"検出した線分数: {lines.Length}");
Cv2.ImWrite("output.png", dst);
実行すると、コンソールに 検出した線分数: 12 のような数値が表示され、output.png に赤い線分が描画された画像が保存されます。
コードのポイント
| 行 | 説明 |
|---|---|
Cv2.Canny |
HoughLinesP はエッジ画像(2値化済みの勾配画像)を入力に取る。事前に Canny 等でエッジを抽出する |
LineSegmentPoint[] |
戻り値の型。各要素に端点 .P1・.P2(Point 型)が入っている |
rho: 1 |
通常は 1 で問題ない。画像が大きい場合は 2 にすることも |
threshold: 80 |
この投票数を超えた直線のみ採用。小さくすると検出数が増えノイズも増える |
minLineLength: 50 |
短い断片を除外するために重要。用途に応じて調整する |
maxLineGap: 10 |
同一の直線上にある断片を繋ぐ最大距離。大きくすると欠けた線も検出できる |
引数と戻り値
LineSegmentPoint[] Cv2.HoughLinesP(
InputArray image,
double rho,
double theta,
int threshold,
double minLineLength = 0,
double maxLineGap = 0
)
| 引数 | 型 | 説明 |
|---|---|---|
image |
InputArray |
8ビット1チャンネルのエッジ画像(Canny 出力等) |
rho |
double |
ハフ空間の距離分解能(ピクセル単位、通常 1) |
theta |
double |
ハフ空間の角度分解能(ラジアン、通常 π/180) |
threshold |
int |
この値を超えた投票数を持つ直線のみを検出 |
minLineLength |
double |
線分として認める最小長(ピクセル)。デフォルト 0 |
maxLineGap |
double |
同一線分とみなす最大ギャップ(ピクセル)。デフォルト 0 |
| 戻り値 | LineSegmentPoint[] |
検出した線分の配列。各要素に .P1・.P2(端点)が入る |
実践例
実践例1: 矩形ドキュメントの辺を検出する
スキャン画像や撮影した書類から外枠の直線を取り出す用途です。minLineLength を大きめにすることで短いノイズ線分を除外できます。検出した線分を使って射影補正まで行う場合は Cv2.WarpPerspective の使い方も参考にしてください。
using OpenCvSharp;
using var src = Cv2.ImRead("document.png", ImreadModes.Grayscale);
using var edges = new Mat();
Cv2.Canny(src, edges, 30, 120);
// 長い線分だけを検出してドキュメント境界を絞り込む
LineSegmentPoint[] lines = Cv2.HoughLinesP(
edges,
rho: 1,
theta: Math.PI / 180,
threshold: 100,
minLineLength: 150,
maxLineGap: 20
);
using var dst = new Mat();
Cv2.CvtColor(src, dst, ColorConversionCodes.GRAY2BGR);
foreach (var line in lines)
Cv2.Line(dst, line.P1, line.P2, Scalar.LimeGreen, 2);
Cv2.ImWrite("document_lines.png", dst);
Console.WriteLine($"検出線分: {lines.Length}");
実践例2: 水平線・垂直線のみを抽出する
角度フィルタを組み合わせることで、水平・垂直の線分だけを取り出せます。表の罫線検出や基板のパターン認識でよく使います。
using OpenCvSharp;
using var src = Cv2.ImRead("grid.png", ImreadModes.Grayscale);
using var edges = new Mat();
Cv2.Canny(src, edges, 50, 150);
LineSegmentPoint[] lines = Cv2.HoughLinesP(
edges,
rho: 1,
theta: Math.PI / 180,
threshold: 80,
minLineLength: 40,
maxLineGap: 5
);
using var dst = new Mat();
Cv2.CvtColor(src, dst, ColorConversionCodes.GRAY2BGR);
foreach (var line in lines)
{
double dx = line.P2.X - line.P1.X;
double dy = line.P2.Y - line.P1.Y;
double angle = Math.Abs(Math.Atan2(dy, dx) * 180.0 / Math.PI);
// 水平(±10°以内)
if (angle < 10 || angle > 170)
Cv2.Line(dst, line.P1, line.P2, Scalar.Blue, 2);
// 垂直(80°〜100°)
else if (angle is > 80 and < 100)
Cv2.Line(dst, line.P1, line.P2, Scalar.Red, 2);
}
Cv2.ImWrite("grid_lines.png", dst);
実行すると、水平線は青、垂直線は赤で描画された画像が保存されます。
つまずきポイント
⚠️ 入力画像は必ずエッジ画像(8ビット1チャンネル)にする
HoughLinesP に BGR カラー画像やグレースケール原画をそのまま渡しても、ほぼ何も検出されません。Cv2.Canny や Cv2.Threshold で前処理したエッジ画像を渡すのが正しい使い方です。エラーメッセージが出ずに空配列が返るだけなので気づきにくい落とし穴です。
⚠️ threshold と minLineLength のバランス調整
threshold を下げると検出数が増えますが、ノイズ線分も増えます。実務では threshold で大まかに絞り込み、minLineLength で短い断片を除外する 2 段階の調整が有効です。パラメータを変えながらトラックバーで試せる Cv2.CreateTrackbar の活用もおすすめです。
✅ LineSegmentPoint の型を把握する
C++ では Vec4i の配列として返るのに対し、OpenCvSharp では LineSegmentPoint という専用構造体の配列になります。.P1・.P2 プロパティで Point 型の端点を取得できるため、C++ コードをそのまま移植しようとするとコンパイルエラーになります。なお LineSegmentPoint は IDisposable ではないため using は不要ですが、Mat は必ず using で解放してください。
関連する関数
- Cv2.Canny — HoughLinesP の前処理として必須。エッジ検出の定番
- Cv2.HoughLines — 線分ではなく無限直線(rho/theta 形式)を返す非確率的ハフ変換
- Cv2.WarpPerspective の使い方 — 検出した直線から射影補正に発展させる場合に使う
- Cv2.ApproxPolyDP の使い方 — 輪郭の多角形近似で直線的な形状を扱う場合に組み合わせることがある
まとめ
Cv2.HoughLinesP は確率的ハフ変換で直線(線分)を端点付きで検出できる関数です。Canny によるエッジ前処理が必須で、threshold・minLineLength・maxLineGap の 3 パラメータを用途に合わせて調整するのが精度を上げるコツです。戻り値が C++ の Vec4i ではなく LineSegmentPoint 構造体になる点が OpenCvSharp 固有の変換ポイントです。
🛠 画像処理のプロが開発するSDK/API
本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/APIを開発しています。SDK は C#/.NET 8 に正式対応しており、OpenCvSharp を使うプロジェクトからそのまま組み込めます。
→ 顔認証のなりすまし対策 設計チェックリスト(当ブログ)
→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード
