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

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

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

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

Q&A

解決済

3回答

2484閲覧

cv2.imshowを使わないリピート動画の停止のエラー回避

shin0859

総合スコア15

Python 3.x

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

1グッド

0クリップ

投稿2021/06/07 08:34

python初心者です

cv2.imshowを使わないリピート動画再生で
https://scribles.net/showing-video-image-on-tkinter-window-with-opencv/)
外部ボタンによる停止でエラーが発生します。
どのような回避方法が有るのでしょうかアドバイス下さい。
(最初のループ動画をスタートボタンで停止し新しいループフォト表示と考えています

python

1# coding: utf-8 2import tkinter as tk 3from tkinter import ttk 4from tkinter import * 5from PIL import Image, ImageTk 6import cv2 7 8class Application(tk.Frame): 9 def __init__(self, master): 10 super().__init__(master) 11 self.pack() 12 self.master.title("Digital Photo") 13 self.menu_frame = tk.Frame(self, width=200) 14 self.menu_frame.grid(row=0, column=0, sticky=tk.NSEW) 15 self.main_frame = tk.Frame(self, width=800, bg="red") 16 self.main_frame.grid(row=0, column=1, sticky=tk.NSEW) 17 18 self.MenuFrame() 19 self.MainCanvas() 20 21 def MainCanvas(self): 22 23 self.win_w = 800 24 self.win_h = 480 25 videoFile="./video/2018年おうし座流星群 大火球出現.mp4" 26 # 取り敢えずyoutubeから https://youtu.be/JTUb5Bxkvck 流用 27 self.cap = cv2.VideoCapture(videoFile) 28 self.img_w = self.cap.get(cv2.CAP_PROP_FRAME_WIDTH) # 動画幅 29 self.img_h = self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT) # 動画高 30 self.canvas1 = tk.Canvas(self.main_frame, width=self.win_w-10, height=self.win_h-10, bg="green") 31 self.canvas1.grid(row=0, column=0, sticky=tk.NSEW) 32 33 ### top_button 34 icon3 = Image.open('./inifile/top.png') 35 icon3 = icon3.resize(size=(55, 57)) 36 self.icon3 = ImageTk.PhotoImage(icon3) 37 self.top_btn = tk.Button(self.main_frame, image=self.icon3, relief=tk.FLAT) 38 # self.top_btn.grid(row=0, column=0, padx=10, pady=10, sticky=tk.SW) 39 40 41 # Update image on canvas 42 self.interval = 10 # Interval in ms to get the latest frame 43 self.update_image() 44 45 def update_image(self): 46 47 # Get the latest frame and convert image format 48 image = cv2.cvtColor(self.cap.read()[1], cv2.COLOR_BGR2RGB) # to RGB 49 image = Image.fromarray(image) # to PIL format 50 image = image.resize((int(self.img_w * (self.win_h / self.img_h)), int(self.win_h))) 51 self.image = ImageTk.PhotoImage(image) # to ImageTk format 52 53 # roop ok 54 ret, frame = self.cap.read() 55 # print(ret) 56 if ret: 57 # Update image 58 self.canvas1.create_image(0, 0, anchor=tk.NW, image=self.image) 59 self.canvas1.create_image(self.win_w / 2 - (self.img_w * (self.win_h / self.img_h) / 2), 0, image=self.image, 60 anchor=tk.NW) 61 # 指定した時間経過後 self.update_image を実行 62 self.master.after(self.interval, self.update_image) 63 64 else: 65 self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0) 66 self.update_image() 67 68 69 def MenuFrame(self): 70 71 ### load_button 72 icon1 = Image.open('./inifile/start.png') 73 icon1 = icon1.resize(size=(60, 57)) 74 self.icon1 = ImageTk.PhotoImage(icon1) 75 # load_btn = tk.Button(menu_frame, 76 # image=icon1, relief=tk.FLAT, command=load_btn_click) 77 self.load_btn = tk.Button(self.menu_frame, image=self.icon1, relief=tk.FLAT) 78 79 ### exit_button 80 icon2 = Image.open('./inifile/exit.png') 81 icon2 = icon2.resize(size=(44, 57)) 82 self.icon2 = ImageTk.PhotoImage(icon2) 83 # exit_btn = tk.Button(menu_frame, 84 # image=icon2, relief=tk.FLAT, command=exit_btn_click) 85 self.exit_btn = tk.Button(self.menu_frame, image=self.icon2, relief=tk.FLAT) 86 87 ### control Layout grid 88 self.load_btn.grid(row=5, column=0, padx=(20, 0), pady=30, sticky=tk.W) 89 self.exit_btn.grid(row=6, column=0, padx=0, pady=0) 90 91 92def main(): 93 root = tk.Tk() 94 app = Application(master=root) 95 96 def show_animation(maxsize=200): 97 def animation(value): 98 value = min(value, maxsize) 99 root.grid_columnconfigure(0, minsize=value) 100 if value < maxsize: 101 root.after(20, animation, value+20) 102 app.menu_frame.grid(row=0, column=0, sticky=tk.NSEW) 103 app.top_btn.grid_forget() 104 root.after_idle(animation, 10) 105 # add code 106 topVideo_show() 107 108 109 def hide_animation(value=200): 110 def animation(value): 111 value = max(0, value-20) 112 root.grid_columnconfigure(0, minsize=value) 113 if value > 0: 114 root.after(20, animation, value) 115 else: 116 app.grid_propagate(False) 117 app.menu_frame.grid_forget() 118 app.top_btn.grid(row=0, column=0, padx=10, pady=10, sticky=tk.SW) 119 # add code 120 canvas_clear() 121 122 root.after_idle(animation, value) 123 124 def toggle_menu(e): 125 is_shown = root.grid_columnconfigure(0)["minsize"] 126 if not is_shown: 127 show_animation() 128 else: 129 hide_animation() 130 131 def canvas_clear(): 132## //// エラー発生 ///// 133 app.cap.release() 134 app.canvas1.delete("all") 135## エラー内容↓ 136## self.image = cv2.cvtColor(self.video.read()[1], cv2.COLOR_BGR2RGB) # to RGB 137## cv2.error: OpenCV(4.5.2) C:\Users\runneradmin\AppData\Local\Temp\pip-req-build-kuwfz3h3\opencv\modules\imgproc\src\color.cpp:182: error: (-215:Assertion failed) !_src.empty() in function 'cv::cvtColor' 138 139 def topVideo_show(): 140 app.MainCanvas() 141 142 143 root.bind_all("<F4>", toggle_menu) 144 app.exit_btn.config(command=root.quit) 145 app.load_btn.config(command=hide_animation) 146 app.top_btn.config(command=show_animation) 147 148 app.mainloop() 149 150if __name__ == "__main__": 151 main()

--環境--
python3.8
tkinter8.6
pycharm2021.1

teamikl👍を押しています

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

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

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

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

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

guest

回答3

0

ベストアンサー

python

1# Application クラス内 2 3 def start(self, path): 4 # TODO: ファイルの存在チェック if os.path.isfile(path): 5 self.stop() 6 self.cap.open(path) 7 self.update_image() 8 9 def stop(self): 10 if self.update_timer: 11 self.master.after_cancel(self.update_timer) 12 self.update_timer = None 13 if self.cap.isOpened(): 14 self.cap.release() 15 self.canvas1.delete("all")
  • stop() と対になる start() を実装します (コメントでは触れてませんが、利便性の為)

 start ... 初期化, stop ... 後始末 と対応が取れるように。

  • app は、クラス内では、自身のインスタンス(self)なので

 app.cap ではなく self.cap で参照する。

python

1# main() 関数内 2 3 # start click at top-screen 4 def mainFame_view(): 5 print("start click") 6 app.stop() 7 8 9 # top click at main-screen 10 def TopFame_view(): 11 print("top click") 12 app.start(videoFile) # TODO: videoFile は動画ファイルのパス。 13 14 # ウィンドウを閉じた時に呼び出される 15 def root_close(): 16 app.stop() 17 root.destroy()
  • open/release 方式の場合は、キャンバスや Video オブジェクトは使いまわすので

 新規生成は不要。MainCanvas() は2度呼び出さない。← 今回採用の方法ではこちら
逆に、MainCanvas() を毎回呼び出す場合は、キャンバスとvideo を毎回破棄する。

  • stop() メソッド内で video の release も行うように変更したので、

 不要なコードを整理。

  • videoFile は MainCanvas 内のローカル変数に書かれてるので直接参照できません。

 ここはインスタンス変数にする、もしくは外側のスコープに出す等で
main() 関数側からも参照できるようにします。

投稿2021/06/11 04:00

teamikl

総合スコア8760

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

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

shin0859

2021/06/11 05:52

teamikl様 希望する動作確認が出来ました!有難うございます。 長いコードで、そして長い間その対処をして頂いて感謝致します。 自分の今の知識では得られないコードに満足しています。 今後はmainFramにphotoviewを表示したいと考えています。 また機会が有りましたらご指導願います。 余談ですが、一連の回答依頼は、はじめてのpython挑戦になります ではでは (PS:エラーの出ないフリーズはちんぷんかんぷんでした)
teamikl

2021/06/11 09:57

初めてという事なので、段階を踏んでいかないと理解が難しいかもしれませんが 参考になるかもしれないので書き残しておきます。 ---- フリーズ(応答なし)とエラーの出ない問題は、それぞれ個別の問題(原因とその作用)で、 GUIプログラムのデバッグが難しい部分で、一般的には 「ブロッキング処理」が発生すると、GUIが応答なしとなります。 「ボタンを押した時に呼ばれる関数」や「after で登録した関数」は、 一見すると、何処から呼ばれてるかコード上では順序を追えないので、解らないと思いますが、 「mainloop() 内で」「順番に」呼ばれます。→ 何処かで止ると次が呼ばれない (※ ここがよくある誤解で、自動的にバックグラウンドで動いてるわけではありません) mainloop 内では、GUIを動かす処理がされているので、 この mainloopの動作が止まってしまうこと自体がフリーズの原因です。 詳しくは、「イベント駆動」「イベントループ」辺りが検索のヒント。 次に、止まった箇所、なぜ止まったかを調べるのですが、 このブロッキングが発生するのは、ある程度傾向が決まっていて ファイルやネットワークからの読込待機 (I/O) 、もしくは単純に重たい処理(時間の掛かるループ) 入出力関連で、正常な時は問題ないが、障害時に発生するというケースが多いです。 (例: ネットに繋いでない環境での、HTTPリクエスト等) ブロッキングは、動作自体がエラーなのではなく、 プログラム側としては、単に待機状態なだけなので、エラーは報告されません。 プログラムを作り手側が、ブロッキングが発生しないように工夫する必要があります。 ==== 後から気が付いた部分ですが、タイマーの間隔が 質問のコードでは 10ms で、2021/06/10付のコードでは 1ms になってます。 opencvの環境や読込ソースにもよりますが、 ウィンドウを動かしたりする時の動作が鈍くなったりする事があります。 対策 cap.get(cv2.CAP_PROP_FPS)) でfps を調べる。 例えば、60FPS (1000ms/60fps = 16.66ms) なら、 タイマー更新間隔は 15~16ms 辺りが適正。
shin0859

