【问题标题】:What's a faster alternative to PIL.ImageTk.PhotoImage?PIL.ImageTk.PhotoImage 的更快替代方案是什么?
【发布时间】:2021-03-14 13:44:02
【问题描述】:

我正在尝试显示视频,并且使用示例/解决方案来解决有关如何在 Tkinter 上播放视频的问题似乎无法实现流畅和快速的渲染。

例如:

frame = self.vid_cap.read()
frame = cv2.resize(frame, (696, 486))
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

self.latest_img_ = Image.fromarray(frame)
self.latest_img_ = ImageTk.PhotoImage(image=self.latest_img_)

self.canvas.create_image(0, 0, anchor="nw", image=self.latest_img_)

上面的代码可以工作,但是视频太慢了,即使使用self.canvas.after(1, self.video_loop_) 之类的代码也是如此。经过一些基准测试,结果是以下行:

self.latest_img_ = ImageTk.PhotoImage(image=self.latest_img_)

无论我在四核 mac 还是 12 核设备上运行代码,都需要 9ms ~ 100ms,而上面的整个代码(从捕获到将 numpy 数组转换为图像)只需要 0ms 到 1ms .

有什么比ImageTk.PhotoImage更快的替代方案?

【问题讨论】:

  • 您是每 1 毫秒创建一个新图像还是重复使用同一个画布对象?这有很大的不同。
  • @HenryYik 我认为没有任何东西被重用(除了 Canvas 对象)。我在问题中包含的部分代码与我在项目中的代码几乎相同。你能分享一个如何重用图像对象的例子吗?
  • 请提供minimal reproducible example。 Tkinter 本身不应该用于显示视频,而是尝试着看集成其他小部件
  • 对于那个尺寸的图像,ImageTk.PhotoImage() 在我的 MacBook Pro 上需要 823 微秒,即不到一毫秒。不知道发生了什么。

标签: python tkinter python-imaging-library tkinter-canvas opencv-python


【解决方案1】:

您应该保留canvas 对象的引用并对其进行修改,而不是每次都创建新对象。为此,您可以首先通过传递image=None 创建一个占位符。以下是一个最小样本:

import tkinter as tk
import cv2
from PIL import Image, ImageTk


class GUI(tk.Tk):
    def __init__(self):
        super().__init__()
        self.canvas = tk.Canvas(self)
        self.canvas.pack(fill="both", expand=1)
        self.streaming_image = self.canvas.create_image(0, 0, anchor="nw", image=None)

    def show_frame(self):
        _, frame = self.vid_cap.read()
        frame = cv2.resize(frame, (696, 486))
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        img = Image.fromarray(frame)
        self.latest_img = ImageTk.PhotoImage(image=img)
        self.canvas.itemconfig(self.streaming_image, image=self.latest_img)
        self.after(10, self.show_frame)

    def start_video(self, path=None):
        self.vid_cap = cv2.VideoCapture(path)
        self.show_frame()


root = GUI()
root.start_video("your_path_here")
root.mainloop()

【讨论】:

  • 谢谢。我将您的代码作为测试运行,它运行良好,但是当我尝试在我的应用程序上使用相同的解决方案时,它仍然很慢。最终,我意识到唯一的区别是我使用的是使用线程/队列的自定义 VideoCapture,当我用 cv2.VideoCapture 替换它时,它播放视频的速度非常快。
  • 这对我来说仍然没有意义,因为当我测量执行时间时,只有ImageTk.PhotoImage(image=img) 需要很长时间并且它不在任何新线程中。但这是另一个问题,我相信。
  • 实际上现在当我在画布上设置宽度和高度时,它确实又变慢了。 :( 如果您将self.canvas = tk.Canvas(self) 替换为self.canvas = tk.Canvas(self, width=696, height=486),您是否也会遇到此问题?
  • 不,设置高度和宽度后我看不到性能变化。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-06
  • 2018-12-11
  • 2012-07-05
  • 2012-01-23
  • 2013-07-13
  • 2011-02-27
相关资源
最近更新 更多