画像のFilter をPython で視覚的に理解する (Gaussian, Edge 抽出).

Python を用いて,画像のFilter を視覚的に理解してみます.コードを載せていますので,実装可能です.

目次

Filter とは

Filter と書きましたが,今回扱うのはその中でもSpatial Filter (空間処理フィルタ)です.
空間処理フィルタとは,生の画像データの特徴をより表現するために,取り扱う画像の画素値とその周辺の画素値を用いて,新たな画像を計算する方法です.

ノイズ除去フィルタ

撮影した画像は,必ず何らかのノイズが含まれています.例えば,真っ白の紙を写真で撮ったとしても,画素値が均一になっているとは限りません.光の当たり方であったりとか,そもそも紙が均一ではなかっただとか.
このようなノイズを最低限抑える,というのがこのノイズ除去フィルタです.

Original 画像と Noised 画像

今回の例では,Original の画像にNoise を加えていますので,もともとの意味とは違いますが,撮影状態によっては右のような画像が得られることがあります.
なお,ノイズに関しては
ImageJ -> Process -> Noise -> Add Noise
によって加えました.

輪郭抽出フィルタ

画像の特徴量(例えばある部分の面積,輪郭線の長さ,曲率など)は,主に画像の画素値の境界付近にあることが多いです.画像の画素値の境界がくっきりと区別される場合ならよいのですが,現実ではそういうことはありません.このようなときに,画素値境界を見つける方法の一つが輪郭抽出フィルタです.

Original 画像とEdge画像

Original 画像にEdge 抽出フィルタを施しています.花びらや草などのEdgeが白く表現されていることがわかります.

ImageJ -> Image -> Type -> 8 bit
で白黒にしたあと,

ImageJ -> Process -> Find Edges
によって処理をしています.

実装に関して

Filter を知るため,Python で実装を行っていきましょう.
まずは以下のようなRectangle 画像を読み込みます.
import numpy as np
import dateutil
import cv2, matplotlib
import matplotlib.pyplot as plt

# import rectangle.jpg.
img = cv2.imread('./picture/rectangle.jpg')
img= cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

plt.imshow(img, cmap='gray')
sample.py

これにノイズを付与してみます.
## Add Noise
pix = len(img)
noise = 5*np.random.randn(pix,pix)
img_n = img + noise
plt.imshow(img_n,cmap='gray')
sample.py

平滑化 フィルタ

平滑化フィルタを施してみます.これはノイズ除去に頻繁に用いられるフィルタです.

周辺の画素を重み付きで足し合わせて,新しい画像の画素値を計算します.

早速やってみましょう.
#Gaussian filter 
g = np.array([[1.0/16,1.0/4,1.0/16],[1.0/4,1.0/2,1.0/4],[1.0/16,1.0/4,1.0/16]])
# convolution
img_g = np.zeros([pix, pix])
for n in range(2,pix):
    for m in range(2,pix):
        for gn in range(len(g)):
            for gm in range(len(g)):
                img_g[n,m] = img_g[n,m] + g[gn,gm]*img_n[n - gn, m- gm ]

plt.imshow(img_g, cmap='gray')
sample.py

どうでしょう.確かにノイズが滑らかになっていることが確認できると思います.

なお,このグラフはImageJで線を引いたあと,
Analyze -> Plot Profile (Cmd + K でもOK) によって表示することができます.便利ですね.

Edge 抽出フィルタ

今度はEdge抽出のフィルタを試してみましょう.簡単のため,Horizontal の線を抽出するフィルタを考えます.
これは以下のような行列をたたみ込むことと同じです.なお,fはもともとの画像の画素値です.
\begin{align} h = \begin{pmatrix} 1 & 1& 1\\ 0 & 0 & 0\\ -1 & -1 & -1 \end{pmatrix} \end{align} \begin{align} (f*h)[n,m] = \sum_{i=0}^{N-1} \sum_{j=0}^{M-1} f[n-i,m-j] h[i,j] \end{align}
## Simple image processing

# Horizontal edge detection
h = np.array([[1,1,1],[0,0,0],[-1,-1,-1]])

# convolution
img_h = np.zeros([pix, pix])
for n in range(2,pix):
    for m in range(2,pix):
        for hn in range(len(h)):
            for hm in range(len(h)):
                img_h[n,m] = img_h[n,m] + h[hn,hm]*img[n - hn, m- hm ]

plt.imshow(img_h, cmap='gray')
sample.py

この Filter をたたみ込むと,もともと画素値が変化していないところは0の値になります.
画素値が大きく変化しているところのみを取り出すことが出来るのがこのFilterです.

最後に

ImageJ には多数のフィルタが搭載されており,簡単に画像処理を行うことができます.
ただ,内部でこのようなことが行われている,というのを知ることは非常に大事です.