2021/06/12 01:06

teamikl様  補足のコメント有難うございます 言われている内容については、理解するに至らないレベルですので ネットで調べて少しでも理解できるように心がけたいと思います。 又最後の1msは無知からくる2,3倍速を想像して直したままの結果でした、戻しておきます。 最後にいろいろご親切に有難うございました。
teamikl

2021/06/12 09:16

1ms は 1000fps ですね。 倍速再生は、簡単な方法はFPSそのままでフレームを間引くと、早送りっぽく描画できます。 大抵の環境ではモニターのリフレッシュレートで制限があるので。 for _ in range(3): # 3回繰り返し・・・3度目に読み込んだフレームを描画する -> 3倍速  ret, frame = self.cap.read() # 読み込みソース次第で、カメラからのリアルタイム読込には対応できません。
guest

0

teamikl様

2021/06/10付のコードをアップします
Q&Aの使い方がいまいち理解していませんでした。

python

1# # coding: utf-8 2import tkinter as tk 3# from tkinter import ttk 4from tkinter import * 5from PIL import Image, ImageTk 6import cv2 7 8class Application(tk.Frame): 9 def __init__(self, master): 10 super().__init__(master) 11 self.pack() 12 self.master.title("Digital Photo") 13 self.menu_frame = tk.Frame(self, width=200) 14 self.menu_frame.grid(row=0, column=0, sticky=tk.NSEW) 15 self.main_frame = tk.Frame(self, width=800, bg="red") 16 self.main_frame.grid(row=0, column=1, sticky=tk.NSEW) 17 18 self.update_timer = None 19 20 self.MenuFrame() 21 self.MainCanvas() 22 23 def MainCanvas(self): 24 self.win_w = 800 25 self.win_h = 480 26 # 取り敢えずyoutubeから流用 27 videoFile="./video/2018年おうし座流星群 大火球出現.mp4" 28 self.cap = cv2.VideoCapture(videoFile) 29 self.img_w = self.cap.get(cv2.CAP_PROP_FRAME_WIDTH) # 動画幅 30 self.img_h = self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT) # 動画高 31 self.canvas1 = tk.Canvas(self.main_frame, width=self.win_w-10, height=self.win_h-10, bg="green") 32 self.canvas1.grid(row=0, column=0, sticky=tk.NSEW) 33 34 # top_button 35 icon3 = Image.open('./inifile/top.png') 36 icon3 = icon3.resize(size=(55, 57)) 37 self.icon3 = ImageTk.PhotoImage(icon3) 38 # self.top_btn = tk.Button(self.main_frame, image=self.icon3, relief=tk.FLAT) 39 self.top_btn = tk.Button(self.main_frame, text="top_Button", relief=tk.FLAT) 40 ## self.top_btn.grid(row=0, column=0, padx=10, pady=10, sticky=tk.SW) 41 42 # # Update image on canvas 43 self.interval = 1 # Interval in ms to get the latest frame 44 self.update_image() 45 46 def update_image(self): 47 # https://scribles.net/showing-video-image-on-tkinter-window-with-opencv/ 48 ret, frame = self.cap.read() 49 # print(ret) 50 if ret: 51 # Get the latest frame and convert image format 52 image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # to RGB 53 image = Image.fromarray(image) # to PIL format 54 image = image.resize((int(self.img_w * (self.win_h / self.img_h)), int(self.win_h))) 55 self.image = ImageTk.PhotoImage(image) # to ImageTk format 56 57 # Update image 58 self.canvas1.create_image(0, 0, anchor=tk.NW, image=self.image) 59 self.canvas1.create_image(self.win_w / 2 - (self.img_w * (self.win_h / self.img_h) / 2), 0) 60 # 指定した時間経過後 self.update_image を実行 61 # self.master.after(self.interval, self.update_image) 62 # 上記+タイマーセット 63 self.update_timer = self.master.after(self.interval, self.update_image) 64 else: 65 self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0) 66 # self.update_image() 67 self.update_timer = None 68 69 def stop(self): 70 if self.update_timer: 71 self.master.after_cancel(self.update_timer) 72 self.update_timer = None 73 # if app.cap.isOpened(): 74 # app.cap.release() 75 # app.canvas1.delete("all") 76 77 def MenuFrame(self): 78 79 # load_button 80 icon1 = Image.open('./inifile/start.png') 81 icon1 = icon1.resize(size=(60, 57)) 82 self.icon1 = ImageTk.PhotoImage(icon1) 83 # load_btn = tk.Button(menu_frame, 84 # image=icon1, relief=tk.FLAT, command=load_btn_click) 85 # self.load_btn = tk.Button(self.menu_frame, image=self.icon1, relief=tk.FLAT) 86 self.load_btn = tk.Button(self.menu_frame, text="start_btn", relief=tk.FLAT, bg="green") 87 88 # exit_button 89 icon2 = Image.open('./inifile/exit.png') 90 icon2 = icon2.resize(size=(44, 57)) 91 self.icon2 = ImageTk.PhotoImage(icon2) 92 # exit_btn = tk.Button(menu_frame, 93 # image=icon2, relief=tk.FLAT, command=exit_btn_click) 94 # self.exit_btn = tk.Button(self.menu_frame, image=self.icon2, relief=tk.FLAT) 95 self.exit_btn = tk.Button(self.menu_frame, text="exit_btn", relief=tk.FLAT, bg="green") 96 97 # control Layout grid 98 self.load_btn.grid(row=5, column=0, padx=(20, 0), pady=30, sticky=tk.W) 99 self.exit_btn.grid(row=6, column=0, padx=0, pady=0) 100 101 102 103def main(): 104 root = tk.Tk() 105 app = Application(master=root) 106 107 def show_animation(maxsize=200): 108 def animation(value): 109 value = min(value, maxsize) 110 root.grid_columnconfigure(0, minsize=value) 111 if value < maxsize: 112 root.after(20, animation, value+20) 113 app.menu_frame.grid(row=0, column=0, sticky=tk.NSEW) 114 app.top_btn.grid_forget() 115 root.after_idle(animation, 10) 116 # add code 117 TopFame_view() 118 119 def hide_animation(value=200): 120 def animation(value): 121 value = max(0, value-20) 122 root.grid_columnconfigure(0, minsize=value) 123 if value > 0: 124 root.after(20, animation, value) 125 else: 126 app.grid_propagate(False) 127 app.menu_frame.grid_forget() 128 app.top_btn.grid(row=0, column=0, padx=10, pady=10, sticky=tk.SW) 129 # add code 130 mainFame_view() 131 root.after_idle(animation, value) 132 133# ??????????????????????????????????????? 134 # start click at top-screen 135 def mainFame_view(): 136 print("start click") 137 # videoをストップしてcanvas1.delete(main_frameは残したまま) 138 # video は必ずオープンしている 139 app.stop() 140 app.cap.release() 141 app.canvas1.delete("all") 142 143 # top click at main-screen 144 def TopFame_view(): 145 print("top click") 146 # canvas1を作ってvideoをroop(main_frameは残ったまま) 147 app.MainCanvas() 148# ?????????????????????????????????????? 149 150 def root_close(): # ウィンドウを閉じた時に呼び出される 151 if app.cap.isOpened(): 152 app.cap.release() 153 app.stop() 154 root.destroy() 155 156 # def toggle_menu(e): 157 # is_shown = root.grid_columnconfigure(0)["minsize"] 158 # if not is_shown: 159 # show_animation() 160 # else: 161 # hide_animation() 162 163 164 # root.bind_all("<F4>", toggle_menu) 165 app.exit_btn.config(command=root_close) 166 app.load_btn.config(command=hide_animation) 167 app.top_btn.config(command=show_animation) 168 169 app.mainloop() 170 171if __name__ == "__main__": 172 main()

