7

私は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.チア

4

3 に答える 3

8

解決しました!Python の OpenCV 2.4.2/ cv2

なんらかの奇妙な理由で、以前や他のフォーラムで「リリース」メソッドを見つけることができませんでした。ページには、opencv への python バインディングにリリース メソッドが含まれていないことが特に記載されていました。おそらく、これは「import cv」を使用する場合にのみ適用されます。後者を使用して最初のプロトタイピングを行いましたが、 ReleaseCapture メソッドを探していたときに、何らかの理由で cv2 の 'release' メソッドを見逃していました。

ドキュメントで見つけました:http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html

import cv2

cam=cv2.VideoCapture(0)
cam.release
于 2013-03-26T01:32:01.270 に答える
0

このコードを試して、得られる FPS を確認できますか? メモを比較できるように、FPS 計算を含めました。(編集:また、どのようなエラーが発生しましたか。元のコードで発生したエラーは発生しませんでした。以下のコードではエラーは発生しません)

何か違うものを思いついたかどうかを確認するためだけに、ゼロから始めました。いくつかの違いがあります:

  1. (マイナーな?) バグがありました: opencv のデフォルトのカラー チャンネルは RGB ではなく BGR です。したがって、grascale 変換をcv2.COLOR_RGB2GRAY -->から変更しますcv2.COLOR_BGR2GRAYVideoCapture の例で、同様のことを行うことがわかります。
  2. キャンバスの代わりに単純なラベルを使用して画像を表示しました。これまでキャンバスを使用したことがないので、キャンバスをどうする必要があるかわかりません。単純なラベルでは、ガベージ コレクションが発生しないように、表示している画像への参照を保持する必要があります。それは update_image() で確認できます。
  3. コールバックの場合、引数付きのラムダを使用しました(コメントで述べたように)。それ以外の場合、引数を使用して関数呼び出しを行うと、コールバックを登録する代わりにすぐに実行しています。最終的には機能しているように見えますが、思ったほどではありません。または、引数をパッケージ化し、それを呼び出されていない関数として送信する場合は、 functools.partialを使用できます。
  4. コールバックについても、ルートが破棄された後にコールバックが実行を開始する場合に備えて、try: except ブロックを追加しました。これが「正しい」方法かどうかはわかりませんが、私が知る限り機能します。

このコードを使用すると、Windows 7 で 15 FPS が得られ、エラーは発生しません。

from collections import deque
import cv2
import Image, ImageTk
import time
import Tkinter as tk

def quit_(root):
    root.destroy()

def update_image(image_label, cam):
    (readsuccessful, f) = cam.read()
    gray_im = cv2.cvtColor(f, cv2.COLOR_BGR2GRAY)
    a = Image.fromarray(gray_im)
    b = ImageTk.PhotoImage(image=a)
    image_label.configure(image=b)
    image_label._image_cache = b  # avoid garbage collection
    root.update()


def update_fps(fps_label):
    frame_times = fps_label._frame_times
    frame_times.rotate()
    frame_times[0] = time.time()
    sum_of_deltas = frame_times[0] - frame_times[-1]
    count_of_deltas = len(frame_times) - 1
    try:
        fps = int(float(count_of_deltas) / sum_of_deltas)
    except ZeroDivisionError:
        fps = 0
    fps_label.configure(text='FPS: {}'.format(fps))


def update_all(root, image_label, cam, fps_label):
    update_image(image_label, cam)
    update_fps(fps_label)
    root.after(20, func=lambda: update_all(root, image_label, cam, fps_label))


if __name__ == '__main__':
    root = tk.Tk()
    # label for the video frame
    image_label = tk.Label(master=root)
    image_label.pack()
    # camera
    cam = cv2.VideoCapture(0)
    # label for fps
    fps_label = tk.Label(master=root)
    fps_label._frame_times = deque([0]*5)  # arbitrary 5 frame average FPS
    fps_label.pack()
    # quit button
    quit_button = tk.Button(master=root, text='Quit',
                            command=lambda: quit_(root))
    quit_button.pack()
    # setup the update callback
    root.after(0, func=lambda: update_all(root, image_label, cam, fps_label))
    root.mainloop()
于 2013-03-22T08:44:29.120 に答える