Pythonによる画像処理100本ノック#9 ガウシアンフィルタ


はじめに

どうも、らむです。
今回は画像中のノイズを除去するガウシアンフィルタを実装します。

9本目:ガウシアンフィルタ

ガウシアンフィルタとは画像の平滑化を行うフィルタです。このフィルタを適用することによって画像全体をぼやかしたような加工ができます。

このフィルタでは注目画素の周辺画素をガウス分布によって重み付けし、フィルタの中心画素に近いほど大きな重みを付けます。ガウス分布による重み付けは以下のように定義されます。

g(x,y) = \frac{1}{2\pi\sigma^2}e^{-\frac{x^2+y^2}{2\sigma^2}}

例えば、3×3や5×5のガウシアンフィルタは以下のフィルタがよく用いられます。おそらく引数のsに$σ=0.85$あたりを指定するとこの値になるかと思います。

$\frac{1}{16}$ $\frac{2}{16}$ $\frac{1}{16}$
$\frac{2}{16}$ $\frac{4}{16}$ $\frac{2}{16}$
$\frac{1}{16}$ $\frac{2}{16}$ $\frac{1}{16}$
$\frac{1}{256}$ $\frac{4}{256}$ $\frac{6}{256}$ $\frac{4}{256}$ $\frac{1}{256}$
$\frac{4}{256}$ $\frac{16}{256}$ $\frac{24}{256}$ $\frac{16}{256}$ $\frac{4}{256}$
$\frac{6}{256}$ $\frac{24}{256}$ $\frac{36}{256}$ $\frac{24}{256}$ $\frac{6}{256}$
$\frac{4}{256}$ $\frac{16}{256}$ $\frac{24}{256}$ $\frac{16}{256}$ $\frac{4}{256}$
$\frac{1}{256}$ $\frac{4}{256}$ $\frac{6}{256}$ $\frac{4}{256}$ $\frac{1}{256}$

注目画素が中心だとすると、周辺画素と対応するフィルタ値の積の総和を注目画素に代入すれば良いですね。
3×3フィルタであれば$I(x_0,y_0)×\frac{1}{16} + I(x_0,y_1)×\frac{2}{16} + ... I(x_2,y_2)×\frac{1}{16}$の値を注目画素に代入します。

また、画像の端部分はフィルタリング処理が行えないので存在しない画素は0を用います。これを0パディングといいます。

ソースコード

gaussianFilter.py
import numpy as np
import cv2
import matplotlib.pyplot as plt


def gaussianFilter(img,k,s):
  w,h,c = img.shape
  size = k // 2

  # 0パディング処理
  _img = np.zeros((w+2*size,h+2*size,c), dtype=np.uint8)
  _img[size:size+w,size:size+h] = img.copy().astype(np.uint8)
  dst = _img.copy()

  # フィルタ作成
  ker = np.zeros((k,k), dtype=np.float)
  for x in range(-1*size,k-size):
    for y in range(-1*size,k-size):
      ker[x+size,y+size] = (1/(2*np.pi*(s**2)))*np.exp(-1*(x**2+y**2)/(2*(s**2)))

  ker /= ker.sum()

  # フィルタリング処理
  for x in range(w):
    for y in range(h):
      for z in range(c):
        dst[x+size,y+size,z] = np.sum(ker*_img[x:x+k,y:y+k,z])

  dst = dst[size:size+w,size:size+h].astype(np.uint8)

  return dst


# 画像読込
img = cv2.imread('image.jpg')

# ガウシアンフィルタ
# 第2引数:フィルタサイズ、第3引数:標準偏差(σ)
img = gaussianFilter(img,21,2)

# 画像保存
cv2.imwrite('result.jpg', img)
# 画像表示
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.show()

 

画像左は入力画像、画像右は出力画像です。
点状のノイズが低減されていることが分かります。

おわりに

もし、質問がある方がいらっしゃれば気軽にどうぞ。
imori_imoriさんのGithubに公式の解答が載っているので是非そちらも確認してみてください。
それから、pythonは初心者なので間違っているところがあっても優しく指摘してあげてください。