投稿2021/06/10 20:54

shin0859

総合スコア15

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

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

teamikl

2021/06/11 04:44

変則的になったかもしれないけど、 コードがコメント欄では追い難いくらいの規模になってきたので。 本来は質問に追記か、別質問とした方が良いのかもしれませんが、 個人的には、題名の範囲内ですし、回答欄を有効に利用すればいいかなと思ってます。 ---- コードの規模が大きくなってきた場合の対処としては、 質問に投稿する用として、問題の再現に特化した、コンパクトなコードを作ると良いです。 画像ファイル → 他の環境では、ファイルが無いとエラーで止る。  画像ボタンのコードがコメントされてた配慮はありがたかったですが、  エラーになるのは、その前の画像ファイルを必要とする Image.open です。 不要なコード → アニメーション関連は、問題の再現には不要。 コメント等 → 投稿の際に、問題の理解に関係ない部分のコメントは削除 その際に、「MainCanvas」等は、クラスとして作っておくと、 コードの変更なしに再利用がしやすくなります。 (ここは、文章だけでは説明が難しいので、後日でよければサンプル提示します。  前回の質問の時に編集中だったコードがあるので)
shin0859

2021/06/11 06:01

はい おっしゃる通りで、今後不要コードを省き、コピペで現象が体感できる 質問に努力いたします。いろいろと有難うございました。
teamikl

