質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.48%
Keras

Kerasは、TheanoやTensorFlow/CNTK対応のラッパーライブラリです。DeepLearningの数学的部分を短いコードでネットワークとして表現することが可能。DeepLearningの最新手法を迅速に試すことができます。

Python 3.x

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

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Q&A

解決済

1回答

1081閲覧

tensorflowによる性別予測モデルのリアルタイム予測と描画方法について

teramax

総合スコア1

Keras

Kerasは、TheanoやTensorFlow/CNTK対応のラッパーライブラリです。DeepLearningの数学的部分を短いコードでネットワークとして表現することが可能。DeepLearningの最新手法を迅速に試すことができます。

Python 3.x

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

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

0グッド

1クリップ

投稿2022/07/24 04:56

編集2022/07/24 05:08

現在、tensorflowを使用したリアルタイム性別予測のシステムを作成しています。
顔の切り出しはpaddlehub、分類はwide_resnetというmoduleを使用しました。
描画と予測を逐次処理で流す場合推論部分で時間がかかってしまいwebカメラの映像がカクついてしまうため、マルチスレッドにより描画と推論別々の処理として実行させようと考えました。
作成したプログラムは以下の通りです。

python

1import tkinter as tk 2from PIL import Image, ImageTk, ImageOps 3import paddlehub as hub 4import cv2 5import numpy as np 6 7import threading 8 9from wide_resnet import WideResNet 10from pathlib import Path 11import tensorflow as tf 12from keras.utils.data_utils import get_file 13import os.path as os 14 15tf.compat.v1.disable_eager_execution() 16 17module = hub.Module(name="pyramidbox_lite_server_mask", version='1.1.0') 18 19 20class Application(tk.Frame): 21 def __init__(self, master=None): 22 super().__init__(master) 23 self.pack() 24 self.process_list = [] 25 self.master.title("monoliths_stream_windows") 26 self.master.geometry("1000x1000") 27 28 self.canvas = tk.Canvas(self.master) 29 30 self.canvas.bind('<Button-1>', self.canvas_click) 31 32 self.canvas.pack(expand=True, fill=tk.BOTH) 33 34 self.capture = cv2.VideoCapture(0) 35 36 self.disp_id = None 37 self.frame = None 38 self.roop_flag = False 39 self.axis = [] 40 41 def canvas_click(self, event): 42 43 if self.disp_id is None: 44 self.roop_flag = True 45 thread1 = threading.Thread(target=self.disp_image) 46 thread1.start() 47 thread2 = threading.Thread(target=self.reserch_face) 48 thread2.start() 49 50 else: 51 self.after_cancel(self.disp_id) 52 self.disp_id = None 53 self.roop_flag = False 54 55 # 性別・年齢を表記する関数 56 def draw_label(self, image, point, label, font=cv2.FONT_HERSHEY_SIMPLEX, 57 font_scale=0.3, thickness=1): 58 cv2.putText(image, label, point, font, font_scale, (0, 0, 0), thickness, lineType=cv2.LINE_AA) 59 60 def disp_image(self): 61 ret, self.frame = self.capture.read() 62 63 frame_copy = self.frame.copy() 64 65 if len(self.axis) != 0: 66 for i in range(len(self.axis)): 67 top, right, bottom, left = self.axis[i][1], self.axis[i][2], self.axis[i][3], self.axis[i][4] 68 cv2.rectangle(frame_copy, (top, left), (bottom, right), (0, 255, 255), 2) 69 label = self.axis[i][0] 70 self.draw_label(frame_copy, (top, right), label) 71 72 out_put = cv2.cvtColor(frame_copy, cv2.COLOR_BGR2RGB) 73 pil_image = Image.fromarray(out_put) 74 canvas_width = self.canvas.winfo_width() 75 canvas_height = self.canvas.winfo_height() 76 pil_image = ImageOps.pad(pil_image, (700, 700)) 77 self.photo_image = ImageTk.PhotoImage(image=pil_image) 78 79 80 self.canvas.create_image( 81 canvas_width / 2, 82 canvas_height / 2, 83 image=self.photo_image 84 ) 85 86 self.disp_id = self.after(10, self.disp_image) 87 88 def age_gender_predict(self, faces): 89 if len(faces) > 0: 90 # モデルの設定 91 img_size = np.asarray(faces.shape)[1] 92 if os.isdir("model") == False: 93 pre_model = "https://github.com/yu4u/age-gender-estimation/releases/download/v0.5/weights.28-3.73.hdf5" 94 modhash = 'fbe63257a054c1c5466cfd7bf14646d6' 95 self.weight_file = get_file("weights.28-3.73.hdf5", pre_model, cache_subdir="model", 96 file_hash=modhash, cache_dir=str(Path(__file__).resolve().parent)) 97 else: 98 self.weight_file = "model/weights.28-3.73.hdf5" 99 model = WideResNet(img_size, depth=16, k=8)() 100 model.load_weights(self.weight_file) 101 102 # 予測 103 results = model.predict(faces) 104 Genders = results[0] 105 return Genders 106 107 def reserch_face(self): 108 while self.roop_flag: 109 if self.frame is not None: 110 target_axis = [] 111 input_dict = {"data": [self.frame]} 112 results = module.face_detection(data=input_dict) 113 faces = np.zeros((len(results), 64, 64, 3), dtype="uint8") 114 for i in range(len(results)): 115 result = results[i] 116 top, right, bottom, left = int(result['data']['top']), int( 117 result['data']['right']), int(result['data']['bottom']), int( 118 result['data']['left']) 119 aligned = cv2.resize(self.frame[top: bottom, left: right], (64, 64)) 120 faces[i, :, :, :] = cv2.cvtColor(aligned, cv2.COLOR_BGR2RGB) 121 target_axis.append([top, right, bottom, left]) 122 import time 123 start_time = time.time() 124 genders = self.age_gender_predict(faces) 125 print(time.time() - start_time) 126 for i in range(len(target_axis)): 127 gender = "Male" if genders[i][0] < 0.5 else "Female" 128 target_axis[i].insert(0, gender) # genders[i]) 129 del faces 130 self.axis = target_axis 131 132 133if __name__ == "__main__": 134 root = tk.Tk() 135 app = Application(master=root) 136 app.mainloop()

上記プログラムを実行したのですが、age_gender_predictメソッドについて処理のたびに処理時間が延びていってしまう事象が発生しました。
処理時間の推移は以下に示します。

1.4923202991485596 1.6585233211517334 1.9348974227905273 2.128232002258301 2.41341495513916 2.723492383956909 3.138969659805298 3.9985647201538086 4.804307460784912 5.1527650356292725 5.146689176559448 5.88690710067749 6.015024185180664 6.525038957595825 6.470944166183472 8.12919569015503 8.783061742782593 8.08810830116272

メソッド内でモデルのロードも同時に行っているため処理に時間がかかっていると考えファイルの穂見込みからload_weightsをクラスの外に出すプログラムに変更しました。
しかし、modelの定義をクラス外で行ったところ以下のようなエラーが出てしまいました。

exception

1Exception in thread Thread-6: 2Traceback (most recent call last): 3 File "C:\Program Files\Python38\lib\threading.py", line 932, in _bootstrap_inner 4 self.run() 5 File "C:\Program Files\Python38\lib\threading.py", line 870, in run 6 self._target(*self._args, **self._kwargs) 7 File ".\realtime_face.py", line 126, in reserch_face 8 genders = self.age_gender_predict(faces) 9 File ".\realtime_face.py", line 105, in age_gender_predict 10 File "C:\Users\terao_yuta_microad\AppData\Roaming\Python\Python38\site-packages\tensorflow\python\framework\ops.py", line 5070, in control_dependencies 11 c = self.as_graph_element(c) 12 File "C:\Users\terao_yuta_microad\AppData\Roaming\Python\Python38\site-packages\tensorflow\python\framework\ops.py", line 3974, in as_graph_element 13 return self._as_graph_element_locked(obj, allow_tensor, allow_operation) 14 File "C:\Users\terao_yuta_microad\AppData\Roaming\Python\Python38\site-packages\tensorflow\python\framework\ops.py", line 4053, in _as_graph_element_locked 15 raise ValueError("Tensor %s is not an element of this graph." % obj) 16ValueError: Tensor Tensor("pred_gender/Softmax:0", shape=(None, 2), dtype=float32) is not an element of this graph.

modelをクラス変数として使いまわそうとしても似たようなエラーが出てしまいます。
自分自身、これまでtensorflowやkerasでのモデルの生成と評価はコンペなどでやったことがあるのですが、アプリへの導入はやったことがなかったため対処法がわからないです。
上記の原因について回答していただけると助かります。
文章が下手でわかりずらいところがあれば申し訳ありません。
回答よろしくお願い致します。

追記
上記エラーについて、参照サイトにてスレッドごとにモデルを初期化する必要があるため、そのために_make_predict_function()を使うべきとのことが書かれていたため追記してみたのですが、別途以下のようなエラーが出てしまいい対処がわからない状態です。

Colocations handled automatically by placer. File "C:\Users\terao_yuta_microad\AppData\Roaming\Python\Python38\site-packages\keras\backend.py", line 4273, in __call__ self._make_callable(feed_arrays, feed_symbols, symbol_vals, session) File "C:\Users\terao_yuta_microad\AppData\Roaming\Python\Python38\site-packages\keras\backend.py", line 4209, in _make_callable callable_fn = session._make_callable_from_options(callable_opts) File "C:\Users\terao_yuta_microad\AppData\Roaming\Python\Python38\site-packages\tensorflow\python\client\session.py", line 1513, in _make_callable_from_options return BaseSession._Callable(self, callable_options) File "C:\Users\terao_yuta_microad\AppData\Roaming\Python\Python38\site-packages\tensorflow\python\client\session.py", line 1471, in __init__ self._handle = tf_session.TF_SessionMakeCallable( tensorflow.python.framework.errors_impl.InvalidArgumentError: Tensor input_1:0, specified in either feed_devices or fetch_devices was not found in the Graph

顔画像から顔の部分を縁取りして性別、年齢を予測し出力するというロジックはうまくいっているためマルチスレッドによる弊害と考えられますが、逐次処理じゃないプログラムを組んだことがないため助言いただきたいです。
長文となり申し訳ありません。よろしくお願い致します。

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

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

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

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

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

guest

回答1

0

ベストアンサー

複数の問題を抱えていて、誤った設計のまま問題解決を試みようとしているように見受けられます。
tensorflow のエラーは一旦差し置いて、マルチスレッドでのGUIプログラミングでは

  • メインスレッドでGUIのイベントループ
  • サブスレッドでその他の時間のかかる処理

という構成にして、同期キュー等でスレッド間でのデータをやりとりする設計が一般的です。

現状のマルチスレッドに関する問題点では、主に2点

  • after で登録される関数はイベントループ側で実行されます。

 提示のコードの場合では、1回目のみが別スレッドで、2回目以降はメインスレッドでの処理になります。
disp_image 関数の PhotoImage 作成~ canvas 更新辺りの処理は GUIの描画関連なので
必ずメインスレッド側で処理してください。(イベント駆動型プログラミングでの作法)
cv2 関連の処理は、必要があれば別スレッドで行っても構いません。

  • self.axis がスレッドセーフではない。

 disp_image と reserch_face で意図しないタイミングでデータが書き換えられることがあります。
解消法は、同期キューを使ったスレッド間のデータのやり取りをお勧めします。


その他、モデルの初期化に関しては別問題です。
クラス内外等のソースコード上での場所よりも、実行時にどのスレッドで実行されているかを調べてみてください。

別問題2 canvas.create_image で毎回 canvas内にアイテムを生成しているので、
蓄積されメモリーリークの原因となってます。対策は、A) delete する B) create_image は一度のみにして、item_configure で image を更新する。
PhotoImage オブジェクト自体はデストラクタで破棄されるので、GCにまかせて問題ありません。

投稿2022/07/25 13:53

teamikl

総合スコア8664

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問