在Tkinter主循环期间录制OpenCV视频

时间:2014-08-05 22:46:55

标签: python opencv video tkinter

我正在开发一个心理学实验,分析用户在完成行为任务时所做的面部表情。该应用程序主要通过Tkinter运行,我正在使用openCV来捕获视频。

在极少数情况下,我需要根据用户的反应开始和停止录制。例如,在下面的代码中,我希望用户使用鼠标指定何时开始和停止录制视频按钮。

import Tkinter as tk
import cv2

# -------begin capturing and saving video
def startrecording():
    cap = cv2.VideoCapture(0)
    fourcc = cv2.cv.CV_FOURCC(*'XVID')
    out = cv2.VideoWriter('output.avi',fourcc,  20.0, (640,480))

    while(cap.isOpened()):
        ret, frame = cap.read()
        if ret==True:
            out.write(frame)
        else:
            break

# -------end video capture and stop tk
def stoprecording():
    cap.release()
    out.release()
    cv2.destroyAllWindows()

    root.quit()
    root.destroy()

# -------configure window
root = tk.Tk()
root.geometry("%dx%d+0+0" % (100, 100))
startbutton=tk.Button(root,width=10,height=1,text='START',command = startrecording)
stopbutton=tk.Button(root,width=10,height=1,text='STOP', command = stoprecording)
startbutton.pack()
stopbutton.pack()

# -------begin
root.mainloop()

问题是OpenCV使用循环来录制视频,在此期间Tkinter无法监听用户响应。该程序陷入OpenCV循环,用户无法继续。 如何同时录制视频和收听用户回复?

我研究过并行处理(例如Display an OpenCV video in tkinter using multiprocessing),但这听起来似乎是一项比看似必要的更大的努力。

我也研究过使用root.after命令(例如Show webcam sequence TkInter),但是使用它看起来你只能捕获一个帧,而我想要一个视频。

还有其他方法吗?我需要使用两个处理流吗?

1 个答案:

答案 0 :(得分:3)

通过multiprocessing处理此问题比您想象的要容易:

import multiprocessing
import Tkinter as tk
import cv2

e = multiprocessing.Event()
p = None

# -------begin capturing and saving video
def startrecording(e):
    cap = cv2.VideoCapture(0)
    fourcc = cv2.cv.CV_FOURCC(*'XVID')
    out = cv2.VideoWriter('output.avi',fourcc,  20.0, (640,480))

    while(cap.isOpened()):
        if e.is_set():
            cap.release()
            out.release()
            cv2.destroyAllWindows()
            e.clear()
        ret, frame = cap.read()
        if ret==True:
            out.write(frame)
        else:
            break

def start_recording_proc():
    global p
    p = multiprocessing.Process(target=startrecording, args=(e,))
    p.start()

# -------end video capture and stop tk
def stoprecording():
    e.set()
    p.join()

    root.quit()
    root.destroy()

if __name__ == "__main__":
    # -------configure window
    root = tk.Tk()
    root.geometry("%dx%d+0+0" % (100, 100))
    startbutton=tk.Button(root,width=10,height=1,text='START',command=start_recording_proc)
    stopbutton=tk.Button(root,width=10,height=1,text='STOP', command=stoprecording)
    startbutton.pack()
    stopbutton.pack()

    # -------begin
    root.mainloop()

我们所做的就是添加对multiprocessing.Process的调用,以便您的视频捕获代码在子进程中运行,并且在捕获完成到该进程时也移动代码进行清理。与单进程版本相比,唯一的额外皱纹是使用multiprocessing.Event在关闭时间时向子进程发出信号,这是必要的,因为父进程无法访问{{ 1}}或out

您可以尝试使用cap代替(只需将threading替换为multiprocessing.Process,将threading.Thread替换为multiprocessing.Event),但我怀疑GIL会绊倒你起来并损害了GUI线程的性能。出于同样的原因,我认为值得尝试通过threading.Event将读取/写入流集成到您的事件循环中 - 它只会损害性能,并且从那时起你并没有试图将你正在做的事情整合到GUI本身中,没有理由试图将它与事件循环保持在同一个线程/进程中。