2021/06/12 08:56

MenuFrame, MainMenu を別「クラス」に別けるサンプル載せておきます。 (クラス関係の提示のみで、中身は実装してません。参考に出来そうな部分のみ拾ってください) https://replit.com/@MiKLTea/TkMenuMain#main.py クラスにうまくまとめる事で、 「スタート→トップを二度繰り返したら」みたいな特定の動作をした場合に起こる問題を テストコードで表現できるようになります。例:  video.start() # 動画再生スタート  video.stop() # 止める  video.start() # 2回目のスタート <-- ここでバグ発生
guest

0

問題の原因:

  • video.release() 後に (稼働中のタイマーにより) video.read() が呼び出されている

解決方法:

  • 事前に after_cancel でタイマーを止めて下さい。

 これにより、update_image 呼び出しがキャンセルされ release 後の read を回避できるはずです。
after_cancel で止める為には、after や after_idle の戻り値を控えておく必要があります

python

1 # __init__ 内 で初期化 2 3 self.update_timer = None 4 5 def update_image(self): 6 7 ... 8 9 if ret: 10 # 指定した時間経過後 self.update_image を実行 11 self.update_timer = self.master.after(self.interval, self.update_image) 12 else: 13 self.update_timer = None 14 15 def stop(self): 16 if self.update_timer: 17 self.master.after_cancel(self.update_timer) 18 self.update_timer = None

