質問するログイン新規登録
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

2回答

675閲覧

Pythonでの画像処理の計算速度を改善したい

Ta2S

総合スコア5

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

2グッド

0クリップ

投稿2023/10/15 02:24

2

0

実現したいこと

今手持ちのWindows11+Pythonでの画像処理の計算速度が遅く、実際に新しいパソコンで動作させた場合、現実的な処理速度になるかがわかりません。
現在販売されている新しいパソコンでは、このコードでどのくらいの処理速度が実現できるのか、どのくらい早いパソコンを購入しないといけないのか、知りたいです。
また、プログラムを修正することで、今のパソコンでも処理速度を改善可能かどうか、知りたいです。
よろしくお願いします。

前提

i5-7400のパソコンで開発しています。
高解像度4656x3496(0.1mm程度)のUSBカメラで2回撮影するシート資材写真に写った0.1mm以上の欠点を発見して、OK/NGの表示判定するプログラムを作成しています。
その前段階として下記ソースコードの、GIMPで作成した下記リンクpngの動作チェック用画像に含まれる欠点を黒に変え赤い四角で囲むコードはできたのですが、(画像のRGBの平均を出し、平均から30以上離れた欠点を処理しています)
KRGB107530G50.png
KRGB107530G90.png
いかんせん自分のパソコンでは計算速度が遅く、写真撮影時間も考えると、現実的な処理時間になりません。
処理結果は次のpng画像です。
KRGB107530G90a.png

発生している問題・エラーメッセージ

2023-10-15 09:41:52.733589 ファイルを開く 2023-10-15 09:41:52.745589 平均値算出0 image0.pngのRGB平均値: (127, 127, 127) 2023-10-15 09:41:59.650292 平均値算出1 image1.pngのRGB平均値: (228, 228, 228) 2023-10-15 09:42:06.759561 画素走査と変更0 2023-10-15 09:42:14.034474 画素走査と変更1 2023-10-15 09:42:21.398917 重複を削除 2023-10-15 09:42:21.398917 欠点を四角で囲む 2023-10-15 09:42:21.398917 画像を保存 2023-10-15 09:42:21.806051 終了 画像処理に約19秒かかっており、実際にはLEDの安定化に4秒x2、撮影に1秒x2かかるので、これらだけでも計29秒もかかり現実的ではありません。

該当のソースコード

from PIL import Image, ImageDraw import datetime # 画像を開く ut_now = datetime.datetime.now() print(ut_now, 'ファイルを開く') im0 = Image.open("C:/code/py311/trial/KRGB107530G50.png") im1 = Image.open("C:/code/py311/trial/KRGB107530G90.png") # RGBそれぞれの平均値を算出 ut_now = datetime.datetime.now() print(ut_now, '平均値算出0') r_sum0, g_sum0, b_sum0 = 0, 0, 0 pixels0 = im0.load() for i in range(im0.size[0]): for j in range(im0.size[1]): r, g, b = pixels0[i, j] r_sum0 += r g_sum0 += g b_sum0 += b num_pixels = im0.size[0] * im0.size[1] r_avg0 = int(r_sum0 / num_pixels) g_avg0 = int(g_sum0 / num_pixels) b_avg0 = int(b_sum0 / num_pixels) print(f"image0.pngのRGB平均値: ({r_avg0}, {g_avg0}, {b_avg0})") ut_now = datetime.datetime.now() print(ut_now, '平均値算出1') r_sum1, g_sum1, b_sum1 = 0, 0, 0 pixels1 = im1.load() for i in range(im1.size[0]): for j in range(im1.size[1]): r, g, b = pixels1[i, j] r_sum1 += r g_sum1 += g b_sum1 += b num_pixels = im1.size[0] * im1.size[1] r_avg1 = int(r_sum1 / num_pixels) g_avg1 = int(g_sum1 / num_pixels) b_avg1 = int(b_sum1 / num_pixels) print(f"image1.pngのRGB平均値: ({r_avg1}, {g_avg1}, {b_avg1})") # 画像を走査して条件に合う画素を変更 ut_now = datetime.datetime.now() print(ut_now, '画素走査と変更0') pos_list = [] for i in range(im0.size[0]): for j in range(im0.size[1]): r, g, b = pixels0[i, j] if abs(r - r_avg0) > 30 or abs(g - g_avg0) > 30 or abs(b - b_avg0) > 30: pixels0[i, j] = (0, 0, 0) pos_list.append((i,j)) ut_now = datetime.datetime.now() print(ut_now, '画素走査と変更1') for i in range(im1.size[0]): for j in range(im1.size[1]): r, g, b = pixels1[i, j] if abs(r - r_avg1) > 30 or abs(g - g_avg1) > 30 or abs(b - b_avg1) > 30: pixels1[i, j] = (0, 0, 0) pos_list.append((i,j)) # 重複を削除して位置情報を記憶 ut_now = datetime.datetime.now() print(ut_now, '重複を削除') pos_list = list(set(pos_list)) # im1に対し欠点を四角で囲む書き込み ut_now = datetime.datetime.now() print(ut_now, '欠点を四角で囲む') draw = ImageDraw.Draw(im1) for pos in pos_list: x, y = pos draw.rectangle((x-50, y-50, x+50, y+50), outline="red", width=5) # 画像を保存 ut_now = datetime.datetime.now() print(ut_now, '画像を保存') im1.save("C:/code/py311/trial/KRGB107530G90a.png") ut_now = datetime.datetime.now() print(ut_now, '終了')

