Canny法によるエッジ検出

目的

このチュートリアルでは以下について学びます.

  • Canny法によるエッジ検出
  • OpenCVの cv2.Canny() 関数

理論

Canny法とはJohn F. Cannyが1986年に発表したエッジ検出のためのアルゴリズムです. 複数ステップによってエッジ検出を行います

  1. ノイズ削減

エッジ検出は画像中のノイズに対して敏感なため,まず初めに画像を平滑化してノイズを削減します.具体的には5x5のサイズの Gaussianフィルタを使います.フィルタリングの方法は既に前のチュートリアルで見ているかと思います.

  1. 画像の輝度勾配を見つける

次に,平滑化された画像からSobelフィルタを使って縦方向(G_y)と横方向(G_x)の1次微分を取得します.これら2つの微分画像から以下のようにエッジの勾配と方向を求めます:

Edge\_Gradient \; (G) = \sqrt{G_x^2 + G_y^2}

Angle \; (\theta) = \tan^{-1} \bigg(\frac{G_y}{G_x}\bigg)

勾配方向は常にエッジに対して直交します.勾配方向は横,縦,二つの対角方向の内どれか一つになります.

  1. 非極大値の抑制

勾配の方向と強度を計算した後は,エッジと関係ない画素を取り除きます.具体的には,各画素に対してその画素が勾配方向に対して極大値であるかどうかを確認します.以下の画像を見てください:

Non-Maximum Suppression

点Aは縦方向のエッジ上の点です.勾配方向はこのエッジの法線です.点BとCは勾配方向上の点です.つまり,点Aが極大値であるかどうかを確認するために点BとCを使います.もし点Aが極大値であれば次の計算に進み,そうでなければ抑制され0となります.

端的に言うと細いエッジを含む2値画像が得られます.

  1. ヒステリシス(Hysteresis)を使ったしきい値処理

前処理で検出されたエッジの内,正しいエッジとそうでないものを区別します.この区別をするために, minValmaxVal という二つのしきい値を使います.画素値の微分値が maxVal 以上であれば正しいエッジとみなし, minVal 以下の値であればエッジではないとみなし除外します.微分値が二つのしきい値の間であれば,正しいエッジとそうでないエッジとの隣接関係を基に区別します.正しいエッジと区別された画素につながっていれば正しいエッジとみなし,そうでなければエッジではない画素とみなします.以下の画像を見てください:

Hysteresis Thresholding

エッジAは maxVal 以上の値であるため “正しいエッジ” であるとみなします.エッジCは maxVal より小さい値ですが,エッジAにつながっているためエッジであるとみなされます.しかし,エッジBは minVal より大きい値でエッジCすが “正しいエッジ” につながっていないためエッジではないと区別されます.これから,正しい結果を得るためには minValmaxVal の値を正しく設定する事が重要であると分かります.

この処理では,エッジは長い線であるという前提のもと,少数の画素で構成されるエッジも削除します.

最終的に画像中の強いエッジを検出できます.

OpenCVのCannyエッジ検出

OpenCVは上記の全ての処理を実行する cv2.Canny() という関数を用意しています.以下に使い方を説明します.第1引数は入力画像を指定します.第2,3引数はヒステリシスを使ったしきい値処理に使う minValmaxVal をそれぞれ指定します.第4引数は画像の勾配を計算するためのSobelフィルタのサイズ aperture_size で,デフォルト値は3です.最後の引数は勾配強度を計算するための式を指定する L2gradient です. True を指定すると上述した式を使い,より精度が高いエッジ強度を計算します.そうでなければ以下の式を使ってエッジ強度を計算します: 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()

以下の結果を見てください:

Canny Edge Detection

補足資料

  1. Cannyエッジ検出 Wikipedia(英語)
  2. Bill Greenによる Cannyエッジ検出チュートリアル(英語) 2002.

課題

  1. Cannyエッジ検出の二つのしきい値をトラックバーで変更できるプログラムを作成してください.この方法でしきい値の効果を理解できます.