OpenCV のエッジ検出について (canny)

2021年5月25日

はじめに

OpenCV のcannyを使ったエッジ検出処理ついて調べたことをまとめた。

とりあえず関数を試してみたい方

実験用プログラム OpenCV_test の GitHubリポジトリ
URL
https://github.com/iryachi/OpenCV_test
実行ファイルだけ
https://github.com/iryachi/OpenCV_test/releases
使い方
https://iryachi.stars.ne.jp/opencv_test_imple/

内部処理

内部でやっていることは以下の感じ
 ・Gaussian フィルタをかけて平滑化。
 ・Sobel フィルタをかけてエッジ抽出。
 ・閾値によって、エッジが鮮明で繋がっているものを選別。

注意点
8ビット整数の入力とした場合でも内部処理は拡張されて行われるので、閾値の設定が 255 を超える。
出力時に入力と同じ形式に変換されるので、8ビットだと 0, 255 の二値データみたいになりやすい。

canny関数について

関数1

void cv::Canny(InputArray image,
OutputArray edges,
double threshold1,
double threshold2,
int apertureSize = 3,
bool L2gradient = false

引数

imageInputArray入力画像グレースケールの画像
edgesOutputArray出力画像グレースケールの画像
threshold1double閾値1この値より小さいエッジの値はエッジではないと判断
threshold2double閾値2この値より大きいものをエッジと判断。 この値より小さくても、閾値より大きいものは、閾値2より大きいものと繋がっていればエッジと判断。
apertureSizeintアパーチャサイズSobelフィルタをかける時のアパーチャサイズ。
L2gradientboolL2 モードフラグ傾きの大きさ計算するのにL2 モード と L1 モードのどちらを使うか。 L1 : |dI/dx| + |dI/dy| L2 : √((dI/dx)2 + (dI/dy)2))

関数2

void cv::Canny(InputArray dx,
InputArray dy,
OutputArray edges,
double threshold1,
double threshold2,
bool L2gradient = false )

引数

dxInputArray入力画像x方向(水平)にSobelフィルタをかけた後の画像
dyInputArray入力画像y方向(垂直)にSobelフィルタをかけた後の画像
edgesOutputArray出力画像エッジ抽出後の画像
threshold1double閾値1この値より小さいエッジの値はエッジではないと判断
threshold2double閾値2この値より大きいものをエッジと判断。 この値より小さくても、閾値より大きいものは、閾値2より大きいものと繋がっていればエッジと判断。
L2gradientboolL2 モードフラグ傾きの大きさ計算するのにL2 モード と L1 モードのどちらを使うか。 L1 : |dI/dx| + |dI/dy| L2 : √((dI/dx)2 + (dI/dy)2))

処理例

元画像
縦線:線ごとに数値を変更
横線:0 – 255 までのグラデーション と
   途中に切れ目を入れたもの

関数1, apercherSize = 3, threshold2 = 0, threshold1 を変更
エッジの緩やかなものから順番に消えている。

関数1,threshold1 = 1200, threshold2 を変更
threshold1 が 0 の時は上段のグラデーションは消えていないが、
threshold2 を変更すると徐々に消えている。

おわりに

“繋がっている場合はエッジとみなす" という考え方がわかるまで難しいと感じた。
影がかかってるけど輪郭を抽出したい場合とかに使用できると思う。