Моя цель - отображать в режиме реального времени канал с USB-камеры в окне Tkinter. Моя проблема в том, что я не могу обновить графический интерфейс достаточно быстро, чтобы не отставать от частоты кадров камеры. Я подключаюсь к камере с помощью оболочки uvclite python вокруг библиотеки libuvc C. uvclite - это легкая оболочка ctypes для базовой библиотеки C, поэтому я не думаю, что этот кусок - мое узкое место. Вот мой код:
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
(первый целочисленный аргумент, единицы миллисекунд), но мне не повезло.
Итак, мой вопрос: Что я могу сделать, чтобы оптимизировать мой код графического интерфейса, чтобы быстрее выводить кадры из очереди? Я что-то упускаю из виду?
Спасибо!