🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

1557閲覧

python:画素の置き換えの計算速度向上

grandchild

総合スコア10

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

1グッド

1クリップ

投稿2021/01/16 04:34

編集2021/01/16 09:50

解析画像
オリジナル結果(閾値20000の場合)
Python初心者です。
画像で黒以外の似たような背景色を全部黒にするプログラムを作っています。
典型的な色として黒以外の画素の最頻値を使っています。
いろいろと苦労してプログラムは動くようになったのですが、
計算速度が遅いです。早くする方法があればご教示ください。
なお、上の画像は閾値20000の場合です。
なお、Kirisakiさんの結果はこちらでした。
イメージ説明

該当のソースコード

import cv2, math
import scipy.stats as sstats

bgr=cv2.imread('A.tif') #BGR
imax=bgr.shape[0]
jmax=bgr.shape[1]

rgb = bgr[:, :, [2, 1, 0]]
r,g,b=cv2.split(rgb)
mode_r=sstats.mode(r[r.nonzero()])[0][0]
mode_g=sstats.mode(g[g.nonzero()])[0][0]
mode_b=sstats.mode(b[b.nonzero()])[0][0]
for i in range(imax):
for j in range(jmax):
mr=((int(mode_r)-r[i,j])**2)

mg=((int(mode_g)-g[i,j])**2) mb=((int(mode_b)-b[i,j])**2) if(mr+mg+mb < 20000.): bgr[i,j]=[0,0,0] cv2.imwrite("B.jpg",bgr)

試したこと

for文とif文を使わずに
bgr[mr2 + mg2 + mb**2 < 100.]=[0,0,0]
を試したら早く計算が終わったのですが、意図した結果になりませんでした。

A_kirisaki👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

A_kirisaki

2021/01/16 05:02

r と b と g はどこから来ましたか?
grandchild

2021/01/16 05:05

r,g,b=cv2.split(rgb) 申し訳ございません。上の通りです。入力が抜けておりました・・。
guest

回答2

0

ベストアンサー

置き換えの処理の部分を高速化したいとのことなので、その部分だけ対象として、次のような実装が考えられます。
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 のコードの高速化方法はいろいろあります。例えば

  1. 対象とする処理コードを関数にする。
  2. 上記関数を含むモジュールを実装する。
  3. cython を使って処理を定義する。
  4. numba.jit を使って関数をジャスト・イン・コンパイルする。
  5. 処理部分を他の高速な言語(C/C++, Rust, etc.)で定義し、pybind11 などでPython でも使用できる形に実装する。

などがあります。それぞれ一長一短があります。これらは検索すればすぐに出てくるので、ぜひ調べてみてください。

投稿2021/01/16 08:55

編集2021/01/16 09:43
Surpris

総合スコア106

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

grandchild

2021/01/16 09:41

ご回答ありがとうございます。とても勉強になりました。 速度もとても早くなりましたし、結果も同じでしたので、ベストアンサーとさせていただきました。 Kirisakiさんのご回答もとても勉強になりました。ありがとうございました。
guest

0

こんな感じでどうでしょうか!for ループをなくし行列演算だけにしました。もし結果が異なるようであればサンプル画像を上げてもらえれば幸いです。

Python

1import numpy as np 2import cv2 3import scipy.stats as sstats 4import matplotlib.pyplot as plt 5 6bgr = cv2.imread('image/Lenna.png') 7rgb = bgr[:, :, [2, 1, 0]] 8 9r, g, b = cv2.split(rgb) 10 11mode_b = sstats.mode(b[b.nonzero()])[0][0] 12mode_g = sstats.mode(g[g.nonzero()])[0][0] 13mode_r = sstats.mode(r[r.nonzero()])[0][0] 14 15mode = np.array([mode_b, mode_g, mode_r], dtype=int) 16 17a = (mode - bgr)**2 18b = a[:, :, 0] + a[:, :, 1] + a[:, :, 2] 19c = (b >= 100).astype(int) 20cs = np.stack([c] * 3, axis=2) 21result = bgr * cs 22 23plt.imshow(result) 24plt.show()

投稿2021/01/16 08:48

A_kirisaki

総合スコア2853

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

grandchild

2021/01/16 09:30 編集

ご回答ありがとうございます。とても早くなりました!ただ、青いバラになってしまいました・・・それはそれで、とても綺麗でしたが・・・。
Surpris

2021/01/16 09:40

`matplotlib.pyplot.imshow(src, **kwargs)` は入力データ `src` が3次元の場合、`(height, width, channels)` の shape を持つ RGB 画像での入力が想定されています。 従って BGR 画像を `pyplot.imshow` で正しく表示したい場合は、RGB 画像に変換して入力する必要があります: ```Python rgb = bgr[:, :, [2, 1, 0]] plt.imshow(rgb) ```
grandchild

2021/01/16 09:43

青いバラになったのはKirisakiさんの方でした。ありがとうございました。
A_kirisaki

2021/01/16 09:48

まあ保存する時 RGB にするっぽいしええんちゃうん?とか雑に思ってました。
grandchild

2021/01/16 09:56

そうですね。私の知識では、今のところ、どちらの方が内容的にベストアンサーなのか分からないのもあって、申し訳ございません。でも、ありがとうございました。
Surpris

2021/01/16 10:20

(A_kirisaki 様のコメントを受けて) 言葉が足らず誤解を与えたようですみません。 一つ前の私のコメントは、「`pyplot.imshow` で青色に表示されてしまったのはなぜか」という疑問に対する回答にすぎません。 A_kirisaki 様のご回答にある置き換え処理はご質問者様の要望(処理速度の向上)に合致していると私も思います。
grandchild

2021/01/17 00:03

Surprisさんのご回答は自分が試したことの間違いの理由も気づかせてくれましたし、今後処理速度向上について勉強する上での参考になることも書かれており、結果も同じだったので選ばせていただきました。Kirisakiさんの方法は目からうろこのエレガントなご回答でした。お二方に感謝申し上げます。失礼いたします。
grandchild

2021/01/17 00:52

あ、すみません。Kirisakiさんの結果は赤くしても微妙に結果が違いました。何故だかは分かりませんが・・。
grandchild

2021/01/17 00:58

あ、重ね重ねすみません。全く同じになることが確認されました。
grandchild

2021/01/17 01:00

↑間違ってpngで出力してました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問