試したこと

このi5-7400が、保有している一番高速なパソコンです。
改善できる方法には、たどり着けていません。

補足情報(FW/ツールのバージョンなど)

環境:
i5-7400 16GBメモリ 256GBSSD QuadroP600Video Windows11
VSCode 1.81.1 Python 3.11.4

tatsu99, melian👍を押しています

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

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

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

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

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

meg_

2023/10/15 03:50

処理時間は質問本文に分かりやすく書いた方が良いかと思います。
meg_

2023/10/15 03:53

> プログラムを修正することで、今のパソコンでも処理速度を改善可能かどうか、知りたいです。 forによるループ処理ではなく配列計算してはどうでしょうか?
退会済みユーザー

退会済みユーザー

2023/10/15 04:26 編集

投げやりな提案なのでコメントに - 画像を3×3で分割すれば9個に分けられるのCPUのスレッドの力を信じて分割処理もありかもしれませんね。 - あとは、Python3.11以降早くなったらしいのでそれを信じて新しいPythonを使うとか(すでにv3.11ですね) - VSCodeで走らせるよりターミナルのPythonのほうが早いかも? - 「LEDの安定化に4秒x2」電気代と時間と天秤にかけて電気代のほうが安いならずっとつけっぱなしにすると幸せになれそうです。 - USBのカメラは動作が怪しくノイズも乗りやすいので、デバグ用くらいの用途のほうが安心です。本気で早くしたい・安定動作させたいなら、Basl〇rなどの「LANケーブルで動くカメラ」のほうがいいと思います。 - 巨大な画像に対してfor文のネスト構造で処理するのはPython的に「やると不幸になる」典型例です。Pythonで画像処理をやるのであれば、なるべくfor文を外した方が良いです。meg_さんのコメントにある配列云々はたとえば、「OpenCVとnumpyで、b,g,r = cv2.split(img)をした後に、np.mean(b)みたいな感じ」でやると素早く平均値は出せます。 --- 具体的なイメージ(写真の拡大図)などがあると、画像処理に詳しい人からいいアドバイスが出そうな気がします。
Ta2S

2023/10/15 05:45

回答ありがとうございます。 Pythonプログラミングの勉強を始めて3か月強の初心者です。 配列計算のNumpyはまだ使い方を理解できていません。 Numpyを使ってみて、平均値出しは、14秒から0.5秒に短縮できました。ありがとうございます。 r_avg0 = int(np.mean(pixels0[:, :, 0])) 画素を変更する処理の方は、調べて1タイプ試してみましたが、pos_list = list(set(pos_list))の所でTypeError: unhashable type: 'list'が出て、もう1タイプはpos_list.extendを使用していますが、pos_listが定義されていない、と出ます。もう少し試してみます。 透過光検査と、反射光検査をしますので、カメラ1つでLED照明2個の切り替え撮影をします。 LEDは、LEDテープライトをUSB2chアナログリレーでON/OFF切り替えるのですが、安定化待ち時間3秒までだと、少し時間が空く(冷える?)と待ち時間が足りなくなり、今後の検討課題としています。 ハードウエアは、いろいろ調べたんですが、500x400のサイズをできるだけ0.1mmの解像度に近づけてスキャンする目標で、固定焦点で安く試してみるには、16MPと解像度が高いELP-USB16MP01-L75しか見つからず、10fpsでの取り込みができず、1fpsで5回撮影して平均化すると結構ノイズが消えるのですが、5秒と時間がかかりすぎ、今はノイズ除去は断念し、検出精度を下げざるをえないと考えています。
melian

