今回はキズ検出を題材に、Sobel, Laplacian, Scharrをカーネルサイズごとに試して、どういう特徴があるのかを見ていきます。
今回はそれぞれの手法の特徴についてのみで、仕組みや数式については触れません。
目次
使うデータ
今回は壁のキズの検出を題材とする。
一枚目はエッジが強いがノイズがかなり多い画像。
前処理でカーネルサイズ5のメディアンフィルタをかけています。
![](https://i0.wp.com/deecode.net/wp-content/uploads/2021/08/image1.jpg?resize=648%2C365&ssl=1)
二枚目は一枚目よりノイズが少ないがエッジが少し弱い画像です。
こちらもノイズを減らすために前処理でカーネルサイズ5のメディアンフィルタをかけています。
![](https://i0.wp.com/deecode.net/wp-content/uploads/2021/08/image2.jpg?resize=648%2C365&ssl=1)
Sobelフィルタ(カーネルサイズ3)の場合
まずはカーネルサイズ3のSobelフィルタです。
どちらもある程度エッジを浮かせることができている。2枚目の少し弱いエッジもある程度検出できている。
1枚目の結果にはある程度ノイズが混じっている。
今回のデータだと、一定レベルでエッジを検出できるがものによってはノイズが残ってしまうという特徴がありそう。
![](https://i0.wp.com/deecode.net/wp-content/uploads/2021/08/sobel3_result.jpg?resize=648%2C365&ssl=1)
ちなみにPythonでOpenCVの場合はこのようなコードです。x, y方向の微分した値を足して2で割る感じ。
import cv2
img = ((abs(cv2.Sobel(img, cv2.CV_32F, 0, 1, ksize=kernel_size)) +
abs(cv2.Sobel(img, cv2.CV_32F, 1, 0, ksize=kernel_size))) / 2).astype('uint8')
Laplacianフィルタ(カーネルサイズ3)の場合
次はLaplacianフィルタ。Sobelフィルタは1次微分に対して、Laplacianフィルタは2次微分です。
1枚目はこんな感じ。Sobelよりもノイズを拾っている。
![](https://i0.wp.com/deecode.net/wp-content/uploads/2021/08/laplacian3_result1-1.jpg?resize=648%2C486&ssl=1)
2枚目はノイズ少ないが、強いエッジではないところはしっかりエッジを浮かせられていない。
![](https://i0.wp.com/deecode.net/wp-content/uploads/2021/08/laplacian3_result2-1.jpg?resize=648%2C486&ssl=1)
これだと「Laplacianフィルタって微妙?」と思うかもしれません。
しかし、以下の画像のようなノイズの少ない画像の場合、Sobelフィルタよりきれいにエッジ検出される。
![](https://i0.wp.com/deecode.net/wp-content/uploads/2021/08/for_laplacian.jpg?resize=648%2C361&ssl=1)
こちらがSobelフィルタの結果。
![](https://i0.wp.com/deecode.net/wp-content/uploads/2021/08/sobel3_result3.jpg?resize=648%2C361&ssl=1)
こちらがLaplacianフィルタの結果。しっかり見ると建物部分のエッジがよりきれいに検出できていることが分かる。
![](https://i0.wp.com/deecode.net/wp-content/uploads/2021/08/laplacian3_result3.jpg?resize=648%2C361&ssl=1)
これらから、LaplacianフィルタはSobelフィルタより強いエッジを浮かせやすいが、ノイズが少し入りやすく弱いエッジは浮かせにくいことが分かる。
しかし、今回の題材のキズ検出には不向きかもしれない。
PythonでOpenCVの場合は以下の実装になる。
import cv2
img = cv2.Laplacian(img, cv2.CV_32F, ksize=kernel_size)
Scharrフィルタの場合
Sobelフィルタは有名だが、Scharrフィルタはあまり聞いたことない人がいるのではないだろうか?
OpenCVの使い方色々書いてあるサイトでは「Sobelフィルタより良いといわれる3×3のScharrフィルタ~」と記載がある。
1枚目はかなりノイズを拾っていてよく分からない。
![](https://i0.wp.com/deecode.net/wp-content/uploads/2021/08/sobel-1_result1.jpg?resize=648%2C486&ssl=1)
ただ二枚目はノイズがあるものの他よりもエッジを多くとらえている。
![](https://i0.wp.com/deecode.net/wp-content/uploads/2021/08/sobel-1_result2.jpg?resize=648%2C486&ssl=1)
ものによっては大量にノイズが入り、フィルタの中身的にScharrの方が値が大きく出やすいということもあるものの、細かいエッジまで浮かせられることが分かる。
そのため、下処理を強めにやる必要がありそう。二値化などである程度絞れるが、ほかの手法よりも閾値を高くする必要がありそう。
Sobel/Laplacianフィルタのカーネルサイズが5の場合は?
カーネルサイズを5にすると全体的に値が大きくなるし、ノイズ部分もエッジと判別されやすくなる。
Sobelフィルタでカーネルサイズが5の場合は以下の通り。全体的に値が大きくなるためかもしれないが、ノイズにも多く反応してしまっている。
閾値次第でどうにかなるのかもしれないがこれだと厳しい。これならScharrの方がいいと感じる。
![](https://i0.wp.com/deecode.net/wp-content/uploads/2021/08/ブログ用画像-5.jpg?resize=648%2C365&ssl=1)
Laplacianフィルタのカーネルサイズ5の場合は以下の通り。Scharrに近い感じもするが、やはり全体的に値が大きくなり、ノイズも拾ってしまっている感じだ。
![](https://i0.wp.com/deecode.net/wp-content/uploads/2021/08/ブログ用画像-6.jpg?resize=648%2C365&ssl=1)
エッジを検出した後はどうするか?
エッジを検出した後は以下のような流れが考えられる。
- 2値化でエッジの強いキズらしき部分以外のところをある程度削る
- Openingでさらに細かいノイズを削除
- Closingで繋がっているキズを繋げるようにする
- 領域検出して面積閾値でキズらしきもの以外を削除
Opening/Closingなどその他の画像処理の手法はこちらの記事にまとめているので見てみてください。
まとめ
今回はキズ検出という題材だった。結果は以下の通りだ。今回はカーネルサイズ3のSobelフィルタがよさそう。
- Sobelフィルタはバランス良くエッジを浮かせる
- Laplacianフィルタは強めのエッジを浮かせるが、Sobelよりノイズを拾いやすく弱いエッジを浮かせにくい
- Scharrフィルタは幅広くエッジを検出するが、全体的に値が大きくなりノイズを多く拾う
- カーネルサイズは3がバランスよく、5などに増やすとノイズを大量に拾ってしまう
ちなみに今回はそれぞれに前処理カスタマイズすると純粋に比較できないのでわざと同じもの使ってます。Scharrが微妙に見えるけど実務では一番優秀でした。手法によって適切な前処理が変わるので比較が難しい。。。