【问题标题】:Keeping Up With Camera Frame Rate in Tkinter GUI在 Tkinter GUI 中跟上相机帧速率
【发布时间】:2019-10-23 15:08:46
【问题描述】:

我的目标是在 Tkinter 窗口中显示来自 USB 摄像头的实时馈送。我的问题是我似乎无法足够快地更新 GUI 以跟上相机的帧速率。我正在使用uvclite 围绕libuvc C 库的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 函数会为相机生成的每一帧调用。我看到“掉帧!”打印得非常频繁,这意味着我的 GUI 代码没有足够快地将帧从队列中拉出,frame_callback 在尝试将新帧放入队列时遇到queue.Full 异常。我尝试过使用 window.after 预定函数的延迟(第一个整数参数,以毫秒为单位),但运气不佳。

所以我的问题是:我可以做些什么来优化我的 GUI 代码以更快地从队列中拉出帧?我错过了什么明显的东西吗?

谢谢!

【问题讨论】:

  • 阅读Image.frombytes(...。我觉得你的 frame_queue 无法在 1 毫秒内传递。
  • 相机的帧率是多少?我怀疑它是每秒 1000 帧,这是您尝试更新图像的频率。
  • 我找到了一个更好的方法(灵感来自 @stovfl 的评论)。我现在将其发布为答案。

标签: python tkinter uvc


【解决方案1】:

我将这个答案发布为受@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 响应速度非常快,可以实时捕捉动作。我暂时不打算将此标记为答案,我想先看看其他人的想法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多