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

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

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

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

Tkinter

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

Q&A

解決済

1回答

2898閲覧

Python3 Tkinter カメラ映像の描画位置と、カメラとの接続が途切れた場合の処理について

person

総合スコア223

Python 3.x

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

Tkinter

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

0グッド

0クリップ

投稿2021/05/11 02:35

編集2021/05/12 04:47

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()

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

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

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

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

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

guest

回答1

0

ベストアンサー

出力場所をcanvasの中央にしたいが左上に表示されてしまっている

キャンバスのサイズや、coords() の戻り値を確認してみてください。

tk canvas coordinate より

All coordinates related to canvases are stored as floating-point numbers.
Coordinates and distances are specified in screen units, which are floating-point numbers optionally followed by one of several letters. If no letter is supplied then the distance is in pixels.

キャンバスのサイズを得るには、canvas["width"]cavas.winfo_reqwidth() を用います。
mainloop が始まる前では、キャンバスのサイズは 1 になっています。
(その為 update() で更新してるのだと思いますが、不十分なケースが有るので)

追記: winfo_width() でサイズを正常に取れないケースを再現するコードを掲載してましたが、
質問のコードに合わせて、ここは修正しました。

coords() の戻り値は、0.0 .. 1.0 の値で表現されるので、ピクセルで座標を計算するには
キャンバスのサイズを掛ける必要があります。

