チーム19F3

課題名

FFTによる画像のフィルター処理

研究者名

3年15組 Toda Kosuke
3年15組 Ota Ryo

開発環境

デバイス名 : Raspberry Pi 3 Model B
CPU : Broadcom BCM2837 [1.2GHz 64-bit quad-core ARMv8 Cortex-A53]
メモリ : 1GB
電源定格 : DC 5V
消費電流 : 1.3A(Typ)


OS情報
Distributor ID: Raspbian
Description: Raspbian GNU/Linux 10 (buster)
Release: 10
Codename: buster


Pythonバージョン
Python 3.7.3

使用したWebカメラ
Logicool Webカメラ C525

概要

画像をグレースケール化し、その処理後の画像に対してフーリエ変換を行う。
まず、バンドパスフィルターを作成する。コマンドライン上で画像名、閾値(0以上)2つを引数として与え、make_filterプログラムを実行する。

python3 make_filter.py 画像ファイル名 閾値(下限) 閾値(上限)
(例)
python3 make_filter.py img.png 50 100


これを実行すると、npz形式でバンドパスフィルターが保存される。得られたフィルターは中央が低周波成分、外側が高周波成分となっている。このフィルターは画像の(height x width)のndarray型配列であり、黒を0、白を1とし、0~1の値が格納されている。これを対象の画像にかけるだけでマスク可能である。

次に画像をフーリエ変換し、その結果と作成されたフィルターを掛け合わせ、逆フーリエ変換を行う。これで、バンドパスフィルターを施した画像を得ることができる。

python3 fft.py 画像ファイル名 フィルター名(.npz形式)
(例)
python3 fft.py img.png img_50_100.npz

さらに、画像をWeb Cameraから画像を取得するプログラムも作成した。プログラムを実行するとモニターにWeb Cameraの映像が映し出され、キーボードの'c'キーで撮影できる。この画像のサイズは640 x 480とした。なお、強制終了する場合はESCキーで終了できる。引数は保存するときの画像名である。

python3 take_photo.py 画像名
(例)
python3 take_photo.py camera

<参考文献>
このプログラムを作成するにあたり、以下のサイトを参考とした。(2019/12/16)
実験系研究者のための画像処理技術

Raspberry Pi3 Model B とWebカメラで遠隔撮影

ソースコード(フーリエ変換・逆フーリエ変換)

# -- cording utf-8 -- #
import numpy as np
import cv2
import matplotlib.pyplot as plt
import math
import time
import sys


def BandPassFilter(img, filter_matrix, isShowImage):

  fourier_start = time.time()
   #フーリエ変換
   ifimg = np.fft.fft2(img)
   ifimg = np.fft.fftshift(ifimg)

  #逆フーリエ変換
   ifimg = ifimg*filter_matrix
   ifimg = np.fft.fftshift(ifimg)
   ifimg = np.fft.ifft2(ifimg)
   ifimg = ifimg.real
   ifimg = ifimg - np.min(ifimg)
   ifimg = (ifimg/np.max(ifimg)) * 255

  fourier_finish = time.time()

  fourier_time = round(fourier_finish - fourier_start, 4)

  print("フーリエ変換にかかった時間 : "+str(fourier_time))
   
   if(isShowImage):
       fig = plt.figure()
       plt.title("Result")
       plt.imshow(ifimg.astype(np.uint8))
       plt.colorbar()
       plt.gray()
       plt.savefig("result.png")
       plt.show()

  return ifimg.astype(np.uint8)


def main():
  args = sys.argv
   img_name = args[1]
   filter_name = args[2]
   img = cv2.imread(img_name, 0)
   outfile = np.load(filter_name)
   bpfilter = outfile["bpfilter"]
   
   bpf_img = BandPassFilter(img, bpfilter, True)


if __name__ == "__main__":
  main()

ソースコード(バンドパスフィルター)

# -- cording utf-8 -- #
import numpy as np
import cv2
import matplotlib.pyplot as plt
import math
import time
import sys

def print_progress(now, fin):

  if now == int(fin * 0.1):
       print("progress[**__________________]")
   elif now == int(fin *0.2):
       print("progress[****________________]")
   elif now == int(fin *0.3):
       print("progress[******______________]")
   elif now == int(fin * 0.4):
       print("progress[********____________]")
   elif now == int(fin * 0.5):
       print("progress[**********__________]")
   elif now == int(fin * 0.6):
       print("progress[************________]")
   elif now == int(fin * 0.7):
       print("progress[**************______]")
   elif now == int(fin * 0.8):
       print("progress[****************____]")
   elif now == int(fin * 0.9):
       print("progress[******************__]")
   elif now == int(fin-1):
       print("progress[********************]")
       print("Complete")
   elif now == 0:
       print("progress[____________________]")

      
