OpenCV開発ノート(44):赤いデブ男がハフ円変換を詳細に理解するのに8分かかります(写真とテキスト+わかりやすい+プログラムのソースコード付き)

記事がオリジナルの記事である場合、許可なく複製することはできません。
元のブロガーのブログアドレス:https : //blog.csdn.net/qq21497936
オリジナルのブロガーのブログナビゲーション:https : //blog.csdn.net/qq21497936/article/details / 102478062
この記事のブログアドレス:https : //blog.csdn.net/qq21497936/article/details/105575546
読者、知識は無限大、人材は乏しい、需要を変える、専門家を見つける、または自分で勉強する

ディレクトリ

序文

デモ

ハフ変換

概観

ハフサークル変身

概観

原理

1.円の画像の2次元空間は、デカルト座標系で表すことができます

2.三次元曲線の形成原理

3.サークルを判断するための基礎

ハフ勾配の原理

ハフ勾配法の利点

ハフ勾配法の欠点

ハフ円変換関数プロトタイプ

デモのソースコード

エンジニアリングテンプレート:対応するバージョン番号v1.39.0


OpenCV開発コラム(ポータルをクリック)

 

    OpenCV開発ノート(44):赤いデブ男がハフ円変換を詳細に理解するのに8分かかります(写真とテキスト+わかりやすい+プログラムのソースコード付き)

 

序文

      赤いデブ男も来ます!

ノイズ除去とエッジ検出の後、それは特徴抽出です。グラフィックスを識別する基本的な方法の1つはハフ変換です。ハフ変換は画像処理における特徴抽出技術です。この章では主にハフ円変換について説明します。

 

デモ

      最初の2つのデモは、作成者が元の画像をズームしたために元の円が少し変形したため、あまり正確ではありません。最後のデモは、スケーリングされた解像度に応じて単独で描画されました。

 

ハフ変換

概観

      ハフ変換(Hough Transform)は、画像処理における特徴抽出技術であり、修正プロセスは、パラメータ空間での累積結果の極大値を計算し、特定の形状に適合するセットをハフ変換結果として取得します。

      従来のハフ変換は、画像内の直線を検出するために使用されていましたが、その後、ハフ変換は、任意の形状のオブジェクト、主に円と楕円の認識に拡張されました。

      ハフ変換は、2つの座標空間間の変換を使用して、1つの空間で同じ形状の曲線または直線を別の座標コントロール上の点にマッピングしてピークを形成し、形状の検出の問題を統計的なピークの問題に変換します。

      以下に示すように、OpenCVのハフ変換は2つのタイプに分けられ、ライン変換は3つのタイプに分けられます。

ハフサークル変身

概観

      ハフ円変換は、名前から実際に円であることを知ることができます。明らかに、これは円を見つける方法です。ここでは特に注意を払っています。ハフ円変換を使用する前に、画像の前処理が必要です:ノイズ除去、エッジ検出、ハフ円変換は円のみを検索し、エッジバイナリ画像のみを認識できるため、入力は2値化(シングルチャネル8ビット)画像のみです。

      ハフ円変換では多数の円が検出されますが、一部の円は実際には役に立たないため、ハフ線変換と同様に、「ノイズの多い」データが生成される場合があります。

原理

1.円の画像の2次元空間は、デカルト座標系で表すことができます

  • デカルト座標系(ハフ円変換で採用されている方法)では、円の中心(a、b)があり、半径rが表します。

デカルト座標系での円の方法:

式は次のとおりです。

そして

したがって、abrで構成される3次元座標系では、点は円を一意に定義できます。

2.三次元曲線の形成原理

デカルトxy座標系の特定の点を通過するすべての円は、3次元曲線としてbr座標系にマッピングされます。

xy座標系のすべてのゼロ以外のピクセルを通過するすべての円は、abr座標系の多くの3次元曲線を構成します。

3.サークルを判断するための基礎

xy座標系の同じ円上のすべての点の円方程式は同じであり、それらはabr座標系の同じ点にマッピングされるため、abr座標系では、円の合計N0曲線が存在するはずです。交差する。abrの各ポイントの交点(累積)の数を判断することにより、特定のしきい値を超えるポイントは円と見なされます。

