この記事では、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】画像処理プログラミング入門
【画像処理入門】アルゴリズム&プログラミング

コメント