置き換えの処理の部分を高速化したいとのことなので、その部分だけ対象として、次のような実装が考えられます。
(cv2.imread(file_path)
) の部分は除いています。)
①for-loop と if 文を除く:
Python
1import cv2, math
2import scipy.stats as sstats
3
4
5def func1(bgr):
6 """for-loop と if 文を除く
7 """
8 b, g, r = cv2.split(bgr.astype(int))
9 mode_r = sstats.mode(r[r.nonzero()])[0][0]
10 mode_g = sstats.mode(g[g.nonzero()])[0][0]
11 mode_b = sstats.mode(b[b.nonzero()])[0][0]
12 bgr[(mode_r - r)**2 + (mode_g - g)**2 + (mode_b - b)**2 < 100., :] = [0, 0, 0]
13 return bgr
②numba.jit を使用する:
Python
1import cv2
2import scipy.stats as sstats
3import numpy
4import numba
5
6
7@numba.jit("void(u1[:,:,:], i4[:,:], i4[:,:], i4[:,:], i4, i4, i4)")
8def func2_numba(bgr, b, g, r, mode_b, mode_g, mode_r):
9 """numba.jit を適用する処理
10 """
11 for i in range(b.shape[0]):
12 for j in range(b.shape[1]):
13 if (mode_r - r[i, j])**2 + (mode_g - g[i, j])**2 + (mode_b - b[i, j])**2 < 100:
14 bgr[i, j] = [0, 0, 0]
15
16
17def func2(bgr):
18 """numba.jit を使用する
19 """
20 b, g, r = cv2.split(bgr.astype(numpy.int32))
21 mode_b = sstats.mode(b[b.nonzero()])[0][0]
22 mode_g = sstats.mode(g[g.nonzero()])[0][0]
23 mode_r = sstats.mode(r[r.nonzero()])[0][0]
24 func2_numba(bgr, b, g, r, mode_b, mode_g, mode_r)
25 return bgr
オリジナルの実装を func0
としておきます(実装は次の通り):
Python
1import cv2
2import scipy.stats as sstats
3import numpy
4
5
6def func0(bgr):
7 """オリジナルの実装
8 """
9 imax = bgr.shape[0]
10 jmax = bgr.shape[1]
11
12 rgb = bgr[:, :, [2, 1, 0]]
13 r, g, b = cv2.split(rgb)
14 mode_r = sstats.mode(r[r.nonzero()])[0][0]
15 mode_g = sstats.mode(g[g.nonzero()])[0][0]
16 mode_b = sstats.mode(b[b.nonzero()])[0][0]
17 for i in range(imax):
18 for j in range(jmax):
19 mr = (int(mode_r) - r[i, j])**2
20 mg = (int(mode_g) - g[i, j])**2
21 mb = (int(mode_b) - b[i, j])**2
22 if mr + mg + mb < 100.:
23 bgr[i, j]=[0, 0, 0]
24 return bgr
手元の PC (Core i7-10510U, 8 GB RAM) で Jupyter Notebook の %timeit
を利用したベンチマークは次の通りです。
処理速度は ② > ① >> original となりました。
※関数のコンパイル時間は除いています。
Python
1%timeit func0(bgr) # 1.84 s ± 46 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
2%timeit func1(bgr) # 34.6 ms ± 1.16 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
3%timeit func2(bgr) # 30.8 ms ± 158 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
以下、処理速度向上に関するコメントです。
Python のコードの高速化方法はいろいろあります。例えば
- 対象とする処理コードを関数にする。
- 上記関数を含むモジュールを実装する。
- cython を使って処理を定義する。
- numba.jit を使って関数をジャスト・イン・コンパイルする。
- 処理部分を他の高速な言語(C/C++, Rust, etc.)で定義し、pybind11 などでPython でも使用できる形に実装する。
などがあります。それぞれ一長一短があります。これらは検索すればすぐに出てくるので、ぜひ調べてみてください。