この記事では、Pythonライブラリ「Numba」でNumPy配列とfor文の処理速度を高速化してみたので紹介します。
Numbaで高速化
Python用数値計算ライブラリ「NumPy」の配列の要素にfor文でアクセスすると、処理速度が急低下する問題があります。
for文を使わずに「NumPyのメソッド」や「他のライブラリ」で処理を実装するのが鉄則ですがどうしてもfor文を使いたい場合があります。
そんなときは「Numba」ライブラリを使うことで元のソースコードをほとんど弄らずに高速化できます。
ソースコード
サンプルプログラムのソースコードです。
NumPy
#-*- coding:utf-8 -*- import cv2 import numpy as np import time def filter2d(src, kernel, fill_value=-1): # カーネルサイズ m, n = kernel.shape # 畳み込み演算をしない領域の幅 d = int((m-1)/2) h, w = src.shape[0], src.shape[1] # 出力画像用の配列 if fill_value == -1: dst = src.copy() elif fill_value == 0: dst = np.zeros((h, w)) else: dst = np.zeros((h, w)) dst.fill(fill_value) for y in range(d, h - d): for x in range(d, w - d): # 畳み込み演算 dst[y][x] = np.sum(src[y-d:y+d+1, x-d:x+d+1]*kernel) return dst def main(): # 入力画像をグレースケールで読み込み gray = cv2.imread("input2.jpg", 0) # 処理開始時間の計測 start = time.time() # カーネル(縦方向の輪郭検出用) kernel = np.array([[1/9, 1/9, 1/9], [1/9, 1/9, 1/9], [1/9, 1/9, 1/9]]) # 方法1(NumPyで実装) dst1 = filter2d(gray, kernel, -1) # 処理終了時刻の計測 end = time.time() # 処理時間の表示 print("処理時間" + str(end-start) + "[sec]") # 結果を出力 cv2.imwrite("output1.jpg", dst1) if __name__ == "__main__": main()
NumPy+Numba
#-*- coding:utf-8 -*- import cv2 import numpy as np import time import numba @numba.jit def filter2d(src, kernel, fill_value=-1): # カーネルサイズ m, n = kernel.shape # 畳み込み演算をしない領域の幅 d = int((m-1)/2) h, w = src.shape[0], src.shape[1] # 出力画像用の配列 if fill_value == -1: dst = src.copy() elif fill_value == 0: dst = np.zeros((h, w)) else: dst = np.zeros((h, w)) dst.fill(fill_value) for y in range(d, h - d): for x in range(d, w - d): # 畳み込み演算 dst[y][x] = np.sum(src[y-d:y+d+1, x-d:x+d+1]*kernel) return dst def main(): # 入力画像をグレースケールで読み込み gray = cv2.imread("input2.jpg", 0) # 処理開始時間の計測 start = time.time() # カーネル(縦方向の輪郭検出用) kernel = np.array([[1/9, 1/9, 1/9], [1/9, 1/9, 1/9], [1/9, 1/9, 1/9]]) # 方法1(NumPyで実装) dst1 = filter2d(gray, kernel, -1) # 処理終了時刻の計測 end = time.time() # 処理時間の表示 print("処理時間" + str(end-start) + "[sec]") # 結果を出力 cv2.imwrite("output1.jpg", dst1) if __name__ == "__main__": main()
OpenCV
#-*- coding:utf-8 -*- import cv2 import numpy as np import time def main(): # 入力画像をグレースケールで読み込み gray = cv2.imread("input.jpg", 0) # 処理開始時間の計測 start = time.time() # 方法3(OpenCVで実装) dst3 = cv2.blur(gray, ksize=(3,3)) # 処理終了時刻の計測 end = time.time() # 処理時間の表示 print("処理時間" + str(end-start) + "[sec]") # 結果を出力 cv2.imwrite("output3.jpg", dst3) if __name__ == "__main__": main()
実行結果
結果は以下の通りになりました。
■実行環境
項目 | 説明 |
---|---|
入力画像 | グレースケール画像(440×450[px]) |
OS | Windows10 Home Premium 64bit |
メモリ容量 | 4GB |
CPU | Core i3-2330M 2.20GHz |
■結果
– | 処理速度[sec] |
---|---|
NumPy | 3.2753376960754395[sec] |
NumPy+Numba | 1.225313425064087[sec] |
OpenCV | 0.13230109214782715[sec] |
Numbaを使うと3倍くらい速くなりました。
ただし、今回の画像処理の場合ではOpenCVの方がさらに10倍程度速くなりました。
NumbaはGPUを使うなど色々な設定が出来るので、もうちょっと弄ればより速くなると思います。
おすすめ記事
PythonでOpenCV入門 サンプル集
【Python】画像処理プログラミング入門
【画像処理入門】アルゴリズム&プログラミング
コメント