前の記事通り、GUIでのカメラ画像表示をやってみます。
前回と前々回の内容の組み合わせ+αです。
カメラ画像表示
前回、前々回の内容に加えて、OpenCV画像→PIL画像の変換があるぐらいです。
下記参照。
【Python/tkinter】OpenCVのカメラ動画をCanvasに表示する | イメージングソリューション
after()
での待ちを入れていますが、いまいち理解できていないので、まずはキーボード入力で更新するようにします。
キー入力受付
と、そのためにはTkinterでキー入力受付が必要。
下記参照で。
Tkinter: イベントを検出する(クリック・キー入力・マウス移動)
キーの値はevent.keysym
で取得。
tkinter超入門【第45回 キー入力イベント】 | ITよろず雑記帳
import tkinter import cv2 from PIL import Image, ImageTk, ImageOps
def key_handler(e): print(e.keycode, e.keysym) root = tkinter.Tk() root.title('Key event test') root.bind('<KeyPress>', key_handler) frame = tkinter.Frame(root) label = tkinter.Label(frame, text='Input text') t = tkinter.StringVar() entry =tkinter.Entry(frame, textvariable=t) frame.pack() label.pack() entry.pack() root.mainloop()
65 a
66 b
67 c
68 d
69 e
70 f
13 Return
27 Escape
最後にEnterキー、Escキーを押しています。
keycode
は基本的にはASCIIコード通りになっているのか。
環境にもよるかな?
今度こそカメラ画像表示
DroidCamアプリのIPアドレス、ポート番号は入力できるようにしておきます。
また、接続ボタンも用意します。
StringVar()
は、ウィジェット変数で、ユーザーが入力した値をリアルタイムで反映してくれるものだそう。
Tkinterで使われるWidget変数とは?StringVarを中心に解説!? | 「モノづくりから始まるエンジニア」
今回は、スペースキーで画像更新にしましたが、テキストボックスとの併用はあんまりよくないかも。
- テキストボックスにスペース入力をしたいだけでも画像更新されてしまう
- テキストボックスをアクティブにしたときに、画像更新のスペースキーが入力になってしまう
このへんはGUI設計のときに要検討。
cap = cv2.VideoCapture() root = tkinter.Tk() root.title('Display DroidCam Image') root.geometry('500x300') # frame = tkinter.Frame(root, padx=10, pady=10) frame = tkinter.Frame(root) frame.pack() #### Entries for connection information #### t_ip = tkinter.StringVar(value='192.168.1.7') t_port = tkinter.StringVar(value='4747') entry_ip = tkinter.Entry(frame, textvariable=t_ip) entry_port = tkinter.Entry(frame, textvariable=t_port) entry_ip.grid(row=0, column=0) entry_port.grid(row=1, column=0) #### Connect button #### text_connect = 'Connect ' text_disconnect = 'Disconnect' t_connect = tkinter.StringVar(value=text_connect) def event_connect(e): if(t_connect.get() == text_connect): ret = cap.open(f'http://{t_ip.get()}:{t_port.get()}/video') if ret: print('Camera opened') t_connect.set(text_disconnect) else: print('Camera open failed') else: cap.release() print('Camera closed') t_connect.set(text_connect) button_connect = tkinter.Button(frame, textvariable=t_connect) button_connect.bind('<Button-1>', event_connect) button_connect.grid(row=0, column=1) #### Image canvas #### canvas_image = tkinter.Canvas(frame, bg='white') canvas_image.grid(row=2,column=0) frame.update() w, h = canvas_image.winfo_width(), canvas_image.winfo_height() print(f'Canvas size: {w},{h}') disp_img = None #### Capture and display camera image on canvas #### def update_image(): global disp_img ret, img = cap.read() if not ret: print('Can\'t capture image') return img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = Image.fromarray(img) img = ImageOps.pad(img, (w,h)) disp_img = ImageTk.PhotoImage(image=img) canvas_image.create_image(w/2,h/2,image=disp_img) # canvas_image.create_image(0,0,image=img, anchor=tkinter.NW) print('updated') def key_handler(e): # print(e.keysym) if e.keysym == 'space': update_image() root.bind('<KeyPress>', key_handler) root.mainloop()
Canvas size: 382,269
Camera opened
Camera closed
Camera opened
updated
updated
updated
updated
Camera closed
ちょっとはまってしまいましたが、なんとかできた。
Canvas.create_image()
自体では画面の更新はしておらず、mainloop
に戻ってから実際の更新をしているよう。
また、Canvas.create_image()
では画像への参照を登録しているのみと思われます。
ということで、mainloop内からアクセスできる変数になっている必要があると。
で、グローバル変数を使いました。
グローバル変数を使う関数の中でglobal (変数名)
を書いておかないといけないのも要注意。
【Python】tkinterのmainloopについて解説 | だえうホームページ
after()
についても解説があり、結局理解することになりました。
ここまで
とりあえず今回やることはできました。
次回は春のパン祭りスクリプトとのつなぎになります。