【画像処理】空間フィルタリングとは?

プログラミング

空間フィルタリングとは、ある画素の値を、その画素と周囲の画素を使って計算し直す画像処理です。周囲を見ながら1画素ずつ塗り替えていくイメージで、ぼかし・輪郭抽出・シャープ化といった処理は、すべてこの同じ仕組みで実現できます。

こんにちは!zhackです。

今回は画像処理でよく使われる空間フィルタリングについて紹介します。仕組みを図で追いかけたあと、フィルタ(カーネル)の中身、画像の端はどう処理するのか、線形と非線形の違い、そしてディープラーニングのCNNとの関係まで、順に見ていきます。

空間フィルタリングとは?

あらためて言葉にすると、こうなります。

画像中の注目している画素に対して、その画素とその周囲の画素から、注目している画素の値を変換する処理

この一言で伝わりましたでしょうか、、?

絵で示したほうがわかりやすいかもしれませんね。

なお、このとき使う「重みの並んだ小さな表」のことをフィルタ、またはカーネルと呼びます。どちらも同じものを指すので、調べるときは両方の言葉で検索してみてください。

具体的にどのような処理なのか

処理対象の画像と、フィルタを用意しました。
こんなイメージです。

処理対象の画像と3×3のフィルタ(カーネル)を並べた図。フィルタ中央の注目画素が濃い黄色で示されている

フィルタの濃い黄色のところが注目画素のフィルタになります。
フィルタ処理を行う際、このフィルタを以下のように動かします。

フィルタを画像の左上から右下へ1画素ずつずらしながら走査していく様子を示した図

フィルタを処理対象画像に重ね、上の図のように、処理対象画像の全画素がフィルタの注目画素に重なるように1画素ずつずらしていきます。

このずらしていく過程で、数値計算を行い、処理対象画像を変換します。

例えば、以下のように重なっている場合の計算の様子はこんな感じです。

注目画素とその周囲8画素に、フィルタの対応する重みをかけ合わせる様子を示した図 画素値と重みの積をすべて足し合わせて、注目画素の新しい値を求める計算式

やっていることは、「重なった画素の値とフィルタの数値をそれぞれ掛けて、全部足す」だけです。これを画像の左上から右下まで、1画素ずつ場所をずらしながら繰り返します。

なんとなくイメージできましたでしょうか。

ちなみに、このような「掛けて足す」計算処理のことを畳み込み演算(convolution)と呼びます。

キーワードとして覚えておくと、調べたときに役に立つと思います。

フィルタ(カーネル)について

今回の例では、3×3の注目画素の左右の画素が1、他は0であるフィルタを用いました。

このフィルタの中身の数値を変えることで、いろんな処理を行うことができます。

フィルタにより可能な処理の一覧はこんな感じです~。

  • 平滑化(ぼかし)
  • 輪郭抽出(エッジ検出)
  • 鮮鋭化(シャープ化)

順に説明していきます。図だけだと数値が拾いにくいので、代表的なフィルタの中身も表で載せておきますね。

平滑化

平滑化の名前の通り、全体的に画素の値をならすような処理をします。

結果として表れる画像は、全体的にボケた感じの画像になります。

フィルタの例として、このようなフィルタになります。

平滑化フィルタの例。3×3のすべてのマスに同じ重みが入っている

代表的なのが平均化フィルタで、3×3のすべてのマスに 1/9 を入れたものです。周囲9画素の平均を取っているだけ、と考えるとわかりやすいと思います。

3×3の平均化フィルタ
1/9 1/9 1/9
1/9 1/9 1/9
1/9 1/9 1/9

中央ほど重みを大きくして、より自然なぼかしにしたものがガウシアンフィルタです。ノイズ除去の前処理などでよく使われます。

輪郭抽出

画像中に写っている境界線を抽出します。

エッジ検出ですね。

フィルタの例として、こういうフィルタが考えられます。

輪郭抽出(エッジ検出)フィルタの例。中央が負の大きな値、周囲が正の値になっている

代表的なものにラプラシアンフィルタがあります。

ラプラシアンフィルタの例
0 1 0
1 -4 1
0 1 0

周囲との差を取っているので、色が急に変わる場所(=輪郭)だけ大きな値になり、平坦な場所は0に近くなります。縦方向・横方向のエッジを別々に取り出したい場合は、ソーベルフィルタがよく使われます。

鮮鋭化

画像中のエッジが強調される処理です。

エッジを抽出するのではなく、強調されます。

フィルタの例として、このようなフィルタが挙げられます。

鮮鋭化フィルタの例。中央が大きな正の値、上下左右が負の値になっている

鮮鋭化フィルタの例
0 -1 0
-1 5 -1
0 -1 0

元の画像に、輪郭抽出の結果を足し込んでいるようなイメージです。輪郭が際立ってクッキリした印象になります。

フィルタの数値の合計はいくつにするのか

フィルタを自分で作るときに迷いやすいのが、数値の合計をいくつにするか、です。

画素の値は0〜255で表現されているので、フィルタの数値の合計が1より大きいと、計算結果がこの範囲を超えて白飛びしてしまいます。そのため画像の明るさを変えたくない処理では、フィルタ内の数値の合計が1になるように調整します

先ほどの平均化フィルタ(1/9が9個で合計1)や鮮鋭化フィルタ(0-1+0-1+5-1+0-1+0 = 1)が、これにあたります。

ただし輪郭抽出のような微分系のフィルタは、合計が0になります。上のラプラシアンフィルタも 0+1+0+1-4+1+0+1+0 = 0 ですね。