追記: 上記の stop() を canvas_clear 関数内から呼び出す

投稿2021/06/07 13:54

編集2021/06/07 16:18
teamikl

総合スコア8760

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

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

shin0859

2021/06/07 14:49

teamikl様 アドバイス有難うございます ご指導のコードはなんとなく理解できましたが、(私にはとても思いつかない) 実際修正コードで走らせると、以前同様に def update_image(self):の最初の1行名で同じエラーが発生します。 ## エラー内容↓ ## self.image = cv2.cvtColor(self.video.read()[1], cv2.COLOR_BGR2RGB) # to RGB 私の def update_image(self): 中身コードが間違っているのでしょうか 明日考えてみます 有難うございました。
teamikl

2021/06/07 15:25 編集

確認: app.cap.release() 前に、app.stopメソッドは呼び出せてますか 回答に提示したコードはタイマーを止める方法 stop() メソッドの実装迄なので、 canvas_clear 内で呼び出しが必要です。
shin0859

2021/06/07 21:27 編集

あっ はい そうするんですか、エラーは出なくなりました。 def canvas_clear(): app.stop() app.cap.release() app.canvas1.delete("all") ただ、それ以前からスタート→トップを二度繰り返したら、 エラーは何もでませんが、フリーズしてしまいます なぜなんでしょう? どれかのメモリーを開放しないとダメなんでしょうか
teamikl

