TkinterでUSB接続したWebカメラの映像を映したいです。
ソースコードを作りましたが、下記について悩んでいます。
- 描画位置をcanvasの中央にしたいが左上に表示されてしまっている
- カメラの接続が物理的に切れた場合などの処理をどうすればいいかわからない
描画位置については、おそらくcreate_image()の座標設定だと思いますが、指定する座標の求め方がわかりません。
カメラの解像度を求めて、解像度/2をすればよいのでしょうか?
カメラの接続が物理的に切れる場合の処理は、try-exceptを使えばよいのでしょうか?
(手元にUSBカメラがなく、内蔵カメラで試しているため切断した際の動作確認ができずどのような挙動になるかはわかりませんが、エラーは出ると思うので。)
それともほかに方法はありますでしょうか?
ちなみに、カメラが接続されていない場合は、ソースコードのコメントアウトされている処理をしたいです。
(canvas上に黒塗の長方形を配置、その中央に白色で"NO SIGNAL" の文字を配置。)
回答よろしくお願いします。
Python
1from PIL import Image, ImageTk 2import cv2 3import tkinter as tk 4 5class App: 6 def __init__(self, win): 7 video_source = 0 8 self.vcap = cv2.VideoCapture(video_source) 9 self.width = self.vcap.get(cv2.CAP_PROP_FRAME_WIDTH) 10 self.height = self.vcap.get(cv2.CAP_PROP_FRAME_HEIGHT) 11 12 self.win = win 13 self.win.title("Camera") 14 self.disp_center(self.win) 15 self.win.protocol("WM_DELETE_WINDOW", self.close) 16 self.create_view() 17 self.update() 18 19 def disp_center(self, win, wx=400, wy=300): 20 #win.resizable(0, 0) 21 dx = win.winfo_screenwidth() 22 dy = win.winfo_screenheight() 23 win_size = str(wx) + "x" + str(wy) + \ 24 "+" + str(int(dx/2 - wx/2)) + "+" + str(int(dy/2 - wy/2)) 25 win.geometry(win_size) 26 27 def close(self): 28 self.win.destroy() 29 self.vcap.release() 30 31 def create_view(self): 32 self.win.rowconfigure(0, weight=1) 33 self.win.columnconfigure(0, weight=1) 34 35 self.frame = tk.Frame(self.win, relief="sunken", bd=1) 36 self.frame.grid(row=0, column=0, sticky="nsew") 37 self.frame.rowconfigure(0, weight=1) 38 self.frame.columnconfigure(0, weight=1) 39 40 self.canvas = tk.Canvas(self.frame) 41 self.canvas.grid(row=0, column=0, sticky="nsew", padx=10, pady=10) 42 43 """ 44 self.canvas.update() 45 cv_w = self.canvas.winfo_width() 46 cv_h = self.canvas.winfo_height() 47 48 rec_id = self.canvas.create_rectangle(0, 0, cv_w, cv_h, fill="black") 49 50 51 # 長方形の座標取得 52 rec_pos = self.canvas.coords(rec_id) 53 # テキストを描画(位置は適当) 54 text_id = self.canvas.create_text(0, 0, text="NO SIGNAL", font=("", 20), fill="white") 55 # テキストのサイズ取得 56 text_size = self.canvas.bbox(text_id) 57 # テキストの座標移動 58 rc_x = rec_pos[2] / 2 59 rc_y = rec_pos[2] / 3 60 tc_y = text_size[3] / 2 61 self.canvas.move(text_id, rc_x - (rec_pos[0] / 2), rc_y - tc_y) 62 """ 63 64 self.button = tk.Button(self.win, text="Snapshot", command=self.pushed_button) 65 self.button.grid(row=1, column=0, sticky="", pady=10) 66 67 68 def pushed_button(self): 69 pass 70 71 def update(self): 72 _, frame = self.vcap.read() 73 74 frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) 75 self.photo = ImageTk.PhotoImage(image = Image.fromarray(frame)) 76 77 cv_w = self.canvas.winfo_width() 78 cv_h = self.canvas.winfo_height() 79 img_id = self.canvas.create_image(0, 0, image=self.photo, anchor="nw") 80 self.canvas.after(15, self.update) 81 82 83def main(): 84 win = tk.Tk() 85 App(win) 86 win.mainloop() 87 88if __name__ == "__main__": 89 main()
参考にした記事
追記
追記2
teamikl様からのコメントの指摘をもとに変更
Python
1from datetime import datetime 2from PIL import Image, ImageTk 3import cv2 4import queue 5import threading 6import tkinter as tk 7 8class App: 9 def __init__(self, win): 10 video_source = 0 11 self.vcap = cv2.VideoCapture(video_source) 12 self.width = self.vcap.get(cv2.CAP_PROP_FRAME_WIDTH) 13 self.height = self.vcap.get(cv2.CAP_PROP_FRAME_HEIGHT) 14 15 self.data_queue = queue.Queue() 16 17 self.win = win 18 self.win.title("Camera") 19 self.thread = threading.Thread(target=self.get_queue) 20 self.thread.start() 21 self.disp_center(self.win, 720, 480) 22 self.win.protocol("WM_DELETE_WINDOW", self.close) 23 self.create_view() 24 self.bind_event() 25 self.update_by_timer() 26 27 28 def disp_center(self, win, wx=400, wy=300): 29 #win.resizable(0, 0) 30 dx = win.winfo_screenwidth() 31 dy = win.winfo_screenheight() 32 win_size = str(wx) + "x" + str(wy) + \ 33 "+" + str(int(dx/2 - wx/2)) + "+" + str(int(dy/2 - wy/2)) 34 win.geometry(win_size) 35 36 37 def close(self): 38 self.data_queue.put(["close", None]) 39 self.thread.join() 40 self.win.destroy() 41 self.vcap.release() 42 43 44 def create_view(self): 45 self.win.rowconfigure(0, weight=1) 46 self.win.columnconfigure(0, weight=1) 47 48 self.frame = tk.Frame(self.win, relief="sunken", bd=1) 49 self.frame.grid(row=0, column=0, sticky="nsew") 50 self.frame.rowconfigure(0, weight=1) 51 self.frame.columnconfigure(0, weight=1) 52 53 self.canvas = tk.Canvas(self.frame) 54 self.canvas.grid(row=0, column=0, sticky="nsew", padx=10, pady=10) 55 56 _, frm = self.vcap.read() 57 self.frame = cv2.cvtColor(frm, cv2.COLOR_BGR2RGB) 58 self.photo = ImageTk.PhotoImage(image = Image.fromarray(self.frame)) 59 #self.canvas.update() 60 cv_w = self.canvas.winfo_reqwidth() 61 cv_h = self.canvas.winfo_reqheight() 62 self.img_id = self.canvas.create_image(cv_w // 2, cv_h // 2, image=self.photo, anchor="center") 63 64 65 """ 66 self.canvas.update() 67 cv_w = self.canvas.winfo_width() 68 cv_h = self.canvas.winfo_height() 69 70 rec_id = self.canvas.create_rectangle(0, 0, cv_w, cv_h, fill="black") 71 72 73 # 長方形の座標取得 74 rec_pos = self.canvas.coords(rec_id) 75 # テキストを描画(位置は適当) 76 text_id = self.canvas.create_text(0, 0, text="NO SIGNAL", font=("", 20), fill="white") 77 # テキストのサイズ取得 78 text_size = self.canvas.bbox(text_id) 79 # テキストの座標移動 80 rc_x = rec_pos[2] / 2 81 rc_y = rec_pos[2] / 3 82 tc_y = text_size[3] / 2 83 self.canvas.move(text_id, rc_x - (rec_pos[0] / 2), rc_y - tc_y) 84 """ 85 86 self.button = tk.Button(self.win, text="Snapshot", command=self.pushed_button) 87 self.button.grid(row=1, column=0, sticky="", pady=10) 88 89 90 def pushed_button(self): 91 self.data_queue.put(["snapshot", None]) 92 93 94 def update_by_timer(self): 95 _, frm = self.vcap.read() 96 self.frame = cv2.cvtColor(frm, cv2.COLOR_BGR2RGB) 97 self.photo = ImageTk.PhotoImage(image = Image.fromarray(self.frame)) 98 self.canvas.itemconfig(self.img_id, image=self.photo) 99 self.canvas.after(15, self.update_by_timer) 100 101 102 def update_by_resize(self, e): 103 self.canvas.itemconfig(self.img_id, image=self.photo, anchor="center") 104 cv_w = self.canvas.winfo_width() 105 cv_h = self.canvas.winfo_height() 106 print(datetime.now()) 107 print(cv_w, cv_h) 108 self.canvas.coords(self.img_id, cv_w // 2, cv_h // 2) 109 110 111 def bind_event(self): 112 self.canvas.bind("<Configure>", self.update_by_resize) 113 114 115 def get_queue(self): 116 while True: 117 msg, data = self.data_queue.get() 118 if msg == "snapshot": 119 self.snapshot() 120 elif msg == "close": 121 break 122 else: 123 print("Undefined msg") 124 125 def snapshot(self): 126 filename = "C:/Users/user01/Desktop/img/frame-" + datetime.now().strftime("%Y-%d-%m-%H-%M-%S_%f") + ".jpg" 127 cv2.imwrite( filename, 128 cv2.cvtColor( self.frame, cv2.COLOR_BGR2RGB ) ) 129 print(filename) 130 131 132def main(): 133 win = tk.Tk() 134 App(win) 135 win.mainloop() 136 137if __name__ == "__main__": 138 main()
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/05/11 07:06
2021/05/11 07:44
2021/05/11 08:06 編集
2021/05/11 08:17
2021/05/11 08:26
2021/05/11 08:52 編集
2021/05/11 09:05
2021/05/11 09:18
2021/05/11 09:32
2021/05/11 15:07
2021/05/11 15:33
2021/05/12 02:22 編集
2021/05/12 00:31 編集
2021/05/12 02:14
2021/05/12 03:22 編集
2021/05/12 03:53
2021/05/12 04:54 編集
2021/05/12 05:11
2021/05/12 05:45 編集
2021/05/12 05:54
2021/05/12 06:22 編集
2021/05/12 06:39
2021/05/12 09:48