チーム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()
画像
元の画像
グレースケール化した画像
フーリエ変換した画像
上記の画像で使用したフィルター
カメラ撮影した画像
カメラ画像をフーリエ変換した画像
上記の画像で使用したフィルター
考察
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