2021/06/07 21:10

Video の初期化が MainCanvas 作成時に一度きりなので、 複数回のスタートに対応してません。 スタートボタンを押したときに Video を初期化もしくは再openしましょう。 ストップを押さずにスタートを複数回押した時に、 2重に初期化してしまわないようにも注意。(cap.isOpened() を確認する) ---- 別問題ですが、update_image 内で2回 read() があります。⇛ FPS が倍になる 2回目の ret, frame = self.cap.read() は、ret が確認されるだけで、 frame は表示されることなく読み飛ばされてます。 # 問題点: ret が確認されていない image = cv2.cvtColor(self.cap.read()[1], ... # 最初に ret の判定をしてから、frame を使います。2回目の cap.read() は不要 ret, frame = self.cap.read() if ret:   image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # to RGB
shin0859

2021/06/07 21:29

は はやっ おはようございます 内容確認します
shin0859

2021/06/08 05:16

teamikl様 アドバイスを確認しましたが、下記の内容についてお知らせください。 >Video の初期化が MainCanvas 作成時に一度きりなので、 >複数回のスタートに対応してません。 >スタートボタンを押したときに Video を初期化もしくは再openしましょう。 初期化をいろいろ試しましたが、正解が得られませんでしたので もう少し詳しくお知らせください。 def topVideo_show(): if app.cap.isOpened() == False: print("false") # app.cap.release() # app.canvas1.delete("all") # cv2.destroyAllWindows() app.MainCanvas() >ストップを押さずにスタートを複数回押した時に、 >2重に初期化してしまわないようにも注意。(cap.isOpened() を確認する) ストップ及びスタートは同一画面に表示しない事にしてますので そのままでokと思っています >別問題ですが、update_image 内で2回 read() があります 理解しましたので、修正しました。
teamikl

2021/06/09 12:18