2023/10/15 06:03

参考までに、axis を指定すれば RGB 別の平均値を計算できます。 pixels0 = np.asarray(im0) r_avg0, g_avg0, b_avg0 = np.mean(pixels0, axis=(0, 1)).astype(int)
Ta2S

2023/10/15 06:50

回答ありがとうございます。 pos_list = []を間違って消してしまっていたので、定義されていない、エラーが出ていました。 両方のタイプとも、重複を削除まで進んでエラーになるようになりました。 # タイプ1 画像を走査して条件に合う画素を変更 pos_list = [] ut_now = datetime.datetime.now() print(ut_now, '画素走査と変更0') mask0 = np.logical_or.reduce((np.abs(pixels0[:, :, 0] - r_avg0) > 30, np.abs(pixels0[:, :, 1] - g_avg0) > 30, np.abs(pixels0[:, :, 2] - b_avg0) > 30)) pixels0[mask0] = [0, 0, 0] pos_list.extend(np.argwhere(mask0)) # タイプ2 画像を走査して条件に合う画素を変更 pos_list = [] ut_now = datetime.datetime.now() print(ut_now, '画素走査と変更0') diff = np.abs(pixels0 - [r_avg1, g_avg1, b_avg1]) pixels0[np.logical_or.reduce(diff > 30, axis=2)] = [0, 0, 0] im2 = Image.fromarray(pixels0) pos_list = np.argwhere(np.any(diff > 30, axis=2)).tolist() melian様、 検索すると、元のNumpy配列と同期され続けるコピーを作る場合は、np.asarrayを使う、とあり、重複削除が不要になるのではと期待したんですが、両方とも重複削除に進む前にエラーが出ます。 どうもnp.asarrayを使用すると、両方とも重複削除に進む前にエラーが出るようです。 # タイプ1 pixels0[mask0] = [0, 0, 0] ~~~~~~~^^^^^^^ ValueError: assignment destination is read-only # タイプ2 pixels0[np.logical_or.reduce(diff > 30, axis=2)] = [0, 0, 0] ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ValueError: assignment destination is read-only
melian

2023/10/15 07:26 編集

なるほど、np.asarray() では read only になってしまうのですね。np.array() で ndarray に変換すると writable になります。 pixels0 = np.array(im0) それから、pos_list は以下の様にすればよいかと。 pos_list = np.argwhere(mask0 | mask1).tolist() なお、座標軸が逆転しているので、欠点を四角で囲む際に x と y を逆にします。 for pos in pos_list:  y, x = pos # x, y が逆  draw.rectangle((x-50, y-50, x+50, y+50), outline="red", width=5)
Ta2S

2023/10/15 07:32

重複削除は不要では?と思い、削除してみると、 画素走査と変更0,画素走査と変更1,欠点を四角で囲む、秒数と四角描画は、 for使用は、7s+7s+0.01s、結果変化なしでOK np type1は、0.2s+0.2s+0.01s、四角が縦横逆の並びになり欠点からずれている np type2は、8s+1.6s+0.00s、im1だけの四角になり、更に四角が縦横逆の並びで欠点からずれている np type1の四角が縦横逆の並びになるのを直せれば、高速に処理できそうです。
Ta2S

2023/10/15 07:35

melian様、追加回答ありがとうございます。 試してみます。
Ta2S

2023/10/15 08:01

試してみました。 (色欠点に対する黒の塗りつぶしは無くなっていたが、こちらの方が目標に近く、OK) 四角の縦横逆の並びは、直りました。 ただ、 R100,G100,B100とRGB=000への四角がありません。 (四角の縦横逆の並びの時も無かった。) もう少し、見直してみます。
melian

2023/10/15 08:34

> 色欠点に対する黒の塗りつぶしは無くなっていたが ndarray から PIL Image へ戻すとよいかと。 print(ut_now, '欠点を四角で囲む') im1 = Image.fromarray(pixels1) # 追加 draw = ImageDraw.Draw(im1) > R100,G100,B100とRGB=000への四角がありません。 こちらで試してみたところ、mask1 のしきい値を 30 から 27 に変更すると出現します。
Ta2S

