簡體   English   中英

在 Tkinter GUI 中跟上相機幀速率

[英]Keeping Up With Camera Frame Rate in Tkinter GUI

我的目標是在 Tkinter Window 中顯示來自 USB 相機的實時饋送。 我的問題是我似乎無法足夠快地更新 GUI 以跟上相機的幀速率。 我正在使用圍繞libuvc C 庫的uvclite python 包裝器與相機連接。 uvclite 是圍繞底層 C 庫的輕量級 ctypes 包裝器,所以我認為這不是我的瓶頸。 這是我的代碼:

import tkinter as tk
from PIL import ImageTk, Image
import uvclite
import io
import queue

frame_queue = queue.Queue(maxsize=5)
# frame_queue = queue.LifoQueue(maxsize=5)
user_check = True

def frame_callback(in_frame, user):
    global user_check
    if user_check:
        print("User id: %d" % user)
        user_check = False
    try:
        # Dont block in the callback!
        frame_queue.put(in_frame, block=False)
    except queue.Full:
        print("Dropped frame!")
        pass

def update_img():
    print('getting frame')
    frame = frame_queue.get(block=True, timeout=None)
    img = ImageTk.PhotoImage(Image.open(io.BytesIO(frame.data)))
    panel.configure(image=img)
    panel.image = img
    print("image updated!")
    frame_queue.task_done()
    window.after(1, update_img)


if __name__ == "__main__":

    with uvclite.UVCContext() as context:
        cap_dev = context.find_device()
        cap_dev.set_callback(frame_callback, 12345)
        cap_dev.open()
        cap_dev.start_streaming()

        window = tk.Tk()
        window.title("Join")
        window.geometry("300x300")
        window.configure(background="grey")

        frame = frame_queue.get(block=True, timeout=None)
        # Creates a Tkinter-compatible photo image, which can be used everywhere Tkinter expects an image object.
        img = ImageTk.PhotoImage(Image.open(io.BytesIO(frame.data)))
        panel = tk.Label(window, image=img)
        frame_queue.task_done()
        panel.pack(side="bottom", fill="both", expand="yes")
        window.after(1, update_img)
        window.mainloop()

        print("Exiting...")
        cap_dev.stop_streaming()
        print("Closing..")
        cap_dev.close()
        print("Clear Context")

每幀都是一個完整的 JPEG 圖像,存儲在bytearray中。 frame_callback function 會為相機生成的每一幀調用。 我看到“丟幀”的打印頻率很高,這意味着我的 GUI 代碼沒有足夠快地將幀從隊列中拉出,並且frame_callback遇到了隊列。嘗試將新幀放入隊列時出現queue.Full異常。 我嘗試過使用 window 的延遲。在預定的window.after之后(第一個 integer 參數,以毫秒為單位),但運氣不佳。

所以我的問題是:我可以做些什么來優化我的 GUI 代碼以更快地從隊列中拉出幀? 我錯過了一些明顯的東西嗎?

謝謝!

我將其發布為受@stovfl 對該問題的評論啟發的答案,但我仍然很想知道其他人會如何解決這個問題。

他的評論指出我的frame_queue在 1 毫秒內調用frame_queue.get()時可能無法傳遞幀,所以我只是從 GUI 更新代碼中完全刪除了隊列。 相反,我直接從回調中調用 GUI 更新代碼。 這是新代碼:

import tkinter as tk
from PIL import ImageTk, Image
import uvclite
import io

user_check = True

def frame_callback(in_frame, user):
    global user_check
    if user_check:
        print("User id: %d" % user)
        user_check = False
    img = ImageTk.PhotoImage(Image.open(io.BytesIO(in_frame.data)))
    panel.configure(image=img)
    panel.image = img

if __name__ == "__main__":

    with uvclite.UVCContext() as context:
        cap_dev = context.find_device()
        cap_dev.set_callback(frame_callback, 12345)
        cap_dev.open()
        cap_dev.start_streaming()

        window = tk.Tk()
        window.title("Join")
        window.geometry("300x300")
        window.configure(background="grey")
        panel = tk.Label(window)
        panel.pack(side="bottom", fill="both", expand="yes")

        window.mainloop()
        print("Exiting...")
        cap_dev.stop_streaming()
        print("Closing..")
        cap_dev.close()
        print("Clear Context")

這很好用,而且 GUI 響應非常靈敏,可以實時捕捉動作。 我還不打算將此標記為答案,我想先看看其他人想出什么。

暫無
暫無

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

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