試した内容がわからなかったので、確認ですが - Application クラス内 update_imageメソッドの修正 - Application クラス内 stop メソッドを追加 - canvas_clear 関数内で app.stop() 呼び出し ここまでは対応した上で、 topVideo_show 関数の内容を試されているという理解であってますか? 2通りの方法があって A) ウィジェットから作り直す ... tk.Canvas, VideoCapture のインスタンスを再度作る この場合、新規にキャンバスを作るなら canvas1.destroy() で破棄する必要があります。 (delete("all") はキャンバス内容のクリア) B) release/open release した後に再openする。キャンバス等はそのまま利用できます。
shin0859

2021/06/10 03:40 編集

teamikl様 いろいろお手数掛けて申し訳ありません。 3件の確認事項は、下記のコードで修正済と思います。恐らく エラー現象が2回目のupボタンは押せますが、そこで無反応となります。 何か違うところに問題がある様に思われますが、どうなんでしょうか? あと試して見ますので (簡単と思われる)B案をご指示ください。 宜しくお願いいたします。 下記に現時点のコードをアップします。 ```python # # coding: utf-8 import tkinter as tk from tkinter import ttk from tkinter import * from PIL import Image, ImageTk import cv2 class Application(tk.Frame): def __init__(self, master): super().__init__(master) self.pack() self.master.title("Digital Photo") self.menu_frame = tk.Frame(self, width=200) self.menu_frame.grid(row=0, column=0, sticky=tk.NSEW) self.main_frame = tk.Frame(self, width=800, bg="red") self.main_frame.grid(row=0, column=1, sticky=tk.NSEW) self.update_timer = None self.MenuFrame() self.MainCanvas() def MainCanvas(self): self.win_w = 800 self.win_h = 480 videoFile="./video/2018年おうし座流星群 大火球出現.mp4" # 取り敢えずyoutubeから https://youtu.be/JTUb5Bxkvck 流用 self.cap = cv2.VideoCapture(videoFile) self.img_w = self.cap.get(cv2.CAP_PROP_FRAME_WIDTH) # 動画幅 self.img_h = self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT) # 動画高 self.canvas1 = tk.Canvas(self.main_frame, width=self.win_w-10, height=self.win_h-10, bg="green") self.canvas1.grid(row=0, column=0, sticky=tk.NSEW) ### top_button icon3 = Image.open('./inifile/top.png') icon3 = icon3.resize(size=(55, 57)) self.icon3 = ImageTk.PhotoImage(icon3) self.top_btn = tk.Button(self.main_frame, image=self.icon3, relief=tk.FLAT) # self.top_btn.grid(row=0, column=0, padx=10, pady=10, sticky=tk.SW) # Update image on canvas self.interval = 1 # Interval in ms to get the latest frame self.update_image() def update_image(self): # https://scribles.net/showing-video-image-on-tkinter-window-with-opencv/ ret, frame = self.cap.read() # print(ret) if ret: # Get the latest frame and convert image format image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # to RGB image = Image.fromarray(image) # to PIL format image = image.resize((int(self.img_w * (self.win_h / self.img_h)), int(self.win_h))) self.image = ImageTk.PhotoImage(image) # to ImageTk format # Update image self.canvas1.create_image(0, 0, anchor=tk.NW, image=self.image) self.canvas1.create_image(self.win_w / 2 - (self.img_w * (self.win_h / self.img_h) / 2), 0, image=self.image, anchor=tk.NW) # 指定した時間経過後 self.update_image を実行 # self.master.after(self.interval, self.update_image) # 上記+タイマーセット self.update_timer = self.master.after(self.interval, self.update_image) else: self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0) self.update_image() self.update_timer = None def stop(self): if self.update_timer: self.master.after_cancel(self.update_timer) self.update_timer = None def MenuFrame(self): ### load_button icon1 = Image.open('./inifile/start.png') icon1 = icon1.resize(size=(60, 57)) self.icon1 = ImageTk.PhotoImage(icon1) # load_btn = tk.Button(menu_frame, # image=icon1, relief=tk.FLAT, command=load_btn_click) self.load_btn = tk.Button(self.menu_frame, image=self.icon1, relief=tk.FLAT) ### exit_button icon2 = Image.open('./inifile/exit.png') icon2 = icon2.resize(size=(44, 57)) self.icon2 = ImageTk.PhotoImage(icon2) # exit_btn = tk.Button(menu_frame, # image=icon2, relief=tk.FLAT, command=exit_btn_click) self.exit_btn = tk.Button(self.menu_frame, image=self.icon2, relief=tk.FLAT) ### control Layout grid self.load_btn.grid(row=5, column=0, padx=(20, 0), pady=30, sticky=tk.W) self.exit_btn.grid(row=6, column=0, padx=0, pady=0) def main(): root = tk.Tk() app = Application(master=root) def show_animation(maxsize=200): def animation(value): value = min(value, maxsize) root.grid_columnconfigure(0, minsize=value) if value < maxsize: root.after(20, animation, value+20) app.menu_frame.grid(row=0, column=0, sticky=tk.NSEW) app.top_btn.grid_forget() root.after_idle(animation, 10) # add code //////////// topFream_view() # //////////////////// def hide_animation(value=200): def animation(value): value = max(0, value-20) root.grid_columnconfigure(0, minsize=value) if value > 0: root.after(20, animation, value) else: app.grid_propagate(False) app.menu_frame.grid_forget() app.top_btn.grid(row=0, column=0, padx=10, pady=10, sticky=tk.SW) # add code ////// mainFream_view() # ////////////// root.after_idle(animation, value) def toggle_menu(e): is_shown = root.grid_columnconfigure(0)["minsize"] if not is_shown: show_animation() else: hide_animation() # ??????????????????????????????????????? def mainFream_view(): app.stop() app.cap.release() app.canvas1.delete("all") # cv2.destroyAllWindows() def topFream_view(): app.cap.open("./video/2018年おうし座流星群 大火球出現.mp4") # if app.cap.isOpened(): # print(app.cap.isOpened()) app.MainCanvas() # ?????????????????????????????????????? root.bind_all("<F4>", toggle_menu) app.exit_btn.config(command=root.quit) app.load_btn.config(command=hide_animation) app.top_btn.config(command=show_animation) app.mainloop() if __name__ == "__main__": main() ```
teamikl