こちらは「明るさを保つ」ことが目的ではなく「変化量を取り出す」ことが目的なので、平坦な場所が0(=真っ黒)になるのが正しい挙動です。結果を画像として見たいときは、絶対値を取ったり、128などのオフセットを足したりして表示します。

合計1は絶対のルールではなく、「明るさを保ちたいかどうか」で決まると覚えておくとスッキリすると思います。

画像の端はどう処理するのか

図を見ていて「画像のいちばん端の画素はどうするの?」と思った方、鋭いです。

3×3のフィルタを端の画素に重ねると、フィルタの一部が画像からはみ出してしまいます。この対処法にはいくつかやり方があります。

  • 端を処理しない:はみ出す部分は計算せず、出力画像を一回り小さくする。いちばん単純
  • 0で埋める(ゼロパディング):画像の外側を0とみなす。実装が楽だが、端が暗くなりやすい
  • 端の画素を複製する:いちばん外側の画素をコピーして外側に伸ばす。端の不自然さが出にくい
  • 折り返す(ミラーリング):画像を鏡のように折り返して埋める。写真の処理でよく使われる

どれを選んでも大きく結果が変わるわけではありませんが、端が黒く縁取られたような画像になったときは、まずパディングを疑ってみてください。

線形フィルタと非線形フィルタ

ここまで紹介したのは、すべて線形フィルタです。掛けて足すだけ、という単純な計算でしたね。

一方で、掛けて足す以外の計算をする非線形フィルタもあります。代表がメディアンフィルタで、こちらは周囲の画素を並べて真ん中の値(中央値)を選ぶという処理をします。

これが効くのが、ゴマ塩ノイズのような「ポツポツと極端な値が乗るノイズ」です。平均を取る平滑化だと極端な値に引っ張られてしまいますが、中央値なら極端な値は選ばれないので、輪郭をぼかさずにノイズだけ消せます。

「ぼかしたらノイズは消えたけど輪郭までボケた」というときは、メディアンフィルタを試してみてください。

空間フィルタリングとCNNの関係

実はこの空間フィルタリング、ディープラーニングの画像認識で使われるCNN(畳み込みニューラルネットワーク)と、やっていることはほぼ同じです。

違いはフィルタの数値を誰が決めるかだけ、と言ってもいいくらいです。

  • 空間フィルタリング:人間が「輪郭を出したい」と考えてフィルタの数値を設計する
  • CNN:フィルタの数値を、大量の画像から学習して自動で決める

CNNの中身を覗くと、浅い層では今回紹介したエッジ検出とそっくりなフィルタが出てきます。画像処理の古典的な手法を知っていると、ディープラーニングの理解がぐっと早くなるのはこのためですね。

CNNについては別の記事で詳しく解説しているので、興味があればこちらもどうぞ。

CNNを窓拭きで理解する--画像認識AIが「見ている」もの
畳み込みニューラルネットワーク(CNN)のフィルタ・プーリング・特徴マップを、窓を小さな雑巾で拭いていく動作に例えて視覚的に解説する。

よくある質問

空間フィルタリングと畳み込みは何が違うのですか?

ほぼ同じ意味で使われます。厳密には「畳み込み」は計算方法そのものの名前、「空間フィルタリング」はその計算を画像に対して行う処理の名前、という使い分けです。日常的にはどちらで呼んでも通じます。

フィルタのサイズは3×3以外もありますか?

あります。5×5や7×7もよく使われます。サイズを大きくするほど広い範囲を見るので効果は強くなりますが、計算量も増え、細部が失われやすくなります。まずは3×3から試すのがおすすめです。

フィルタは正方形でないといけませんか?

いいえ。縦方向だけ、横方向だけを見る1×3や3×1のフィルタもよく使われます。ソーベルフィルタのように、縦と横を別々に計算して後で合成する手法もあります。

さいごに

画像の空間フィルタリングについて、紹介しました。

ポイントを整理すると、こんな感じです。

  • 注目画素とその周囲の画素から、注目画素の値を計算し直す処理
  • 計算の中身は「掛けて足す」だけ(=畳み込み演算)
  • フィルタの数値を変えれば、ぼかし・輪郭抽出・シャープ化と用途が変わる
  • 明るさを保ちたいならフィルタの合計は1、変化量を取りたいなら0
  • CNNは、このフィルタの数値を学習で自動的に決めているもの

実際に、空間フィルタリングを実装した記事もよろしければご参考ください。

【画像処理】Go言語で空間フィルタリングいろいろやってみた【サンプルコード付】
こんにちは!zhackです。今日は、いろんな空間フィルタリングをやっていきます。先日のプログラムの大半を変えることなく、変数の値を変えるだけでいろんな画像処理ができます!ちなみに、空間フィルタリングに関する解説記事もございますので、そちらも...

平滑化と輪郭抽出については、それぞれ単体の実装記事もあります。

【画像処理】Go言語で平滑化処理【サンプルコード付】
【画像処理】Go言語で輪郭抽出!X方向Y方向微分【サンプルコード付き】

そもそも画像がプログラムの中でどう表現されているかは、【画像処理】画像はプログラム内部でどのように表現されているのかで解説しています。

ではでは!

コメント

  1. […] 先日、フィルタ処理に関する記事を掲載しましたが、 その中の輪郭抽出である、X方向及びY方向の微分について、実装してみました。 […]

  2. […] […]

  3. […] […]

タイトルとURLをコピーしました