上記は標準的なハフ円変換アルゴリズムですが、問題はその累積面が3次元空間であるため、ハフ線変換よりも計算コストがかかることです。

Opencv ハフサークル変換は、標準のハフサークル変換を最適化します。「ハフ勾配法」を採用しています。その検出のアイデアは、すべてのゼロ以外のポイントに対応する円の中心をトラバースして累積し、円の中心を考慮することです。

円の中心は、円上の各点のモジュロベクトル上にある必要があります。つまり、点に垂直で、点の接線を通る垂直線上にある場合、これらの円上のモードベクトルの交点が円の中心になります。

ハフ勾配法は、これらの中心点を見つけ、「中心点」上のモジュロベクトルの交点の数に応じて、しきい値に従って最終的な判断を行う方法です。

ハフ勾配の原理

  1. 最初に、キャニーエッジ検出を使用するなど、画像のエッジ検出を実行します。
  2. エッジ画像の非ゼロポイントごとに、ローカルグラディエントが考慮されます。つまり、Sobel()関数を使用して、x方向とy方向のソーベル微分を計算し、グラジエントを取得します。
  3. 得られた勾配を使用して、あるレートの指定されたライン上のすべてのポイントがアキュムレータに累積されます。ここで、勾配は、指定された最大値から指定された最大値までの距離です。
  4. 同時に、エッジイメージ内のゼロ以外の各ピクセル位置をマークします。
  5. 2次元アキュムレータのこれらの点から候補の中心を選択します。これにより、すべての中心が特定のしきい値より大きく、そのすべての近傍より大きくなります。これらの候補の中心は、累積値の降順で配置され、ピクセルをサポートする側の中心が最初に表示されます。
  6. 各中心について、ゼロ以外のすべてのピクセルを考慮します。
  7. この場合、ピクセルは中心からの距離に従って配置され、最小距離から最大半径まで、ゼロ以外のピクセルによってサポートされる半径が選択されます。
  8. 中心がエッジ画像のゼロ以外のピクセルによって完全にサポートされており、以前に選択した中心までの距離が十分にある場合、それは保持されます。

ハフ勾配法の利点

      このアルゴリズムは効率的であり、3次元アキュムレータで大量のノイズを生成し、結果を不安定にするスパースで不安定な問題を解決できます。

ハフ勾配法の欠点

  • Sobel導関数を使用して計算すると、ノイズが増える可能性があります。
  • エッジ画像では、ゼロ以外のピクセル全体が候補の中心と見なされるため、アキュムレータの値を低く設定すると、ポイントが急上昇し、計算量が多くなり、アルゴリズムの時間がかかりすぎます。
  • 中心は関連するアキュムレータの昇順で配置され、新しい中心が以前に受け入れられた中心点に近すぎる場合、その点は保持されません。これは、最大の半径の円のみを保持する同心円と同等です。

ハフ円変換関数プロトタイプ

void HoughCircles( InputArray image,
                OutputArray circles,
                int method,
                double dp,
                double minDist,
                double param1 = 100,
                double param2 = 100,
                int minRadius = 0,
                int maxRadius = 0);
  • パラメータ1: InputArrayタイプの画像、8ビットのソース画像、シングルチャネルのバイナリ画像。元の画像を読み込み、関数でこの形式に変更してから、ここに入力できます。
  • パラメーター2:出力配列タイプcirlces、出力円ベクトル、各ベクトルには3つの浮動小数点要素が含まれます- 円の中心横座標、円の中心縦座標、および円の半径。
  • パラメーター3:ハフ変換円検出を使用するアルゴリズムであるint型メソッド。

シリアル番号

列挙

価値

説明文

1

HOUGH_STANDARD

0

CV_HOUGH_STANDARD-従来または標準のハフ変換(SHT)。各線分は2つの浮動小数点数(ρ、θ)で表されます。ここで、ρは線と原点(0,0)間の距離、およびθ線分とx軸間の角度です。したがって、マトリックスタイプはCV_32FC2タイプでなければなりません。