2021/06/10 06:41

ほぼ出来てるように思います topFream_view関数内 (多分 typo: Frame) # 再生中の場合は停止する if app.cap.isOpened():  app.cap.release() app.stop() app.cap.open(...) app.update_image() # <-- 読込のタイマーを開始 ---- もう一点、後から気づいたことですが 終了時にもリソースの解放をきちんと行った方が良いです。 def close(): # ウィンドウを閉じた時に呼び出される  if app.cap.isOpened():   app.cap.release()  app.stop()  root.destroy() root.protocol("WM_DELETE_WINDOW", close) app.exit_btn.config(command=close) # root.quit では呼び出されない為、変更する。 ※ コードが重複してしまったので、 cap.isOpened() cap.release() は stop メソッド側で行う方が良さそうです。
shin0859

2021/06/10 13:11

teamikl様 度重なるアドバイス有難うございます >もう一点、後から気づいたことですが >終了時にもリソースの解放をきちんと行った方が良いです。 これは理解出来ました def root_close(): # ウィンドウを閉じた時に呼び出される if app.cap.isOpened(): app.cap.release() app.stop() app.destroy() app.exit_btn.config(command=root_close) 画面の切り替え スタート←→トップは 何度も試行錯誤しましたが、 2回目でフリーズします。 >>何か違うところに問題がある様に思われますが、どうなんでしょうか? 想像ですが、メモリーが解放されないで、フリーズを呼び起こしているのでしょうか? 私にはお手上げ状態です... もうしばらく試行錯誤してみます。 現時点最良コードは(2回目でフリーズしますが) # ??????????????????????????????????????? # start click at top-screen def mainFame_view(): print("start click") # videoをストップしてcanvas1.delete(main_frameは残したまま) app.stop() app.cap.release() app.canvas1.delete("all") # top click at main-screen def TopFame_view(): print("top click") # canvas1を作ってvideoをroop(main_frameは残ったまま) app.MainCanvas() # ??????????????????????????????????????
teamikl

2021/06/10 13:58

見た感じでは、release 前の確認位でしょうか。 コメントだとインデント等が解らずコードの全体を把握できないので、 別回答か質問のコードに実行したコードを掲載してください。 if app.cap.isOpened():  app.cap.release() open/release の方法で行う場合は、 app.MainCanvas() は2回呼び出してはいけません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問