【问题标题】:python opencv and tkinter capture webcam problempython opencv和tkinter捕获网络摄像头问题
【发布时间】:2019-02-19 23:21:21
【问题描述】:

大家好,我有一个用于从网络摄像头读取视频的代码,并通过定时器线程将其显示到 tkinter 窗口中。 当用户单击显示按钮时,应用程序会创建一个线程并每隔 x 秒运行一次以逐帧显示。 这是问题所在:每几帧应用程序都会显示从视频源捕获的第一帧。 我知道这很奇怪,但我不知道为什么!!! 这是我的代码:

import tkinter as tk
from tkinter import ttk 
from tkinter import *
import threading
import cv2
import PIL.Image, PIL.ImageTk
from PIL import Image
from PIL import ImageTk
import time

class App(threading.Thread):
        def __init__(self, root, window_title, video_source=0):
            self.root = root
            self.root.title(window_title)
            self.video_source = video_source
            self.show_switch=False
            self.showButton = Button(self.root, text="PlayStream",command=self.showStram,width=15, padx="2", pady="3",compound=LEFT)
            self.showButton.pack()
            # Create a canvas that can fit the above video source size
            self.canvas = tk.Canvas(root, width = 530, height = 397, cursor="crosshair")
            self.canvas.pack()
            self.root.mainloop()

        def updateShow(self):

            # Get a frame from the video source
            cap=cv2.VideoCapture(0)
            while True:    
                if(cap.isOpened()):
                    #read the frame from cap
                    ret, frame = cap.read()

                    if ret:

                        #show frame in main window
                        self.photo = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(frame))
                        self.canvas.create_image(0, 0, image = self.photo, anchor = tk.NW)


                    else:
                        break
                        raise ValueError("Unable to open video source", video_source)


                    if self.show_switch==False:
                        cap.release()
                        return False
                time.sleep(0.0416666666666667)

            #release the cap
            cap.release()

        def showStram(self):

            if self.show_switch:
                self.showButton["text"]="StartStream"
                # self.showButton.configure(image=self.playIcon)
                self.show_switch=False

            else:
                self.showButton["text"]="StopStream"
                self.show_switch=True
                # self.showButton.configure(image=self.stopIcon)
                self.showTimer=threading.Thread(target=self.updateShow,args=())
                #self.showTimer.daemon=True
                self.showTimer.start()

App(tk.Tk(), "Main")

【问题讨论】:

标签: python opencv tkinter


【解决方案1】:

这是一个复杂的问题,因为您的示例代码很广泛,并且结合了多个可能出错的内容。我无法以当前形式测试您的代码。

首先,您正在从一个不是 MainThread 的线程访问您的 Tk 实例。这可能会导致各种问题。在 Tkinter 和 solution has not yet been merged 中实现假定的线程安全也存在​​错误。如果您确实需要在 Tkinter 中使用多线程,请查看 mtTkinter,即使那样,最好不要这样做,尤其是如果您正在构建一个新应用程序并且可以选择使用队列或其他一些系统。

其次,你创建了一个threading.Thread 的(子类)实例,但你永远不会在App.__init__ 中调用threading.Thread.__init__(如果你想将它用作线程,这是绝对要求!)。然后你在def showStream(self) 中创建一个新的Thread,而实际上你已经有了一个线程。现在,这不会破坏您的代码,但如果您不打算将您的类用作Thread,则不需要子类化threading.Thread。在类中创建线程时,无需在类中创建线程。

然后,继续执行您的代码,您确实启动了线程,因此updateShow 开始运行。但是,在您的 while 循环中,存在一个问题:

while True:
if (cap.isOpened()):
    ret, frame = cap.read()
    if ret:
        ...
    else:
        break
        # Error will never be raised because of break
        # break exits the loop immediately, so the next line is never evaluated 
        raise ValueError()
cap.release()
# No notification of the loop having ended

这里可能有两个问题。首先是因为else 子句中的break,您的循环可能刚刚结束。 Break 立即退出循环代码。它后面的任何东西都将永远被执行。如果由于未能获得下一帧而退出循环,则您无法确定,因为您没有检查线程是否还活着(threading.Thread.is_alive)或有任何打印语句表明循环已结束。

其次,您的程序实际上可能会在您从第二个线程访问 Tkinter 时出现硬崩溃。这样做会导致未定义的行为,包括奇怪的错误和 Python 解释器锁定,因为 Tk 解释器和 Python 解释器在死锁中争夺流控制(简单地说)。

最后但并非最不重要的一点是,您创建图像的方式存在问题:

self.canvas.create_image(0, 0, image = self.photo, anchor = tk.NW)

在这一行中,您在画布上创建了一个新图像。但是,如果图像已经存在于您正在创建新图像的位置的画布上,它将显示在已显示的图像下方。如果您不删除旧图像(无论如何建议这样做以防止巨大的内存泄漏),它将在画布上保持可见。

def __init__(...):
    ...
    self.im = None

def updateShow(self):
    ...
    while True:
        if (cap.isOpened()):
            ...
            if ret:
                if self.im is not None:
                    self.canvas.delete(self.im)
                ...
                self.im: str = self.canvas.create_image(0, 0, image=self.photo, anchor=tk.NW)

总结:使用您当前发布的代码,有很多地方可能会出错。没有额外的信息,是不可能知道的。但是,如果您修复了从不同线程访问 Tkinter,调整您的 while 循环以不中断但引发错误,您调整您的 Canvas 图像创建代码并且您的视频源确实可以正常工作,它应该可以工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-05
    • 1970-01-01
    • 2012-06-16
    • 2011-01-03
    相关资源
    最近更新 更多