私はPythonに比較的慣れていません。先月かそこらでPythonを学び、オンラインで見つけた例や他の人のコードに基づいてこれを一緒にハックしました。
ウェブカメラからのフィードを継続的に更新されるイメージのループとしてキャンバスに表示する Tkinter GUI を取得しました。GUI を終了してスクリプトを 1 回おきに再実行すると、次のエラーが発生します。
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python27\lib\lib-tk\Tkinter.py", line 1410, in __call__
return self.func(*args)
File "C:\Python27\lib\lib-tk\Tkinter.py", line 495, in callit
func(*args)
File "C:\...\cv2_cam_v8.py", line 20, in update_video
(self.readsuccessful,self.f) = self.cam.read()
SystemError: NULL object passed to Py_BuildValue
エラーが発生すると、画像は読み取られず、ビデオフィードはキャンバスを更新するための画像を受信しません。スクリプトは、初回および 2 回目以降はエラーなく正常に実行されます。cv2 モジュールの VideoCapture 関数を使用した以前のテストから、カメラ オブジェクトを削除して解放し、その後の実行で問題なくカメラ ストリームをキャプチャできるようにする必要があることがわかりました。who
コンソールに入力して名前空間を確認しても表示cam
されないため、GUI を閉じた後に名前空間が適切に削除されていることがわかります。cv2 の read 関数がエラーになる理由がわかりません。エラーが発生すると、ガベージコレクションまたはエラー処理によってカメラと関係のあるものが削除または解放されるため、2回ごとにしか発生していないと思いますが、これが何であるかはわかりません...
これが私のコードです:
import cv2
import Tkinter as tk
from PIL import Image, ImageTk
class vid():
def __init__(self,cam,root,canvas):
self.cam = cam
self.root = root
self.canvas = canvas
def update_video(self):
(self.readsuccessful,self.f) = self.cam.read()
self.gray_im = cv2.cvtColor(self.f, cv2.COLOR_RGB2GRAY)
self.a = Image.fromarray(self.gray_im)
self.b = ImageTk.PhotoImage(image=self.a)
self.canvas.create_image(0,0,image=self.b,anchor=tk.NW)
self.root.update()
self.root.after(33,self.update_video)
if __name__ == '__main__':
root = tk.Tk()
videoframe = tk.LabelFrame(root,text='Captured video')
videoframe.grid(column=0,row=0,columnspan=1,rowspan=1,padx=5, pady=5, ipadx=5, ipady=5)
canvas = tk.Canvas(videoframe, width=640,height=480)
canvas.grid(column=0,row=0)
cam = cv2.VideoCapture(2)
x = vid(cam,root,canvas)
root.after(0,x.update_video)
button = tk.Button(text='Quit',master=videoframe,command=root.destroy)
button.grid(column=0,row=1)
root.mainloop()
del cam
次のようにコードをリファクタリングします。
def update_video(cam,root,canvas):
(readsuccessful,f) = cam.read()
gray_im = cv2.cvtColor(f, cv2.COLOR_RGB2GRAY)
a = Image.fromarray(gray_im)
b = ImageTk.PhotoImage(image=a)
canvas.create_image(0,0,image=b,anchor=tk.NW)
root.update()
root.after(33,update_video(cam,root,canvas))
if __name__ == '__main__':
root = tk.Tk()
videoframe = tk.LabelFrame(root,text='Captured video')
videoframe.grid(column=0,row=0,columnspan=1,rowspan=1,padx=5, pady=5, ipadx=5, ipady=5)
canvas = tk.Canvas(videoframe, width=640,height=480)
canvas.grid(column=0,row=0)
cam = cv2.VideoCapture(2)
root.after(0,update_video(cam,root,canvas))
button = tk.Button(text='Quit',master=videoframe,command=root.destroy)
button.grid(column=0,row=1)
root.mainloop()
del cam
GUI にボタンが表示されず、ウィンドウを閉じた後に次のエラーが発生します。
RuntimeError: Too early to create image
3つの質問があります
1 -いずれかの例外を防ぐにはどうすればよいですか?
更新: 「root.after(0,update_video(cam,root,canvas))」を「root.after(0,lambda: update_video(cam,root,canvas))」および「update_video(cam,root,canvas)」に変更" を "update_video(cam,root,canvas,event=None)" にするか、次の形式を使用して引数をコールバックに渡します: "root.after(time_to_wait, callback, arguments, master)" は、2 番目のエラーを修正します (およびその他のエラーを修正します)。投稿しない)。また、kobejohn が指摘したように、try: except ブロックを追加すると、2 番目のエラーも修正されます。詳細については、彼の回答を参照してください。
2 - cv2 の .read() よりも高速で効率的な関数はありますか? 編集:より高いフレームレートを得るためにコードをリファクタリングする方法はありますか? 読み取り機能はドキュメントにリストされている唯一のものであり、ドキュメントに記載されていない場合は利用できないことをどこかで読んだだけです。この方法では約 5 fps しか得られませんが、10 ~ 20 fps の方がはるかに許容範囲です。
更新: 別のカメラを使用した kobejohn のテストと私のテストとの不一致から、低フレームレートは低品質の Web カメラの結果です。高品質の Web カメラは、より高いフレームレートをもたらします。
3 - update() はできるだけ避けるべきだと読んでいますが、それ以外の方法でキャンバスに画像を再描画させるにはどうすればよいですか (または、このコードで update_idletasks() を実装します)? ある種のスレッドを実装する必要がありますか、それとも回避できますか?
更新: update() メソッドを使用せずにコードが機能するようになりましたが、メイン GUI のボタンからビデオフィードの記録を開始すると、フリーズ/応答しなくなるため、とにかくスレッドの実装を検討する必要があります。
完成したプログラムは、Ubuntu と Windows (おそらく Mac でも) で使用されます。Windows 7 を実行しています。IDE は Spyder 2.1.11 (Python 2.7.3) です。
事前に感謝します。アドバイスや解決策をいただければ幸いです。
よろしく、
S.チア