キャンバスのサイズが正常に得られれば、coords やテキストの大きさの計算は不要で
create_text(canvas_width//2, canvas_height//2, ...) で中央に描画出来るはずです。

解決策(どれかひとつ)

  • create_text(canvas["width"]//2, canvas["height"]//2, ...)
  • create_text(canvas.winfo_reqwidth()//2, winfo_reqheight()//2, ...)
  • キャンバス内容の初期化処理をタイマーから呼び出す。

 追記: できれば mainloop 後に初期化したほうが良いのですが、
今回の問題には直接該当せず。

検証用コード

python

1 2import tkinter as tk 3 4def init_canvas(key, canvas): 5 cv_w1 = canvas.winfo_width() 6 cv_h1 = canvas.winfo_height() 7 cv_w2 = canvas.winfo_reqwidth() 8 cv_h2 = canvas.winfo_reqheight() 9 cv_w3 = canvas["width"] 10 cv_h3 = canvas["width"] 11 12 print(f"""test {key} 13 width/height {cv_w1}x{cv_h1} 14 reqwidth/reqheight: {cv_w2}x{cv_h2} 15 ["width"]/["height"]: {cv_w3}x{cv_h3} 16 """) 17 18 canvas.create_text(cv_w2//2, cv_h2//2, text="NO SIGNAL") 19 20 21root = tk.Tk() 22canvas = tk.Canvas(root, width=400, height=400) 23canvas.pack(fill=tk.BOTH, expand=True) 24 25## mainloop 前に呼び出す(現状のコードに該当) 26# canvas.update() 27# init_canvas("before init", canvas) 28 29## 検証用、タイマーでの呼び出し 30## (mainloop 無いから呼び出されるので、canvas.update() は不要) 31 32# root.after_idle(init_canvas, "after_idle", canvas) 33# root.after(1, init_canvas, "after 1", canvas) 34 35## canvas.winfo_width を使う場合は、この様な呼び出しに 36# root.after(1, root.after_idle, init_canvas, "after 1 idle", canvas) 37 38root.mainloop() 39

カメラの接続が物理的に切れた場合などの処理をどうすればいいかわからない

物理的に切れた時の挙動は デバイス固有またはOS によっても異なると思いますが、
障害を検知した場合は、速やかにリソースを開放して、
エラーメッセージを表示といった対応をすることになると思います。

切断時にGUIが一時的にフリーズする場合は、
原因はイベントループ内での時間のかかるブロッキング処理なので、
該当する処理(カメラからの読み込み)を別スレッドへ移すことで回避できます。
キャンバスへの描画はメインスレッド側で行う必要があるため、Queueで通信。

※ 内蔵カメラでは発生しないかもしれません。

切断時にOSが落ちる場合は、
デバイス固有の問題、デバイスドライバーの不具合等には、
アプリケーション側では対応は難しいです。

(カメラではありませんが、古いusb接続のオーディオインターフェースで経験しました。)


別問題ですが
self.canvas.create_image ... 毎回 create_image はメモリリークになるので
img_id を控えておき、itemconfig で iamge (PhotoImageオブジェクト) のみ更新するようにします。

投稿2021/05/11 07:05

編集2021/05/11 07:49
teamikl

総合スコア8664

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

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

teamikl

2021/05/11 07:06

書きかけで間違えて投稿してしまいました。切断時の対応については編集中です。
teamikl

2021/05/11 07:44

訂正: >その為 update() で更新してるのだと思いますが、不十分なケースが有るので には、該当しませんでした。配置 grid() 後に update() であれば大丈夫です。
person

2021/05/11 08:06 編集

回答ありがとうございます。 canvasにテキストを表示する際は、回答のようにやることでテキストが中央に配置されることを確認しました。 (テキスト表示と映像表示の切り替えは複雑そうなので、いったん後回しにします。カメラは正常に接続されてそれを維持している前提でいったん作ります。) カメラの映像を表示する際は、どうすればいいでしょうか。 質問文にあるupdate()の中身のwidth, height辺りを cv_w = self.canvas.winfo_reqwidth() cv_h = self.canvas.winfo_reqheight() self.canvas.create_image(cv_w // 2, cv_h // 2, image_self.photo, anchor="nw") # anchorを"center"にすると左上に寄る に変えて試した見たのですが、右下に寄ってしまっています。 (画像を質問文に添付します。) create_imageの配置場所をcanvas中央にしたい場合の考え方が間違ってますかね? あと、create_image()って何度も指定するとメモリリークの原因になるんですね。変数img_idに対して上書きしてるだけのイメージだったので気にしてませんでした・・・。 ほかにもTkinterでメモリリークの原因になるものもあるんでしょうかね。
teamikl

2021/05/11 08:17

> create_image(cv_w // 2, cv_h // 2, image_self.photo, anchor="nw") ここは、元のままで良かったと思います。 anchorによって座標の起点が変わるので、anchor="nw" の場合は、0, 0 で良いはずです。 中心座標で設定する場合は、anchor="center" になります。 create_text の場合だけ anchor の解釈が変わってた気がする(追検証してみます > ほかにもTkinterでメモリリークの原因になるものもあるんでしょうかね。 タイマー内でオブジェクトを新規生成してる場合が注意ですね。 非ウィジェットのPhotoImage は、デストラクタで自動的に開放されるので、 他から参照せずにself.photo を上書きしてる限りは問題ありません。
teamikl

2021/05/11 08:26

create_text も同樣でした。anchorにより座標指定が変わります anchor は、省略時anchor="center"で、 canvas.create_text(100, 100, text="TEST NW", anchor="nw") # テキストの左上の座標が 100.100 canvas.create_text(100, 100, text="TEST CENTER") # テキストの中心の座標が 100,100 cv_w // 2, cv_h // 2, anchor="nw" では、 画像の左上の座標がキャンバスの中心になるので、 右下に表示されることになります。
person

2021/05/11 08:52 編集

では、canvas中央に映像を表示したい場合は cv_w = self.canvas.winfo_reqwidth() cv_h = self.canvas.winfo_reqheight() self.canvas.create_image(cv_w // 2, cv_h // 2, image_self.photo) とすればよいのでしょうか。 ただ、質問文のコードだとウィンドウサイズを変えたときに、pack()みたいに引っ付いてきてくれないんですよね。 canvas配置先となっているframeのrowconfigureとかはweight=1にしたので同じような動作になると思ったのですが・・・。 ウィンドウリサイズ時に、映像の描画位置を更新しなきゃダメなんですかね。
teamikl

2021/05/11 09:05

リサイズ時もキャンバスの中央に配置したい場合は、そうなりますね。 anchor="nw" の場合は左上に固定なので。中央配置したい場合は center width, height を毎回問い合わせるのではなく、 リサイズ時に更新すると良いです。 表示位置計算が必要なのは、リサイズ時一度のみ。
person

2021/05/11 09:18

> width, height を毎回問い合わせるのではなく、 > リサイズ時に更新すると良いです。 > 表示位置計算が必要なのは、リサイズ時一度のみ。 self.win.bind("<Configure>", func) で funcの中身で width, heightを取得して > itemconfig で iamge (PhotoImageオブジェクト) のみ更新するようにします。 をするようなかんじですかね。
teamikl

2021/05/11 09:32

そうですね。概ねそのとおり、リサイズのタイミングで width, heightを取得してインスタンス変数 (self.xxx) に保持しておきます。 必須というわけではありませんが、 フレーム描画毎に毎回問い合わせだと、秒間数十回発生するので、 値が変化するリサイズ時に1度のみにする事で、効率改善になります。 注意点は、2つ目の問題に取り組んでスレッドを導入したとき、 別スレッド側では直接 キャンバスのwidth,height (tkinterやGUIの情報全般)は参照してはいけない点に注意してください。 スレッドセーフな法法で値の受け渡しが必要になってきます。 例: スレッド側でキャンバスサイズに合わせて画像をリサイズしたが、 その後にGUIがリサイズされていた、等のタイミングにより問題が起こる。エラーは出ませんが 問題の再現が難しいので、一見うまく行ったようでも長期運用して稀に発生する様な状況になったりします。
person

2021/05/11 15:07

> フレーム描画毎に毎回問い合わせだと、秒間数十回発生するので、 > 値が変化するリサイズ時に1度のみにする事で、効率改善になります。 値が変化しないリサイズ時とはどういうときでしょうか。
teamikl

2021/05/11 15:33

リサイズなので width, height の値は変わります。 タイマーで呼ばれる update() 関数の中で毎回 width/height を問い合わせてますが この値が変わるのは、通常はリサイズ時のみなので、 値が変化するタイミングで一度だけ更新すれば良いという意図です。 因みに、self.win.bind("<Configure>", func) は、 サイズ変更の他にポジション移動も同じイベントなので、 ウィンドウの移動等も捕捉してしまいます。 canvas に対して bind すれば、レイアウト固定な限りは移動はないので canvas のサイズ変更イベントのみにできます。
person

2021/05/12 02:22 編集

質問に追記2として追記しました。 最初の映像の描画位置が右に寄ってしまっています。 リサイズ時の処理を追加したが、リサイズ時に映像が描画されませんでした。 描画位置が右に寄ってしまうことについて、ウィンドウサイズをあらかじめ1000x700とかにすると左上寄りになる(右寄りではない)ため、正常に中央配置できていないかもしれません。 リサイズ時の処理については、itemconfig()の後にmove()するように処理を記述したつもりですが、リサイズ時に画像が消えてしまいます。ちょっとずつリサイズしていくと、だんだん右下に画像が移動してはみ出していきます。
person

2021/05/12 00:31 編集

ちなみに、リサイズ時の座標取得時(update_by_resize())に winfo_reqwidth()とwinfo_reqheight()を使っているのですが、print()してみたらリサイズしても値が変わりませんでした。(自分の中ではcanvasをウィンドウサイズに対応させているので値は変わるイメージです。) winfo_width()、winfo_height()にすると値は変わりましたが、それでも中央配置はできていません。
person

2021/05/12 02:14

すみません。 move()の仕様をわかっていませんでした。 現在位置からの移動量を指定しなければいけませんでした。 ただ、移動先の座標を指定するほうが簡単そうなので使う関数をmoveto()に変更します。 moveto()に変更したら映像の左上がcanvasの中央に来たので、それに対応するように座標計算しなおします。
teamikl

2021/05/12 03:22 編集

今試せないのですが、位置調整は canvas.coords(img_id, cv_w // 2, cv_h // 2) でできませんか。 引数を省略した場合・現在値所得は0.0~1.0範囲の少数ですが、 設定の場合は、ピクセル単位でも設定できます。 vid_cap.read() は、タイマー内の一箇所にした方が良いです。 用途次第では問題ないかもしれませんが、コマ落ちが発生します。 - 画像を保存したい場合は、frame をインスタンス変数に格納しておき  ボタンクリックイベントで保存。デバッグ用途なら問題ありませんが、  動画再生に影響させないためには、別スレッドで保存が良いです。  ※ サイズが大きい&ボタンが連打されたりした場合、   遅延になりGUIが一瞬フリーズしたりする原因になります。  ※ frame はスレッドセーフな方法で別スレッドに渡す。 - リサイズ時も、フレームを読み込む必要はなく、  self.img_id に対しての操作のみで済みます。
person

2021/05/12 03:53

> 今試せないのですが、位置調整は canvas.coords(img_id, cv_w // 2, cv_h // 2) でできませんか。 できました。ありがとうございます。coords()を使えば同じようにできるんですね。 > vid_cap.read() は、タイマー内の一箇所にした方が良いです。 確かにそうですね。 teamikl様の回答のように変更します。
person

2021/05/12 04:54 編集

前回の追記2を削除し、変更したコードを追記2に記載しました。 後からわかったことですが、ウィンドウを閉じたときに [ WARN:0] global C:\Users\runneradmin\AppData\Local\Temp\pip-req-build-duilijvh\opencv\modules\videoio\src\cap_msmf.cpp (438) `anonymous-namespace'::SourceReaderCB::~SourceReaderCB terminating async callback とvscodeのターミナルに表示されていて、これエラーっぽいです。(質問前の時点ですでにこれは出ていました。) https://teratail.com/questions/276744 に似たようなエラーについて質問があってsleep()で解決しているようですが、エラーの原因が同じ場合、sleep()を入れるとせっかく遅延問題とかに対処したのに無駄になると思います。 (別の質問としてteratailに投げたほうがいいですかね?)
teamikl

2021/05/12 05:11

sleep するくらいなら、タイマーの時間を長めに調整したほうが良いです。 read() の戻り値を確認してみてはどうでしょう。 ret, frame = cvap.read() if ret:   ...
person

2021/05/12 05:45 編集

read()の戻り値で、retはTrueでした。 一応、初期のread()からupdate_by_timer()の呼び出しもafterに変えて、両方の時間を500msにしてみましたが、同じエラーが出ます。 (WARN:0がWARN:1になったときもある。) もしかしたらエラーの原因がURLの質問とは違うのかもしれません。 もう少し調べてみます。
teamikl

2021/05/12 05:54

エラーについては詳しく解りませんが、 OpenCV in MSMF backend の問題みたいですね。 以下のどちらかで解決報告があります - cv2.CAP_DSHOW を設定する (但し、フレームレートは遅くなる) - 環境変数 setx OPENCV_VIDEOIO_PRIORITY_MSMF 0  設定で MSMF を無効にする。 - OpenCV のバージョン https://stackoverflow.com/questions/53888878/cv2-warn0-terminating-async-callback-when-attempting-to-take-a-picture 上記のURL では 画像保存の imwrite で起こっているようですが、 終了時に必ず発生しますか?それとも何らかの操作を行った場合でしょうか。 手元の環境 win10/anaconda/内蔵カメラ では確認できませんでした。 >別の質問としてteratailに投げたほうがいいですかね? 質問のトピック範囲の内容なので大丈夫だと思いますが、 コメント内の情報は、質問文にまとめたほうが良いですね。 可能なら、tkinter を使わずに問題を再現できるかどうか試してみてください。 tkinter で使った場合のみ起こるようならtkinterタグも必要ですが、 そうでない場合は、opencv のタグで質問したほうが、 opencv方面の識者の回答を得られやすくなるかもしれません。
person

2021/05/12 06:22 編集

> 上記のURL では 画像保存の imwrite で起こっているようですが、 > 終了時に必ず発生しますか?それとも何らかの操作を行った場合でしょうか。 いえ、スナップショットやリサイズ等は行っていません。 実行してウィンドウが出て×で閉じるとメッセージがVSCodeのターミナルに表示されます。 試しにimwrite()をコメントアウトしましたが、エラーメッセージは出てしまいます。
person

2021/05/12 06:39

一応、 csv2.CAP_DSHOW でエラーメッセージは消えたのでこれを応急処置とします。 あとクローズした時の処理として、stackoverflowの回答にもあるように cv2.destroyAllWindows() もつけておきます。 (なくてもエラーメッセージは出ませんでしたが一応)
teamikl

2021/05/12 09:48

destroyAllWindows は cv2.imshow で使うウィンドウの後始末なので、 tkinter のウィンドウを使う場合は影響しないはずです。 ※ 詳細 https://github.com/opencv/opencv/tree/master/modules/highgui/src destroyAllWindows は、GUI のバックエンドにより異なりますが、 tkinter で言う win.destroy() を呼び出すものです。 問題点は、ログには modules/videoio/~とあるので、 VideoCaptureのバックエンドによるものです。 https://github.com/opencv/opencv/blob/776d92e79705b48c77a27fd68905b6da7a371d88/modules/videoio/src/cap_msmf.cpp#L435 問題箇所は特定できましたが、ライブラリ側の問題のようなので アプリケーション側で出来ることといえば、回避策を取るくらいしかできなさそう。 ---- 参考までに、公式のバグ報告 (開発元のissue tracker でログのメッセージを検索, 未解決問題のみ) opencv VideoCapture::read() returns success when camera disconnected #14044 https://github.com/opencv/opencv/issues/14044 opencv-python [ WARN:0] terminating async callback #198 https://github.com/opencv/opencv-python/issues/198 opencvを利用してるプロジェクトでの報告 BUG: memory leak in MSMF backend on Windows https://github.com/Achilefu-Lab/cancer-goggles/issues/1
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問