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隅の低周波数成分が中央に集まっていることがわかる。
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')
今回は中心から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')