2

HOUGH_PROBABILISTIC

1

CV_HOUGH_PROBABILISTIC- 確率的ハフ変換(PPHT )。画像に長い線形セグメントが含まれている場合、効率は高くなります。セグメント全体ではなく、セグメントセグメントを返します。各セグメントは開始点と終了点で表されるため、マトリックス(または作成されたシーケンス)のタイプCV_32SC4 type です。

HOUGH_MULTI_SCALE

2

従来のハフ変換のマルチスケールバリアント。コードするセグメントCV_HOUGH_STANDARD 一貫し

4

HOUGH_GRADIENT

基本的に21HT

  • パラメーター4:ダブルタイプdp、dp:円弧の中心の累積解像度を見つけるこのパラメーターを使用すると、入力画像よりも低い解像度のアキュムレーターを作成できます。(これは、画像に存在する円が自然に画像の幅と高さと同じ量まで減少すると信じる理由があるためです)。dpが1に設定されている場合、解像度は同じです。大きな値(2など)に設定されている場合、アキュムレータの解像度はこの効果(この場合は半分)によって減少します。dpの値は1より大きくすることはできません小さい
  • パラメーター5:double タイプのminDist 、精度、このパラメーターは、アルゴリズムが明確に区別できる2つの異なる円の間の最小距離です。
  • パラメーター6:double タイプparam1 。デフォルトは100でCanny のエッジしきい値の上限に使用されます下限は上限の半分に設定されます
  • 7つのパラメータ:ダブルPARAM2 、デフォルトは100 円のアキュムレータ閾値検出位相中心であるハフ勾配の場合には、。小さいほど、存在しない円を多く検出でき、検出を通過できる円が多いほど、完璧なプロトタイプに近づきます。
  • パラメーター8:int タイプのminRadius 、デフォルトは0 、検出円の最小半径。
  • パラメータ9: int タイプmaxRadius 、デフォルトは0 、検出円の最大半径、0の場合、最大はピクセルマトリックスのサイズ。

 

デモのソースコード