2023/10/15 13:16

melian様、確認ありがとうございます。 np.logical_or.reduceの意味が分からなくて、調べても英語ばかりでよくわかりませんでした。 いろいろ変えて試してみても、 しきい値30のままでは、色欠点に対する黒の塗りつぶしは無く、赤四角も一部表示されず。 しきい値30は、PySimpleGUIのスライドバーで変数の検査感度として、入力する予定のものです。 画像ファイルのファイル名 KRGB107530G50は、白50%の中に白赤緑青の各100%70%50%30%の点が並べてある KRGB107530G90は、白90%の中に白赤緑青の各100%70%50%30%の点が並べてある という意味で作成しています。 画像データ0-255に変換されて、 白50%の中の白50%の点と、白90%の中の白100%の点以外は、本来赤四角で囲まれるはずです。 しきい値27では正常に赤四角が表示されるのですが、ひょっとして、0-255の間で条件判断できるようにしないといけないとかありますかね? カメラの露光時間を長めにしてノイズを抑えようかと思っているので、平均値228以下になるのを保証できず、しきい値27で正常も意味がわからず、私には最終の正常動作が確定できそうにありません。 何か見落としがないか、アドバイスをいただけたらありがたいです。よろしくお願いします。
melian

2023/10/15 16:25

どうも r_avg0 などを int にしている(切り捨て)のが原因の様です。.astype(int) を取り除いて float 値のままにしておくと、30 で全ての四角形が描画されます。 r_avg0, g_avg0, b_avg0 = np.mean(pixels0, axis=(0, 1)).astype(int) => r_avg0, g_avg0, b_avg0 = np.mean(pixels0, axis=(0, 1)) r_avg1, g_avg1, b_avg1 = np.mean(pixels1, axis=(0, 1)).astype(int) => r_avg1, g_avg1, b_avg1 = np.mean(pixels1, axis=(0, 1))
guest

回答2

0

ベストアンサー

参考までに、コメント欄でのやり取りを反映したコードを載せておきます。

python

1from PIL import Image, ImageDraw 2import datetime 3import numpy as np 4 5# 画像を開く 6ut_now = datetime.datetime.now() 7print(ut_now, 'ファイルを開く') 8im0 = Image.open("C:/code/py311/trial/KRGB107530G50.png") 9im1 = Image.open("C:/code/py311/trial/KRGB107530G90.png") 10 11# RGBそれぞれの平均値を算出 12ut_now = datetime.datetime.now() 13print(ut_now, '平均値算出0') 14pixels0 = np.array(im0) 15avg0 = np.mean(pixels0, axis=(0, 1)) 16print(f'image0.pngのRGB平均値: {avg0}') 17 18ut_now = datetime.datetime.now() 19print(ut_now, '平均値算出1') 20pixels1 = np.array(im1) 21avg1 = np.mean(pixels1, axis=(0, 1)) 22print(f'image1.pngのRGB平均値: {avg1}') 23 24# 画像を走査して条件に合う画素を変更 25mask0 = (np.abs(pixels0 - avg0) > 30).any(axis=-1) 26pixels0[mask0] = 0 27 28mask1 = (np.abs(pixels1 - avg1) > 30).any(axis=-1) 29pixels1[mask1] = 0 30 31pos_list = np.argwhere(mask0 | mask1) 32 33# im1に対し欠点を四角で囲む書き込み 34ut_now = datetime.datetime.now() 35print(ut_now, '欠点を四角で囲む') 36im1 = Image.fromarray(pixels1) # numpy.ndarray から PIL.Image に戻す 37draw = ImageDraw.Draw(im1) 38for y, x in pos_list: 39 draw.rectangle((x-50, y-50, x+50, y+50), outline='red', width=5) 40 41# 画像を保存 42ut_now = datetime.datetime.now() 43print(ut_now, '画像を保存') 44im1.save("C:/code/py311/trial/KRGB107530G90a.png") 45ut_now = datetime.datetime.now() 46print(ut_now, '終了')

投稿2023/10/15 17:05

melian

総合スコア21303

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

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

Ta2S

2023/10/16 14:08

