つれづれなる備忘録

日々の発見をあるがままに綴る

OpenCVの使い方33 ~ 画像のフーリエ変換

今回は画像のフーリエ変換とそれを応用したフィルタ処理について紹介する。

1. 画像の読み込みとフーリエ変換

まず読み込んだ画像をNumpyを用いたフーリエ変換を行う。処理の詳細解説は以下を参照。

OpenCVの基底を使った画像の変換 — OpenCV-Python Tutorials 1 documentation

import cv2
import numpy as np
from matplotlib import pyplot as plt

orig = cv2.imread(uploaded_file_name)
src = cv2.cvtColor(orig, cv2.COLOR_BGR2RGB)
gray = cv2.cvtColor(orig, cv2.COLOR_BGR2GRAY)
plt.imshow(gray)
plt.title('Original')

画像は標準画像データベースのモノクロ画像からビルディングを使用する。

"ビルディング"
ビルディング

次に読み込んだモノクロ画像を2次元フーリエ変換する、Numpyを用いてフーリエ変換する場合はnp.fft.fft2を用いる。フーリエ変換された係数f複素数となる。実数を表示するにはnp.real(),虚数np.imag(),大きさはnp.abs()を用いることでプロットが可能となる。さらに2次元フーリエ変換されたfは行列であり、行列の4隅が低周波数であるため処理しやすくするためnp.fft.fftshift()とすると行列中央が低周波数の領域で、行列の端に行くほど高周波数になる。

f=np.fft.fft2(gray)
fshift=np.fft.fftshift(f)
magnitude=20*np.log(np.abs(fshift))
plt.figure(figsize=(7,14))
plt.subplot(1,2,1)
plt.imshow(20*np.log(np.abs(f)),cmap='gray')
plt.title('FFT magnitute')
plt.subplot(1,2,2)
plt.imshow(magnitude,cmap='gray')
plt.title('fshift')

以下fの大きさ(対数表示)とnp.fft.fftshift()を施したmagnituteを比較すると4隅の低周波数成分が中央に集まっていることがわかる。

"fftshiftの効果"
fftshiftの効果

2. フーリエ変換によるフィルタ処理

画像を2次元フーリエ変換したフーリエ係数を加工して逆2次元フーリエ変換することで、フィルタ処理を行うことができる。 fftshiftしたフーリエ係数fshiftの中心から30までの範囲の係数を0にする。

rows, cols = gray.shape
crow,ccol = int(rows/2) , int(cols/2)
fshift[crow-30:crow+30, ccol-30:ccol+30] = 0
f_ishift = np.fft.ifftshift(fshift)
plt.figure(figsize=(3,3))
plt.imshow(np.abs(fshift),cmap='gray')

加工しフーリエ係数を表示する。

"加工したフーリエ係数"
加工したフーリエ係数

fftshiftしたフーリエ係数をnp.fft.ifftshift()により4隅に低周波数成分をもつ行列に戻してから、逆2次元フーリエ変換: np.fft.ifft2()を適用し、最後に逆フーリエ係数img_backの絶対値をとることで画像を表示できる。今回はフーリエ係数の低周波数の成分を0にしたため、高周波数成のみの画像となり、ハイパスフィルタ処理を施したものに相当する。

f_ishift = np.fft.ifftshift(fshift)
img_back = np.fft.ifft2(f_ishift)
img_back = np.abs(img_back)
plt.figure(figsize=(7,14))
plt.subplot(1,2,1)
plt.imshow(gray,cmap='gray')
plt.title('original')
plt.subplot(1,2,2)
plt.imshow(img_back,cmap='gray')
plt.title('FFT highpass  filter')

"逆フーリエ変換によるフィルタ処理"
フーリエ変換によるフィルタ処理

3. OpenCVによるフーリエ変換処理

 Numpyのフーリエ変換OpenCVの関数を用いて実行することもできる。ただし、Numpyのfftと勝手が異なり画像はfloat32にすることやフラグの設定が必要で、得られる結果は実数と虚数の2チャンネルの行列になる。fftshifについてはNumpyのものを使用する。

dft = cv2.dft(np.float32(gray),flags = cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
magnitude_cv = 20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))
plt.figure(figsize=(3,3))
plt.imshow(magnitude_cv,cmap='gray')

OpenCVによるフーリエ変換"
OpenCVによるフーリエ変換

今回は中心から30まで1として残りを0にするマスクをフーリエ変換係数に適用する。画像の低周波数成分を残し、高周波数成分をカットするローパスフィルタに相当する。OpenCVの逆フーリエ変換の関数はcv2.idf()を使用する。

mask = np.zeros((rows,cols,2),np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 1

fshift_cv = dft_shift*mask
f_ishift_cv = np.fft.ifftshift(fshift_cv)
img_back_cv = cv2.idft(f_ishift_cv)
img_back_cv = cv2.magnitude(img_back_cv[:,:,0],img_back_cv[:,:,1])

plt.figure(figsize=(7,14))
plt.subplot(1,2,1)
plt.imshow(gray,cmap='gray')
plt.title('original')
plt.subplot(1,2,2)
plt.imshow(img_back_cv,cmap='gray')
plt.title('FFT lowpass  filter')

"OpenCVのフーリエ変換によるローパス処理"
OpenCVフーリエ変換によるローパス処理

4. まとめ

今回は画像をフーリエ変換したフィルタ処理の方法と、OpenCVの関数を用いたフーリエ変換について紹介した。