def MakeFilter(img, high, low, isShowFilter):

  filter_start = time.time()
   
   #バンドパスフィルターの作成
   h,w = img.shape
   filter_low = np.zeros((h,w))
   filter_high = np.zeros((h,w))
   center = np.array([h/2, w/2], dtype=np.uint16)
   R = high/2
   r = low/2
   for i in range(0,w):
       print_progress(i,w)

      for j in range(0,h):
           dist_R = np.sqrt(R * R) - np.sqrt(((i-center[1])*(i-center[1]) + (j-center[0])*(j-center[0])))
           dist_r = -np.sqrt(r * r) + np.sqrt(((i-center[1])*(i-center[1]) + (j-center[0])*(j-center[0])))
           filter_low[j][i] = (1+math.erf(dist_r/(r/2)))/2
           filter_high[j][i] = (1+math.erf(dist_R/(R/2)))/2
   filter_matrix = filter_low + filter_high
   filter_matrix = filter_matrix - np.min(filter_matrix)
   filter_matrix = filter_matrix/np.max(filter_matrix)

  filter_finish = time.time()

  filter_time = round(filter_finish - filter_start, 4)
   print("フィルター作成にかかった時間 : "+str(filter_time))
   
   #isShowFilterがTrueの時はフィルターを表示する。
   if(isShowFilter):
       fig = plt.figure()
       plt.imshow(filter_matrix)
       plt.title("BandPassFilter, high="+str(high)+", low="+str(low))
       plt.colorbar()
       plt.gray()
       fig = plt.figure()
       plt.plot(np.linspace(0, w-1, w), filter_low[int(h/2),:])
       plt.plot(np.linspace(0, w-1, w), filter_high[int(h/2),:])
       plt.plot(np.linspace(0, w-1, w), filter_matrix[int(h/2),:])
       plt.title("2D-plot at y=0, high="+str(high)+", low="+str(low))
       plt.savefig("graph.png")
       plt.show()

  return filter_matrix



def main():
  args = sys.argv
   file_name = args[1]
   high = int(args[2])
   low  = int(args[3])

  img = cv2.imread(file_name, 0)
   
   
   if(high < low):
       tmp = low
       low = high
       high = tmp

  name, img_type = file_name.split(".")
       
   bpfilter = MakeFilter(img, high, low, True)
   np.savez(name+"_"+str(low)+"_"+str(high)+".npz",bpfilter = bpfilter)


if __name__ == "__main__":
  main()

ソースコード(カメラ撮影)

# -- cording utf-8 -- #
import os
import cv2
import time
import sys

def take_photo(name):

  cap = cv2.VideoCapture(0)
   ret, frame = cap.read()

  while (cap.isOpened()):
       ret, frame = cap.read()
       cv2.imshow("USB Camera", frame)
       key = cv2.waitKey(1)
       if key == 27:
           cv2.destroyAllWindows()
           break
       elif key == ord("c"):
           cv2.imwrite(name+".png",frame)
           cv2.destroyAllWindows()
           break

def main():
  args = sys.argv
   name = args[1]
   take_photo(name)


if __name__ == "__main__":
  main()

画像

元の画像
19F3_lena.jpg

グレースケール化した画像
19F3_gray.jpg

フーリエ変換した画像
19F3_result.jpg

上記の画像で使用したフィルター
19F3_filter3.png

カメラ撮影した画像
19F3_otya.jpg

カメラ画像をフーリエ変換した画像
19F3_otyaresult.jpg

上記の画像で使用したフィルター
19F3_filter2.jpg

考察

FFTは計算量が少ないという特徴がある。具体的には、加算量も乗算量もnlog(n)である。この計算量が少ないという特徴に着目し、処理能力が少ないデバイスでも実行できるかどうかを検証した。
そこで、今回の課題はRaspberry Piで行った。Raspberry Piは通常のコンピュータに比べCPUクロック数が低く、メモリも圧倒的に少ない。すなわち非常に性能が低い。しかし、FFTを実行することは容易にできた。
詳細な処理時間を見ると、640x480のサイズの画像で0.5185[sec]、512x512のサイズの画像で0.4492[sec]で実行できた。したがって、Raspberry Piの処理能力でもFFTの実行は容易であった。
今後の展望としては、FFTの実行時間を比較することが挙げられる。処理能力による処理時間の差がどれくらいであるかを比較し、CPUのベースクロック数、スレッド数、さらにメモリの容量に比例しているのかを考察する必要があると考えられる。
さらに、RGB画像に対してもフィルタ処理を施すことが可能だと考えられる。今回の処理はグレースケールで行った。すなわち(height x width x 1)のndarray型を処理したが、RGB画像は(height x width x 3)のndarray型であるため、今回の処理を3重にすれば理論上実装可能である。この場合、得られる画像はどのようになっているのかを考察する必要があると考えられる。

  • 最終更新:2019-12-23 14:20:49

このWIKIを編集するにはパスワード入力が必要です

認証パスワード