在 Tkinter GUI 中跟上相机帧速率 [英] Keeping Up With Camera Frame Rate in Tkinter GUI

查看:29
本文介绍了在 Tkinter GUI 中跟上相机帧速率的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的目标是在 Tkinter 窗口中显示来自 USB 摄像头的实时提要.我的问题是我似乎无法足够快地更新 GUI 以跟上相机的帧速率.我正在使用 uvclite python 包装器围绕 libuvc C 库.uvclite 是围绕底层 C 库的轻量级 ctypes 包装器,所以我不认为那部分是我的瓶颈.这是我的代码:

My goal is to display a real time feed from a USB camera in a Tkinter Window. My problem is that I can't seem to update the GUI fast enough to keep up with the frame rate of the camera. I'm interfacing with the camera using the uvclite python wrapper around the libuvc C library. uvclite is a lightweight ctypes wrapper around the underlying C library, so I don't think that piece is my bottleneck. Here is my code:

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 计划函数(第一个整数参数,毫秒单位)上玩延迟,但运气不佳.

Each frame is a complete JPEG image, stored in a bytearray. The frame_callback function gets call for every frame generated by the camera. I see "Dropped frame!" printed quite frequently, meaning my GUI code isn't pulling frames off of the queue fast enough and frame_callback encounters the queue.Full exception when trying to put new frames onto the queue. I've tried playing with the delay on the window.after scheduled function (first integer argument, units of milliseconds), but haven't had much luck.

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

So my question is: What can I do to optimize my GUI code to pull frames off of the queue faster? Am I missing something obvious?

谢谢!

推荐答案

我发布这个答案是受到@stovfl 对该问题的评论的启发,但我仍然很想知道其他人会如何处理这个问题.

I'm posting this as an answer inspired by @stovfl's comment on the question, but I'm still curious to see how other people would approach this.

他的评论指出我的frame_queue在1毫秒内调用frame_queue.get()时可能无法传送帧,所以我只是从GUI更新代码中删除了队列完全.相反,我直接从回调中调用 GUI 更新代码.这是新代码:

His comment pointed out that my frame_queue probably cannot deliver frames when calling frame_queue.get() within 1 millisecond, so I just removed the queue from the GUI update code entirely. Instead, I call the GUI updating code from the callback directly. Here is the new code:

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 反应灵敏,可以实时捕捉动作.我暂时不打算将此标记为答案,我想看看其他人首先提出了什么.

This works quite well, and the GUI is very responsive and captures motion in real time. I'm not going to mark this as an answer just yet, I would like to see what other people come up with first.

这篇关于在 Tkinter GUI 中跟上相机帧速率的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