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

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

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

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

Python

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

Q&A

解決済

2回答

3501閲覧

【Python】【Tkinter】顔認証してその人の名前を表示するGUIを作成したい

mintiamegahard

総合スコア8

Tkinter

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

Python

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

0グッド

0クリップ

投稿2020/06/14 01:41

編集2020/06/14 08:46

前提、発生している問題

Tkinterを使用してPythonでデスクトップアプリを開発しています。
現在、Tkinter上でUSBカメラで撮影している画面を表示するところまで完成しています。
しかし、OpenCVを使用した画像処理のコードとうまく組合わせることができず悩んでいます。

OS:Windows 10
Pythonバージョン:3.5.4(64bit)
開発環境:Pycharm

実現したいこと

下記コードに示したfr.pyの26~50行目の内容をsample.pyと統合したい。
結果的には、カメラ撮影画面の左下に名前が表示される。

該当のソースコード1(sample.py)

Python

1import tkinter as tk 2 3from PIL import Image, ImageTk, ImageDraw, ImageFont 4import cv2 5import numpy as np 6 7root = tk.Tk() 8root.title('sample') 9root.geometry('1000x400+150+10') 10 11f1 = tk.LabelFrame(root, bd=2, relief="ridge", text="camera") 12f1.pack(side='top', anchor='w', padx=20) 13 14camera = tk.Canvas(f1, width=600, height=400, bg="white") 15camera.pack(side='left') 16 17 18def capture(): 19 try: 20 global c, w, h 21 c = cv2.VideoCapture(0) 22 w = c.get(cv2.CAP_PROP_FRAME_WIDTH) 23 h = c.get(cv2.CAP_PROP_FRAME_HEIGHT) 24 pass 25 except: 26 import sys 27 c.release() 28 cv2.destroyAllWindows() 29 30 31def up(): 32 global img 33 ret, frame = c.read() 34 if ret: 35 img = ImageTk.PhotoImage(Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))) 36 camera.create_image(w / 2, h / 2, image=img) 37 root.after(1, up) 38 39 40capture() 41up() 42 43root.mainloop()

該当のソースコード2(fr.py)

Python

1from face_recognition import * 2from predict import * 3from create_dataset import * 4 5import cv2 6from PIL import Image, ImageDraw, ImageFont 7import numpy as np 8 9model_path = '../models/20180402-114759' 10pic_path = "../dataset/images" 11dataset_path = '../dataset/emb/faceEmbedding.npy' 12filename = '../dataset/emb/name.txt' 13reload = False 14face_recognition_threshold = 0.85 15 16print("initializing models...") 17face_detect = Facedetection() 18face_net = facenetEmbedding(model_path) 19if reload: 20 print("refreshing face embeddings...") 21 create_face_embedding(model_path, pic_path, dataset_path, filename) 22print("loading embeddings...") 23dataset_emb, names_list = load_dataset(dataset_path, filename) 24 25 26def put_text_japanese(img, text, pos): 27 img_PIL = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) 28 font_size = 25 29 font = ImageFont.truetype('meiryo', font_size, encoding="utf-8") 30 fillColor = (255, 0, 0) # 青(0, 0, 255) 31 draw = ImageDraw.Draw(img_PIL) 32 draw.text((pos[0], pos[1] - font_size - 10), text, font=font, fill=fillColor) 33 img = cv2.cvtColor(np.asarray(img_PIL), cv2.COLOR_RGB2BGR) 34 return img 35 36 37cap = cv2.VideoCapture(0) 38height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT) 39while cap.isOpened(): 40 ret, frame = cap.read() 41 if not ret: 42 break 43 result = face_recognition_image_driver(dataset_emb, names_list, face_detect, face_net, frame, 44 face_recognition_threshold) 45 # cv2.putText(frame, result, (0,int(height)-10), 0, 1, (0, 0, 255), 2) 46 frame = put_text_japanese(frame, result, (0, int(height))) 47 cv2.imshow("Frame", frame) 48 # if the `q` key was pressed, break from the loop 49 if cv2.waitKey(1) & 0xFF == ord('q'): 50 break 51

ご回答よろしくお願いいたします。

###以下、再編集

##該当のソースコード3(sample.py(統合後))

Python

1import tkinter as tk 2 3from PIL import Image, ImageTk, ImageDraw, ImageFont 4import cv2 5import numpy as np 6 7from face_recognition import * 8from predict import * 9from create_dataset import * 10 11root = tk.Tk() 12root.title('sample') 13root.geometry('1000x400+150+10') 14 15f1 = tk.LabelFrame(root, bd=2, relief="ridge", text="camera") 16f1.pack(side='top', anchor='w', padx=20) 17 18camera = tk.Canvas(f1, width=600, height=400, bg="white") 19camera.pack(side='left') 20 21model_path = '../models/20180402-114759' 22pic_path = "../dataset/images" 23dataset_path = '../dataset/emb/faceEmbedding.npy' 24filename = '../dataset/emb/name.txt' 25reload = False 26face_recognition_threshold = 0.85 27 28print("initializing models...") 29face_detect = Facedetection() 30face_net = facenetEmbedding(model_path) 31if reload: 32 print("refreshing face embeddings...") 33 create_face_embedding(model_path, pic_path, dataset_path, filename) 34print("loading embeddings...") 35dataset_emb, names_list = load_dataset(dataset_path, filename) 36 37 38def capture(): 39 try: 40 global c, w, h 41 c = cv2.VideoCapture(0) 42 w = c.get(cv2.CAP_PROP_FRAME_WIDTH) 43 h = c.get(cv2.CAP_PROP_FRAME_HEIGHT) 44 pass 45 except: 46 import sys 47 c.release() 48 cv2.destroyAllWindows() 49 50 51def up(): 52 global img 53 ret, frame = c.read() 54 if ret: 55 result = face_recognition_image_driver(dataset_emb, names_list, face_detect, face_net, frame, 56 face_recognition_threshold) 57 58 def put_text_japanese(img, text, pos): 59 img_PIL = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) 60 font_size = 25 61 font = ImageFont.truetype('meiryo', font_size, encoding="utf-8") 62 fillColor = (255, 0, 0) # 青(0, 0, 255) 63 draw = ImageDraw.Draw(img_PIL) 64 draw.text((pos[0], pos[1] - font_size - 10), text, font=font, fill=fillColor) 65 img = cv2.cvtColor(np.asarray(img_PIL), cv2.COLOR_RGB2BGR) 66 return img 67 68 frame = put_text_japanese(frame, result, (0, int(h))) 69 70 img = ImageTk.PhotoImage(Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))) 71 camera.create_image(w / 2, h / 2, image=img) 72 root.after(1, up) 73 74 75 root.after(1, up) 76 77 78capture() 79up() 80 81root.mainloop() 82

ご回答よろしくお願いいたします。

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

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

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

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

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

guest

回答2

0

ベストアンサー

動作確認はしてないので、丸ごとコピーはせず
必要な箇所のみ参考にして下さい。

python

1def capture(): 2 try: 3 global c, w, h 4 c = cv2.VideoCapture(0) 5 w = c.get(cv2.CAP_PROP_FRAME_WIDTH) 6 h = c.get(cv2.CAP_PROP_FRAME_HEIGHT) 7 except: 8 import sys 9 c.release() 10 cv2.destroyAllWindows() 11 else: # VideoCapture初期化が成功した場合に実行される 12 13 camera.config(width=w, height=h) 14 15 # 元のコードでは、cv2.VideoCapture が失敗した後も up() を呼び出して 16 # c.read() が実行されていましたが、失敗時は up() を起動しないように変更。 17 up() 18 19 20FONT_SIZE = 25 21FILL_COLOR = (255, 0, 0) 22font = ImageFont.truetype('meiryo', FONT_SIZE, encoding="utf-8") 23 24def put_text_japanese(frame, text, pos): 25 # 主な変更点: BGR2RGB -> RGB2BGR -> GBR2RGB と変換されてたのを、一度の変換のみに 26 # 定数は関数外部に出して、毎回実行されないように。 27 x, y = pos 28 dest = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) 29 draw = ImageDraw.Draw(dest) 30 draw.text((x, y-FONT_SIZE-10), text, font=font, fill=FILL_COLOR) 31 return ImageTk.PhotoImage(dest) 32 33 34def up(): 35 global img 36 ret, frame = c.read() 37 if ret: 38 result = face_recognition_image_driver(dataset_emb, names_list, face_detect, face_net, frame, 39 face_recognition_threshold) 40 img = put_text_japanese(frame, result, (0, int(h))) 41 42 # タグを付けて、以前の画像を削除。 43 # 削除しないと 秒間数十のアイテムが蓄積されます。 44 camera.delete("img") 45 camera.create_image(w / 2, h / 2, image=img, tags="img") 46 root.after(15, up) 47 48## mainloop内から起動。 49# 50# この時点では、canvasのサイズなどは未決定の為、後の mainloopで 51# レイアウトやリサイズにより、設定したはずのサイズが上書きされる可能性があります。 52# 53# after_idle で mainloop() 側から呼んでもらうようにすることで、 54# レイアウト後に関数が実行されるのを期待できます。 55root.after_idle(capture) 56 57root.mainloop() 58 59

投稿2020/06/14 11:07

teamikl

総合スコア8664

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

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

0

tkinter と cv2 連携時の課題
以下の3点は tkinter の mainloop() を阻害する為、そのまま使えませんが、
sample.py では、ほぼ解消できているように見えます。

  • [OK] while ループ

→ tkinter では、「スレッドを使う」か 「after に書き換え」

  • [OK] cv2.imshow

→ tkinter では、canvas に表示

  • cv2.waitKey

→ tkinter では不要 (ウィンドウを閉じて終了)

後は、up() 関数内にコードを持ってくるだけではないですか?


他の注意点

afterの引数の時間は ms 単位ですが、
タイマーの精度は、呼び出し間隔を保証されているわけではないので
もう少し大きい値が良いです。例えば windows であれば 15~16ms (fps 60 目安)

また、現在の実装ではメインスレッド上でキャプチャ等の処理を行いますが
もし時間がかかるような処理で、GUIの反応が鈍くなったりする場合は、
別スレッド・プロセスに処理を移す等も検討。

create.create_image の前に、以前の画像があれば delete した方が良いです。
画像自体はデストラクタにより PhotoImageの変数上書き時に廃棄されますが、
キャンバス内にアイテム情報が蓄積され続けます。

投稿2020/06/14 05:17

teamikl

総合スコア8664

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

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

mintiamegahard

2020/06/14 06:40

いつもご回答ありがとうございます。追加の質問で申し訳ないのですが、「後は、up() 関数内にコードを持ってくるだけではないですか?」についてお聞きしたいです。sample.pyのdef up()下にfr.pyのput_text_japanese()の内容をコピペしましたが、実行できませんでした。どの部分を持ってくるかもう少し具体的に教えていただけますでしょうか。
teamikl

2020/06/14 06:47

実行できる環境がないので概要のみになりますが、 up() では ret, frame = c.read() としてから、 frame をキャンバスへ描画まで行ってますよね。 この c.read() で読まれた frame に対して - face_recognition_image_driver - put_text_japanese を行ってから、frameをキャンバスへ描画 という流れになります。
mintiamegahard

2020/06/14 07:44

おそらく回答していただいた内容を理解していないのですが、up()を以下のようにしても、カメラ撮影画面に名前が表示されません。 def up(): global img ret, frame = c.read() if ret: result = face_recognition_image_driver(dataset_emb, names_list, face_detect, face_net, frame, face_recognition_threshold) def put_text_japanese(img, text, pos): img_PIL = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) font_size = 25 font = ImageFont.truetype('meiryo', font_size, encoding="utf-8") fillColor = (255, 0, 0) # 青(0, 0, 255) draw = ImageDraw.Draw(img_PIL) draw.text((pos[0], pos[1] - font_size - 10), text, font=font, fill=fillColor) img = cv2.cvtColor(np.asarray(img_PIL), cv2.COLOR_RGB2BGR) return img frame = put_text_japanese(frame, result, (0, int(h))) img = ImageTk.PhotoImage(Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))) camera.create_image(w / 2, h / 2, image=img) root.after(1, up) お手数ですがteamikl様のお答えを教えていただけますでしょうか。
teamikl

2020/06/14 08:14

インデント確認のため、質問文への掲載して下さい。 ほぼ想定通りです。が、画像を扱ってる部分の細部までは解りません。 - def put_text_japanese() 関数は def up() 関数の外へだす - canvas.delete("img") # タグを付けて、以前の画像を削除 - canvas.create_image(w/2, h/2, image=img, tags="img") インデントにより挙動が変わるのでわからない部分 - put_text_japanese, face_recognition_image_driver関数は期待通りに呼ばれていますか?  - text が得られてるかどうかを print(text) で確認  - 名前が得られた時の画像をファイルへ保存して、別の手段で表示確認
mintiamegahard

2020/06/14 08:35 編集

失礼しました。編集しなおします。
teamikl

2020/06/14 08:37

「編集」で追記できますよ。
mintiamegahard

2020/06/14 08:47

失礼しました。編集しましたので、ご確認をお願いいたします。
teamikl

2020/06/14 08:48 編集

(問題とは直接関係ない部分ですが) 画像のRGB2BGRの変換処理は、tkinter で表示するなら、 BGR → RGB 最初の一回で済むので、余分な変換を省けそうです。
teamikl

2020/06/14 09:33

編集後、確認しました。 上記に挙げたポイントは残るものの、問題とは関係ない部分です。 他に思い当たる部分、画像に文字を描画する座標を確認してみて下さい。 キャンバスは camera = tk.Canvas(f1, width=600, height=400, bg="white") ですが、print(w, h) はどの様に表示されますか? draw.text の後の行に、img_PIL.save("test_put_text.png") として 出力されたファイルを確認してみて下さい。文字は描画されているでしょうか? 画像のサイズとキャンバスのサイズが不一致の場合、表示領域外になってる可能性があります。 まずは、それぞれの関数が呼ばれているか、引数が正しく渡っているかを確かめましょう。 その上で、もし画像ファイルには文字があるが、tkinter上で表示されてないのでしたら、 キャンバスのサイズ修正で直ると思います。他の原因の場合は、また状況次第なので まずは、上の点を確かめてみて下さい。
mintiamegahard

2020/06/14 10:30

確認ありがとうございます。 canvas.create_image(w/2, h/2, image=img)の下の行にprint(w, h)を入力して実行すると、コンソール上に「640.0 480.0」と表示されました。なのでcamera = tk.Canvas(f1, width=600, height=400, bg="white")をwidth=640, height=480に変更したら画面左下に赤字で名前が表示されました!!
teamikl

2020/06/14 10:38

原因はキャンバスサイズ<->画像サイズの不一致だったみたいですね。 解決策としては他に: キャンバスのサイズを変更する、 w,h を得たタイミングで変更したい場合 camera.config(width=w, height=h) とすることも出来ます。 手元で実行確認出来ませんが、上記の修正点を含めたコードを改めて投稿します。
mintiamegahard

2020/06/15 00:25

今回もアドバイスありがとうございます。コード以外の内容も大変参考になりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問