OpenCV 4.13.0
Open Source Computer Vision
読み込み中...
検索中...
見つかりません
🤖 AIによる機械翻訳(非公式) — これは OpenCV 4.13.0 公式リファレンス(英語)を AI (Claude) で自動翻訳したものです。訳に誤りを含む場合があります。正確な情報は 公式英語版(原文) を参照してください。
Cannyエッジ検出器

前のチュートリアル: ラプラシアン演算子
次のチュートリアル: ハフ直線変換

原著者Ana Huamán
互換性OpenCV >= 3.0

目的

このチュートリアルでは、以下の方法を学ぶ:

  • OpenCV の関数 cv::Canny を使って Canny エッジ検出器を実装する。

理論

Canny エッジ検出器 [50] は、1986年に John F. Canny によって開発された。多くの人に最適検出器 (optimal detector) としても知られており、Canny アルゴリズムは3つの主要な基準を満たすことを目指している:

  • 低エラー率: 実在するエッジのみを良好に検出することを意味する。
  • 良好な位置特定: 検出されたエッジピクセルと実際のエッジピクセルとの距離を最小化しなければならない。
  • 最小限の応答: エッジ1つにつき検出応答は1つのみ。

手順

  1. ノイズを除去する。この目的にはガウシアンフィルタが使われる。使用され得る \(size = 5\) のガウシアンカーネルの例を以下に示す:

    \[K = \dfrac{1}{159}\begin{bmatrix} 2 & 4 & 5 & 4 & 2 \\ 4 & 9 & 12 & 9 & 4 \\ 5 & 12 & 15 & 12 & 5 \\ 4 & 9 & 12 & 9 & 4 \\ 2 & 4 & 5 & 4 & 2 \end{bmatrix}\]

  2. Find the intensity gradient of the image. For this, we follow a procedure analogous to Sobel:
    1. Apply a pair of convolution masks (in \(x\) and \(y\) directions:

      \[G_{x} = \begin{bmatrix} -1 & 0 & +1 \\ -2 & 0 & +2 \\ -1 & 0 & +1 \end{bmatrix}\]

      \[G_{y} = \begin{bmatrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ +1 & +2 & +1 \end{bmatrix}\]

    2. Find the gradient strength and direction with:

      \[\begin{array}{l} G = \sqrt{ G_{x}^{2} + G_{y}^{2} } \\ \theta = \arctan(\dfrac{ G_{y} }{ G_{x} }) \end{array}\]

      The direction is rounded to one of four possible angles (namely 0, 45, 90 or 135)
  3. 非最大値抑制を適用する。これにより、エッジの一部とみなされないピクセルが除去される。その結果、細い線(エッジ候補)のみが残る。
  4. ヒステリシス: 最終ステップ。Canny は2つのしきい値(上限と下限)を使用する:

    1. ピクセルの勾配が上限しきい値より大きい場合、そのピクセルはエッジとして受け入れられる
    2. ピクセルの勾配値が下限しきい値を下回る場合、そのピクセルは棄却される。
    3. ピクセルの勾配が2つのしきい値の間にある場合、上限しきい値を超えるピクセルに接続されている場合にのみ受け入れられる。

    Canny は上限:下限の比を 2:1 から 3:1 の間にすることを推奨している。

  5. 詳細については、お好みのコンピュータビジョンの書籍を参照するとよい。

コード

  • What does this program do?
    • (トラックバーを用いて)Canny エッジ検出器の下限しきい値を設定するための数値を入力するようユーザーに求める。
    • Cannyエッジ検出器を適用し、マスク(黒い背景上にエッジを表す明るい線)を生成する。
    • 得られたマスクを元画像に適用し、ウィンドウに表示する。
  • 解説 (C++コード)

    1. 必要な変数をいくつか作成する:

      Mat src, src_gray;
      Mat dst, detected_edges;
      int lowThreshold = 0;
      const int max_lowThreshold = 100;
      const int ratio = 3;
      const int kernel_size = 3;
      const char* window_name = "Edge Map";

      以下の点に注意する:

      1. 下側しきい値と上側しきい値の比を3:1(変数ratioで指定)に設定する。
      2. カーネルサイズを\(3\)に設定する(Canny関数が内部で実行するSobel演算用)。
      3. 下側しきい値の最大値を\(100\)に設定する。
    2. 元画像を読み込む:
      CommandLineParser parser( argc, argv, "{@input | fruits.jpg | input image}" );
      src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR ); // Load an image
      if( src.empty() )
      {
      std::cout << "Could not open or find the image!\n" << std::endl;
      std::cout << "Usage: " << argv[0] << " <Input image>" << std::endl;
      return -1;
      }
    3. srcと同じ型・サイズの行列(dstとする)を作成する:
      dst.create( src.size(), src.type() );
    4. 画像をグレースケールに変換する(cv::cvtColor 関数を使用):
      cvtColor( src, src_gray, COLOR_BGR2GRAY );
    5. 結果を表示するためのウィンドウを作成する:
      namedWindow( window_name, WINDOW_AUTOSIZE );
    6. Create a Trackbar for the user to enter the lower threshold for our Canny detector:
      createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold );
      Observe the following:
      1. トラックバーで制御される変数はlowThresholdで、その上限はmax_lowThreshold(先ほど100に設定した値)である。
      2. トラックバーが操作を検出するたびに、コールバック関数CannyThresholdが呼び出される。
    7. Let's check the CannyThreshold function, step by step:
      1. まず、カーネルサイズ3のフィルタで画像を平滑化する:
        blur( src_gray, detected_edges, Size(3,3) );
      2. Second, we apply the OpenCV function cv::Canny :
        Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );
        where the arguments are:
        • detected_edges:入力画像、グレースケール
        • detected_edges:検出器の出力(入力と同じものでもよい)
        • lowThreshold:ユーザがトラックバーを動かして入力した値
        • highThreshold:プログラム内で下側しきい値の3倍に設定(Cannyの推奨に従う)
        • kernel_size:3に定義した(内部で使用されるSobelカーネルのサイズ)
    8. dst画像をゼロで埋める(画像が完全に黒になることを意味する)。
      dst = Scalar::all(0);
    9. 最後に、cv::Mat::copyTo関数を使用して、エッジとして識別された画像の領域だけを(黒い背景上に)マッピングする。cv::Mat::copyTosrc画像をdstにコピーする。ただし、ゼロ以外の値を持つ位置のピクセルのみをコピーする。Cannyエッジ検出器の出力は黒い背景上のエッジ輪郭なので、結果として得られるdstは検出されたエッジを除くすべての領域が黒になる。
      src.copyTo( dst, detected_edges);
    10. 結果を表示する:
      imshow( window_name, dst );

    結果

    • 上記のコードをコンパイルした後、画像へのパスを引数として与えて実行できる。たとえば、次の画像を入力として使用する場合:
    • スライダーを動かしてさまざまなしきい値を試すと、次のような結果が得られる:
    • エッジ領域で画像が黒い背景に重ね合わされている様子に注目する。