.. _Canny: Canny法によるエッジ検出 *********************** 目的 ====== このチュートリアルでは以下について学びます. * Canny法によるエッジ検出 * OpenCVの **cv2.Canny()** 関数 理論 ========= Canny法とはJohn F. Cannyが1986年に発表したエッジ検出のためのアルゴリズムです. 複数ステップによってエッジ検出を行います 1. **ノイズ削減** エッジ検出は画像中のノイズに対して敏感なため,まず初めに画像を平滑化してノイズを削減します.具体的には5x5のサイズの Gaussianフィルタを使います.フィルタリングの方法は既に前のチュートリアルで見ているかと思います. 2. **画像の輝度勾配を見つける** 次に,平滑化された画像からSobelフィルタを使って縦方向(:math:`G_y`)と横方向(:math:`G_x`)の1次微分を取得します.これら2つの微分画像から以下のようにエッジの勾配と方向を求めます: .. math:: Edge\_Gradient \; (G) = \sqrt{G_x^2 + G_y^2} Angle \; (\theta) = \tan^{-1} \bigg(\frac{G_y}{G_x}\bigg) 勾配方向は常にエッジに対して直交します.勾配方向は横,縦,二つの対角方向の内どれか一つになります. 3. **非極大値の抑制** 勾配の方向と強度を計算した後は,エッジと関係ない画素を取り除きます.具体的には,各画素に対してその画素が勾配方向に対して極大値であるかどうかを確認します.以下の画像を見てください: .. image:: images/nms.jpg :alt: Non-Maximum Suppression :align: center 点Aは縦方向のエッジ上の点です.勾配方向はこのエッジの法線です.点BとCは勾配方向上の点です.つまり,点Aが極大値であるかどうかを確認するために点BとCを使います.もし点Aが極大値であれば次の計算に進み,そうでなければ抑制され0となります. 端的に言うと細いエッジを含む2値画像が得られます. 4. **ヒステリシス(Hysteresis)を使ったしきい値処理** 前処理で検出されたエッジの内,正しいエッジとそうでないものを区別します.この区別をするために, `minVal` と `maxVal` という二つのしきい値を使います.画素値の微分値が `maxVal` 以上であれば正しいエッジとみなし, `minVal` 以下の値であればエッジではないとみなし除外します.微分値が二つのしきい値の間であれば,正しいエッジとそうでないエッジとの隣接関係を基に区別します.正しいエッジと区別された画素につながっていれば正しいエッジとみなし,そうでなければエッジではない画素とみなします.以下の画像を見てください: .. image:: images/hysteresis.jpg :alt: Hysteresis Thresholding :align: center エッジAは `maxVal` 以上の値であるため "正しいエッジ" であるとみなします.エッジCは `maxVal` より小さい値ですが,エッジAにつながっているためエッジであるとみなされます.しかし,エッジBは `minVal` より大きい値でエッジCすが "正しいエッジ" につながっていないためエッジではないと区別されます.これから,正しい結果を得るためには `minVal` と `maxVal` の値を正しく設定する事が重要であると分かります. この処理では,エッジは長い線であるという前提のもと,少数の画素で構成されるエッジも削除します. 最終的に画像中の強いエッジを検出できます. OpenCVのCannyエッジ検出 =============================== OpenCVは上記の全ての処理を実行する **cv2.Canny()** という関数を用意しています.以下に使い方を説明します.第1引数は入力画像を指定します.第2,3引数はヒステリシスを使ったしきい値処理に使う `minVal` と `maxVal` をそれぞれ指定します.第4引数は画像の勾配を計算するためのSobelフィルタのサイズ `aperture_size` で,デフォルト値は3です.最後の引数は勾配強度を計算するための式を指定する `L2gradient` です. ``True`` を指定すると上述した式を使い,より精度が高いエッジ強度を計算します.そうでなければ以下の式を使ってエッジ強度を計算します: :math:`Edge\_Gradient \; (G) = |G_x| + |G_y|`.デフォルトのフラグは ``False`` を指定しています. :: import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('messi5.jpg',0) edges = cv2.Canny(img,100,200) plt.subplot(121),plt.imshow(img,cmap = 'gray') plt.title('Original Image'), plt.xticks([]), plt.yticks([]) plt.subplot(122),plt.imshow(edges,cmap = 'gray') plt.title('Edge Image'), plt.xticks([]), plt.yticks([]) plt.show() 以下の結果を見てください: .. image:: images/canny1.jpg :alt: Canny Edge Detection :align: center 補足資料 ======================= #. Cannyエッジ検出 `Wikipedia(英語) `_ #. Bill Greenによる `Cannyエッジ検出チュートリアル(英語) `_ 2002. 課題 =========== #. Cannyエッジ検出の二つのしきい値をトラックバーで変更できるプログラムを作成してください.この方法でしきい値の効果を理解できます.