void OpenCVManager::testHoughCircles()
{
    QString fileName1 =
            "E:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/17.jpg";
    cv::Mat srcMat = cv::imread(fileName1.toStdString());
    int width = 400;
    int height = 300;

    cv::resize(srcMat, srcMat, cv::Size(width, height));
    cv::Mat colorMat = srcMat.clone();

    cv::String windowName = _windowTitle.toStdString();
    cvui::init(windowName);

    cv::Mat windowMat = cv::Mat(cv::Size(srcMat.cols * 2, srcMat.rows * 3),
                                srcMat.type());

    cv::cvtColor(srcMat, srcMat, CV_BGR2GRAY);

    int threshold1 = 200;
    int threshold2 = 100;
    int apertureSize = 1;

    int dp = 10;            // 默认1像素
    int minDist = 10;       // 默认1°
    int minRadius = 0;
    int maxRadius = 0;

    while(true)
    {
        qDebug() << __FILE__ << __LINE__;
        windowMat = cv::Scalar(0, 0, 0);

        cv::Mat mat;
        cv::Mat dstMat;
        cv::Mat grayMat;

        // 转换为灰度图像
        // 原图先copy到左边
        cv::Mat leftMat = windowMat(cv::Range(0, srcMat.rows),
                                    cv::Range(0, srcMat.cols));
        cv::cvtColor(srcMat, grayMat, CV_GRAY2BGR);
        cv::addWeighted(leftMat, 0.0f, grayMat, 1.0f, 0.0f, leftMat);

        {
            cvui::printf(windowMat,
                         width * 1 + 100,
                         height * 0 + 20,
                         "threshold1");
            cvui::trackbar(windowMat,
                           width * 1 + 100,
                           height * 0 + 50,
                           200,
                           &threshold1,
                           0,
                           255);
            cvui::printf(windowMat,
                         width * 1 + 100,
                         height * 0 + 100, "threshold2");
            cvui::trackbar(windowMat,
                           width * 1 + 100,
                           srcMat.cols * 0 + 130,
                           200,
                           &threshold2,
                           0,
                           255);

            qDebug() << __FILE__ << __LINE__;
            cv::Canny(srcMat, dstMat, threshold1, threshold2, apertureSize * 2 + 1);
            // copy
            mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
                            cv::Range(srcMat.cols * 0, srcMat.cols * 1));

            cv::cvtColor(dstMat, grayMat, CV_GRAY2BGR);
            cv::addWeighted(mat, 0.0f, grayMat, 1.0f, 0.0f, mat);

            cvui::printf(windowMat,
                         width * 1 + 100,
                         height * 1 + 20 - 80,
                         "dp = value / 10");
            cvui::trackbar(windowMat,
                           width * 1 + 100,
                           height * 1 + 50 - 80,
                           200,
                           &dp,
                           1,
                           1000);
            cvui::printf(windowMat,
                         width * 1 + 100,
                         height * 1 + 100 - 80,
                         "minDist = value / 2");
            cvui::trackbar(windowMat,
                           width * 1 + 100,
                           height * 1 + 130 - 80,
                           200,
                           &minDist,
                           1,
                           720);
            cvui::printf(windowMat,
                         width * 1 + 100,
                         height * 1 + 180 - 80,
                         "minRadius");
            cvui::trackbar(windowMat,
                           width * 1 + 100,
                           height * 1 + 210 - 80,
                           200,
                           &minRadius,
                           0,
                           100);
            cvui::printf(windowMat,
                         width * 1 + 100,
                         height * 1 + 260 - 80,
                         "maxRadius");
            cvui::trackbar(windowMat,
                           width * 1 + 100,
                           height * 1 + 290 - 80,
                           200,
                           &maxRadius,
                           0,
                           1000);
            // 边缘检测后,进行霍夫圆检测
            std::vector<cv::Vec3f> circles;
            cv::HoughCircles(dstMat,
                             circles,
                             cv::HOUGH_GRADIENT,
                             dp / 10.0f,
                             minDist / 10.0f,
                             200,
                             100,
                             minRadius,
                             maxRadius);
            // 在图中绘制出每条线段
            dstMat = colorMat.clone();
            for(int index = 0; index < circles.size(); index++)
            {
                cv::Point center(cvRound(circles[index][0]),
                                 cvRound(circles[index][1]));
                int radius = cvRound(circles[index][2]);
                // 绘制圆心
                cv::circle(dstMat, center, 3, cv::Scalar(255, 255, 255));
                // 绘制圆
                cv::circle(dstMat, center, radius, cv::Scalar(0, 0, 255));
            }
            // copy
            mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                            cv::Range(srcMat.cols * 1, srcMat.cols * 2));
            cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);


            // 在图中绘制出每条线段
            for(int index = 0; index < circles.size(); index++)
            {
                cv::Point center(cvRound(circles[index][0]),
                                 cvRound(circles[index][1]));
                int radius = cvRound(circles[index][2]);
                // 绘制圆心
                cv::circle(grayMat, center, 3, cv::Scalar(255, 255, 255));
                // 绘制圆
                cv::circle(grayMat, center, radius, cv::Scalar(0, 0, 255));
            }
            // copy
            mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                            cv::Range(srcMat.cols * 0, srcMat.cols * 1));
            cv::addWeighted(mat, 0.0f, grayMat, 1.0f, 0.0f, mat);
        }
        // 更新
        cvui::update();
        // 显示
        cv::imshow(windowName, windowMat);
        // esc键退出
        if(cv::waitKey(25) == 27)
        {
            break;
        }
    }
}

 

エンジニアリングテンプレート:対応するバージョン番号v1.39.0

      対応するバージョン番号v1.39.0

 

オリジナルのブロガーのブログのアドレス:https://blog.csdn.net/qq21497936
オリジナルのブロガーがナビゲーションブログ:https://blog.csdn.net/qq21497936/article/details/102478062を
この記事のブログのアドレス:HTTPS://ブログ.csdn.net / qq21497936 /記事/詳細/ 105575546

268の元の記事を公開 466のような 540,000以上の 訪問

おすすめ

転載: blog.csdn.net/qq21497936/article/details/105575546