画像の平滑化

目的

学習内容:

  • 画像をぼかすための様々なローパスフィルタ
  • 自作したフィルタの適用(2D convolution)

2D Convolution (画像のフィルタリング)

1次元の信号と同様に,画像に対してローパスフィルタ(LPF)やハイパスフィルタ(HPF)によるフィルタリングを適用することができる.LPFはノイズ除去や画像をぼかすために,HPFは画像中のエッジ検出に使われる.

cv2.filter2D() 関数は入力画像とカーネル(フィルタ)の畳み込み(convolution)を計算する.以下に5x5サイズの平均値フィルタに使うカーネルを示す:

$ K = \frac{1}{25} \begin{bmatrix} 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \end{bmatrix}$

このカーネルを使った計算は次のように行われる: それぞれの画素に対し、その画素を中心にした周辺5x5の画素(これをウィンドウと呼ぶ)に対し、全部の画素値の合計を求め、それを25で割る.この処理はウィンドウ内の画素値の平均を取る処理になっている.この計算を全画素に対して適用する(フィルタリングした画像を作る)。次のコードを実行し、結果を見てみよう(コード, 対象画像):

In [3]:
%matplotlib inline
import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('opencv_logo.png')

kernel = np.ones((5,5),np.float32)/25
dst = cv2.filter2D(img,-1,kernel)

plt.figure(figsize=(10,5))
plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(dst),plt.title('Averaging')
plt.xticks([]), plt.yticks([])
plt.show()

画像のぼかし (平滑化)

画像のぼかしは、ローパスフィルタのカーネルを重畳積分することで実現でき,画像中のノイズ除去などに使う.画像中の高周波成分(エッジやノイズ)を消すことで、結果として画像全体がぼける(なお、エッジをぼけさせない画像のぼかし方もある).OpenCVが用意しているのは4種類のぼかし方である.

1. 平均

平均を取るには正規化された箱型フィルタを使う. カーネルの範囲内にある全画素の画素値の平均をとる.cv2.blur()cv2.boxFilter() を使う.ここでカーネルの縦幅,横幅を指定する必要がある.このカーネルに関する詳細についてはドキュメントを参照のこと.3x3の箱型フィルタは以下のようになる:

$\boldsymbol{K} = \frac{1}{9} \begin{bmatrix} 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1 \end{bmatrix}$

Note: 正規化した箱型フィルタを使いたくなければ cv2.boxFilter() 関数の引数に normalize=False を指定すればよい.

5x5の箱型フィルタを使った例を以下に示す:

In [1]:
%matplotlib inline
import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('opencv_logo.png')

blur = cv2.blur(img,(5,5))

plt.figure(figsize=(10,5))
plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(blur),plt.title('Blurred')
plt.xticks([]), plt.yticks([])
plt.show()

2. ガウシアンフィルタ

箱型フィルタの場合カーネル内のフィルタ係数が一様だったのに対して,ガウシアンフィルタは注目画素との距離に応じて重みを変えるガウシアンカーネルを採用している.それにはcv2.GaussianBlur()関数を使う.カーネルの縦幅と横幅(どちらも奇数でなければならない)に加え,ガウシアンの標準偏差値sigmaX(横方向)とsigmaY(縦方向)を指定する必要がある.sigmaXしか指定されない場合は,sigmaYsigmaXと同じして扱われる.どちらの値も0にした場合は,カーネルのサイズから自動的に計算される.ガウシアンフィルタは白色雑音の除去に適している.

cv2.getGaussianKernel() 関数を使えば,ガウシアンフィルタを作成することができる.

先のコードを次のように修正すればガウシアンフィルタを試せる:

In [2]:
%matplotlib inline
import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('opencv_logo.png')

blur = cv2.GaussianBlur(img,(5,5),0)

plt.figure(figsize=(10,5))
plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(blur),plt.title('Blurred')
plt.xticks([]), plt.yticks([])
plt.show()

3. 中央値フィルタ

cv2.medianBlur() 関数はカーネル内の全画素の中央値を計算する.ごま塩ノイズのようなノイズに対して効果的である.箱型フィルタとガウシアンフィルタの出力が原画像中には存在しない画素値を出力するのに対し,中央値フィルタの出力は常に原画像中から選ばれる.そのためごま塩ノイズのような特異なノイズに対して効果的である.ここでカーネルサイズは奇数でなければならない.

この例では原画像に対して50%のノイズを追加した劣化画像に中央値フィルタを適用している (対象画像):

In [5]:
%matplotlib inline
import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('opencv_logo_salted.png')

median = cv2.medianBlur(img,5)

plt.figure(figsize=(10,5))
plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(blur),plt.title('Blurred')
plt.xticks([]), plt.yticks([])
plt.show()