簡體   English   中英

python opencv和tkinter捕獲網絡攝像頭問題

[英]python opencv and tkinter capture webcam problem

大家好,我有一個從網絡攝像頭讀取視頻的代碼,並使用計時器線程將其顯示到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")

這是一個復雜的問題,因為您的示例代碼是廣泛的,並且結合了許多可能出錯的內容。 我無法以當前格式測試您的代碼。

首先,您正在從不是MainThread的線程訪問Tk實例。 這可能會導致各種問題。 在Tkinter中假定的線程安全性實現中還存在一些錯誤,並且該解決方案尚未合並 如果確實需要將多個線程與Tkinter一起使用,請簽出mtTkinter ,即使這樣,還是最好不要這樣做 ,尤其是如果您正在構建一個新的應用程序並且可以選擇使用Queues或其他系統時,請不要這樣做。

其次,您創建一個threading.Thread的(子類)實例,但是您永遠不會在App.__init__調用threading.Thread.__init__ App.__init__ (如果您想將其用作Thread,這是絕對要求!)。 然后創建一個新的Threaddef showStream(self) ,而你實際上已經有一個線程。 現在,這不會破壞您的代碼,但是如果您不打算將您的類用作Thread則無需繼承threading.Thread 在類中創建線程時,無需在類中創建線程。

然后,繼續執行代碼,您確實啟動了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

這里可能有兩件事出了問題。 第一,也許你只是循環結束,因為的breakelse -clause。 Break立即退出循環代碼。 緊隨其后的任何事情都將不會執行。 如果由於無法獲取下一幀而退出循環,則無法確定該線程是否仍處於活動狀態( threading.Thread.is_alive )或是否有任何打印語句指示該循環已結束,因此無法確定。

其次,您的程序實際上可能從第二個線程訪問Tkinter時崩潰。 這樣做會導致不確定的行為,包括奇怪的錯誤和Python解釋器鎖定,因為Tk解釋器和Python解釋器正在爭奪死鎖中的流控制(簡而言之)。

最后但並非最不重要的一點是,創建圖像的方式存在問題:

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

在這一行中,您將在Canvas上創建一個新圖像。 但是,如果圖像在創建新圖像的位置的畫布上已經存在,它將顯示已顯示的圖像下方 如果您不刪除舊圖像(在任何情況下都建議這樣做,以防止大量內存泄漏 ),它將在“畫布”上保持可見。

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圖像創建代碼,並且視頻源實際上可以正常工作,那么應該可以解決。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM