
こんな悩みを解決します。
記事の内容
- ノイズ除去の目的によっては、ガウシアンフィルタは最善の手段ではない
- フィルタ処理をどのように使い分けたらよいのか
- フィルタ処理の実装方法
記事の信頼性
この記事を書いている私は、ソフトウェア(画像処理)エンジニアとして自動車メーカーでC言語やPythonを使って製品開発をしています。
画像処理を専門としていますので、ノウハウを共有できたらと思います。
この記事を読むことで、ガウシアンフィルタの使い方がわかります。また、メディアンフィルタやバイラテラルフィルタなど、ノイズ除去の目的に合ったフィルタ処理の選択ができるようになりますよ。
ノイズ除去の目的によっては、ガウシアンフィルタは最善の手段ではない
画像のノイズを除去したいとき、とりあえずガウシアンフィルタを使うという考え方はやめましょう。
ノイズ除去で何をしたいのでしょうか?
たとえば、カメラで撮った写真にザラザラとしたノイズが入っているから綺麗にしたい。とか、物体認識の前処理として細かいエッジだけを除去したい。など。。
フィルタ処理はそれぞれ得意不得意があるので、ノイズ除去の目的によって、使用すべきフィルタ処理は変わってくるのです。
まずは、よく使われるガウシアンフィルタの原理について解説し、そのあと、ガウシアンフィルタのデメリットを説明します。
ガウシアンフィルタの原理
ガウシアンフィルタとは、ガウス分布に従って重みを付けをした平均化フィルタのことです。
平均化フィルタは、変換したい画素(注目画素)とその周辺画素の輝度値を平均化することで、次のようなカーネルで表せます。
(カーネルとは、、フィルタ処理をするときに、参照する画素に掛ける係数のことです。サイズは3×3とか5×5とか奇数になります。)
平均化フィルタは、注目画素も周辺画素もカーネルの重みがすべて同じなので、かなりボケます。
すべて同じ重みを掛けるのは、乱暴なやり方です。
一般の画像であれば、カーネルの中心から近いほど注目画素と似た輝度値になるし、遠いほど注目画素とは異なる輝度値になります。つまり、注目画素に近いほど大きな重みを、遠いほど小さな重みを掛けると、元の情報をできるだけ残しながら、ノイズ除去できるのです。
これを、カーネル中心からの距離を横軸にしたときの正規分布として考えると、以下の数式で重みを計算することができます。これをガウス関数と呼びます。
このガウス関数によって、重みを計算されたカーネルのことを、ガウシアンフィルタと呼びます。
フィルタを設計するときは、カーネルのサイズと正規分布の標準偏差の値を、トライアンドエラーを繰り返して決めていくことになります。
ガウシアンフィルタの処理は以下の式で表現されます。
よく使われるカーネルも載せますので、とりあえず使ってみたい方は、このカーネルを畳み込み処理したら良いです。
ガウシアンフィルタのデメリット
ガウシアンフィルタのデメリットは、エッジがボケてしまうことです。
エッジは、隣合う画素との輝度値の差が大きい領域のことです。
ガウス関数を見ると、カーネル中心からの距離しか考慮されてませんよね?
輝度値の差はまったく考慮されていないので、エッジがあるところもないところも同じようにボケてしまいます。
わかりやすいように、以下の図を描きました。
エッジの場所にフィルタを適用する場合、カーネル中心に近いほど大きな重みになるので、そこにエッジがあっても平均化されボケてしまいます。
このように、ガウシアンフィルタではエッジのボケが発生します。
ガウシアンフィルタは、カーネルのサイズだけ気をつければ、処理時間はそれほどかかりません。リアルタイムで動作するようなアプリケーションでも、一般的に使われています。
ただし、デメリットとしてエッジがボケてしまうので、処理時間を気にしない場合は、バイラテラルフィルタを使ったほうが良いです。
(たとえば、監視カメラや医療検診の解析など、リアルタイムで処理するよりも、よりきれいな画像が求められる場合など。)
フィルタ処理をどのように使い分けたらよいのか
画像のノイズ除去に効果的で、よく使われるフィルタ処理は次の3つなので、覚えておきましょう。
メディアンフィルタとバイラテラルフィルタが何かは、後ほど解説します。
フィルタ処理
- ガウシアンフィルタ
- メディアンフィルタ
- バイラテラルフィルタ
まずは、この3つのフィルタを使うと、どんな画像になるのか比べてみましょう。
ガウシアンフィルタは、画像全体がボケた感じ。
メディアンフィルタは、エッジは強く残るけど、それ以外はのっぺりした感じ。
バイラテラルフィルタは、エッジは残り、それ以外はボケた感じ。
それぞれ画像の特性が違うので、ノイズ除去の用途に合わせてフィルタを選ぶ必要があります。
各フィルタのメリットデメリットを比較する前に、メディアンフィルタと、バイラテラルフィルタの解説を簡単にします。
メディアンフィルタの原理
メディアンフィルタとは、平均化するのではなく中央値をとるフィルタです。
たとえば、以下のような3×3のエリアにメディアンフィルタを適用する場合、エリア内の輝度値9個を順番に並べると、中央値が"101"であることがわかります。この"101"を注目画素に置き換えることが、メディアンフィルタです。
平均化をしないので、エッジがボケにくい特徴があります。
また、ごま塩ノイズを除去するのにも効果的な手法です。
ごま塩ノイズはランダムな位置に黒と白が散りばめられるので、中央値を取ると、これらのノイズは選ばれません。
バイラテラルフィルタの原理
バイラテラルフィルタは、ガウシアンフィルタに輝度値の差を考慮に入れたフィルタです。
ガウス関数は、カーネル中心からの距離しか考慮されていないので、エッジがあってもなくても同じようにボケます。
輝度値の差が大きいところ(=エッジ)は、カーネルの重みを小さくすると、ボケにくいですよね?
輝度値の差を考慮に入れた関数をバイラテラルフィルタと言い、以下のガウス関数で表せます。
左側が、カーネル中心からの距離を横軸にしたときの正規分布。(ガウシアンフィルタで見ましたね。)
右側が、輝度値の差を横軸にしたときの正規分布。輝度値の差が大きいほど重みを小さくします。
バイラテラルフィルタの処理は以下の式で、表現されます。
各フィルタのメリットデメリット
メリット
ガウシアンフィルタ
- カーネルサイズを小さくすれば処理時間は早い
- ガウス分布に基づいた平均化ができる。
メディアンフィルタ
- カーネルサイズを小さくすれば処理時間は早い
- ごま塩ノイズを除去するのに効果的
- エッジを残せる
バイラテラルフィルタ
- ガウス分布に基づき、エッジを残したまま平均化できる
デメリット
ガウシアンフィルタ
- エッジがボケる
メディアンフィルタ
- 平均化ではなく中央値を取るので、元の情報が失われる可能性がある。大きなエッジ以外はのっぺりした画像になる
⇒大きなエッジを検出するための前処理として使うなら問題ない。
バイラテラルフィルタ
- 処理時間がかかる。
- パラメータの適合が大変
まとめると、、
ごま塩ノイズを消したい場合は、メディアンフィルタを使いましょう。
それ以外のノイズを消したい場合、
処理時間に制約があれば、ガウシアンフィルタ。
制約がなければ、バイラテラルフィルタを使いましょう。
物体認識などでは、ノイズ除去だけが目的ではありません。たとえば、特徴点を計算したいから細かい特徴を消したいなど。
そんなときは、ガウシアンフィルタを使いましょう。
メリット、デメリットを理解して、フィルタを使い分けてください。
フィルタ処理の実装方法
この記事で紹介したフィルタ処理の実装方法を解説します。
サンプルコード(Python)も紹介するので、参考にしてください。
ガウシアンフィルタ
ガウシアンフィルタは、 cv2.GaussianBlur関数を使います。
返り値と引数は以下のとおり。
返り値 | 内容 |
第1返り値 | 平滑化画像 |
引数 | 内容 |
第1引数 | 平滑化したい画像 |
第2引数 | カーネルのサイズ(縦横の指定可、奇数) 5×5のサイズなら、(5,5)と設定しましょう |
第3引数 | ガウス関数の標準偏差 |
サンプルコードは以下です。
# sample code import cv2 # 入力画像の読み込み img = cv2.imread("Text.bmp", cv2.IMREAD_GRAYSCALE) cv2.imshow('org_img', img) # ガウシアンフィルタ gauss = cv2.GaussianBlur(img, (9, 9), 0) cv2.imshow('gauss', gauss) cv2.waitKey(0) cv2.destroyAllWindows()
メディアンフィルタ
メディアンフィルタは、 cv2.medianBlur関数を使います。
返り値と引数は以下のとおり。
返り値 | 内容 |
第1返り値 | 平滑化画像 |
引数 | 内容 |
第1引数 | 平滑化したい画像 |
第2引数 | カーネルのサイズ(縦横同じサイズで片方のみ指定可、奇数) 5×5のサイズなら、5と設定しましょう |
サンプルコードは以下です。
# sample code import cv2 # 入力画像の読み込み img = cv2.imread("Text.bmp", cv2.IMREAD_GRAYSCALE) cv2.imshow('org_img', img) # メディアンフィルタ median = cv2.medianBlur(img, 9) cv2.imshow('median', median) cv2.waitKey(0) cv2.destroyAllWindows()
バイラテラルフィルタ
バイラテラルフィルタは、 cv2.bilateralFilter関数を使います。
返り値と引数は以下のとおり。
返り値 | 内容 |
第1返り値 | 平滑化画像 |
引数 | 内容 |
第1引数 | 平滑化したい画像 |
第2引数 | カーネルのサイズ(縦横同じサイズで片方のみ指定可、奇数) 5×5のサイズなら、5と設定しましょう |
第3引数 | ガウス関数の標準偏差(距離) |
第4引数 | ガウス関数の標準偏差(輝度値の差) |
第3、第4引数の標準偏差は、先ほど説明したバイラテラルフィルタの数式のσに当てはまります。
サンプルコードは以下です。
# sample code import cv2 # 入力画像の読み込み img = cv2.imread("Text.bmp", cv2.IMREAD_GRAYSCALE) cv2.imshow('org_img', img) # バイラテラルフィルタ bilateral = cv2.bilateralFilter(img, 9, 50, 50) cv2.imshow('bilateral', bilateral) cv2.waitKey(0) cv2.destroyAllWindows()
以上で解説は終わりです。
これからPythonを使って画像処理を始めたい方、さらに知識を深めていきたい方は、動画コンテンツで学ぶことをおすすめします。
私もよく利用しますので、さいごに紹介だけして終わります。
画像処理の基礎:フィルタリング,パターン認識から撮像過程モデルまで
入門としてはこのあたりがおすすめです。
参考までに。