melian様 確認検証させていただきました。 指摘いただいた通り、整数化した方が計算が早くなると思い正数化していたのですが、これがnumpyでは誤差の元になっていたようです。 ただ、最後に教えていただいたコードでは、ファイルの読み込みと保存を除く変換計算処理に2.3秒弱の時間になり、同じ条件になるよう書き換えた下記のタイプ1のコードの変換計算処理に1.1秒強の時間になりました。 RGB分けての処理の方が記述変更が多く面倒ではありますが、初心者の私にとっては理解しやすいこともあり、下記コードでの実装で進めてみようと思います。 ありがとうございました。 from PIL import Image, ImageDraw import numpy as np import datetime # 画像をNumPy配列として読み込みます。 ut_now = datetime.datetime.now() print(ut_now, 'ファイル読み込み') im0 = Image.open("C:/code/py311/trial/KRGB107530G50.png") im1 = Image.open("C:/code/py311/trial/KRGB107530G90.png") # RGBそれぞれの平均値を算出 ut_now = datetime.datetime.now() print(ut_now, '平均値算出0') # 平均値を算出するために、NumPyの関数を使用します。 pixels0 = np.array(im0) r_avg0 = np.mean(pixels0[:, :, 0]) g_avg0 = np.mean(pixels0[:, :, 1]) b_avg0 = np.mean(pixels0[:, :, 2]) print(f"image0.pngのRGB平均値: ({r_avg0}, {g_avg0}, {b_avg0})") ut_now = datetime.datetime.now() print(ut_now, '平均値算出1') # 平均値を算出するために、NumPyの関数を使用します。 pixels1 = np.array(im1) r_avg1 = np.mean(pixels1[:, :, 0]) g_avg1 = np.mean(pixels1[:, :, 1]) b_avg1 = np.mean(pixels1[:, :, 2]) print(f"image1.pngのRGB平均値: ({r_avg1}, {g_avg1}, {b_avg1})") # 画像を走査して条件に合う画素を変更 pos_list = [] ut_now = datetime.datetime.now() print(ut_now, '画素走査と変更0') mask0 = np.logical_or.reduce((np.abs(pixels0[:, :, 0] - r_avg0) > 27, np.abs(pixels0[:, :, 1] - g_avg0) > 27, np.abs(pixels0[:, :, 2] - b_avg0) > 27)) pixels0[mask0] = [0, 0, 0] ut_now = datetime.datetime.now() print(ut_now, '画素走査と変更1') mask1 = np.logical_or.reduce((np.abs(pixels1[:, :, 0] - r_avg1) > 27, np.abs(pixels1[:, :, 1] - g_avg1) > 27, np.abs(pixels1[:, :, 2] - b_avg1) > 27)) pixels1[mask1] = [0, 0, 0] pos_list = np.argwhere(mask0 | mask1).tolist() # im1に対し欠点を四角で囲む書き込み ut_now = datetime.datetime.now() print(ut_now, '欠点を四角で囲む') im1 = Image.fromarray(pixels1) draw = ImageDraw.Draw(im1) for pos in pos_list: y, x = pos draw.rectangle((x-50, y-50, x+50, y+50), outline="red", width=5) # 画像を保存 ut_now = datetime.datetime.now() print(ut_now, '画像を保存') im1.save("C:/code/py311/trial/KRGB107530G90a.png") ut_now = datetime.datetime.now() print(ut_now, '終了')
Ta2S

2023/10/16 14:17

あ、しきい値27の時のコードを貼り付けてしまっています。 しきい値30でも問題ない結果がでています。
guest

0

damdam23様、回答ありがとうございます。
遠隔地で使用してもらう、後4年で再雇用期間が終了しサポートできなくなる、ことを前提条件とし、ユーザーが自分で一般の電気店で買い替え可能なi3以上、8GBメモリのノートパソコンでの運用を想定しています。
被検査物のセットミスが発生した場合、1600万dotに対する四角描画の重ね塗り潰しの発生をプログラムの高速化で無反応時間を短縮改善できるか、という課題を持っていました。
現在、被検査物のセットミスが発生した場合、5000dotの欠点判定でセットミスと判断することで、動作タイミングの見直しもあり、最長9秒程度の判定時間までに抑えることができるようになりました。

投稿2024/05/12 20:57

Ta2S

総合スコア5

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.30%

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

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

質問する

関連した質問