将C或numpy数组转换为具有最小份数的Tkinter PhotoImage

And*_*rew 10 python tk-toolkit numpy tkinter tcl

我知道通过Tkinter将MxNx3 numpy数组显示为RGB图像的配方,但我的配方在此过程中制作了几个数组副本:

a = np.random.randint(low=255, size=(100, 100, 3), dtype=np.uint8) # Original
ppm_header = b'P6\n%i %i\n255\n'%(a.shape[0], a.shape[1])
a_bytes = a.tobytes() # First copy
ppm_bytes = ppm_header + a_bytes # Second copy https://en.wikipedia.org/wiki/Netpbm_format
root = tk.Tk()
img = tk.PhotoImage(data=ppm_bytes) # Third and fourth copies?
canvas = tk.Canvas(root, width=a.shape[0], height=a.shape[1])
canvas.pack()
canvas.create_image(0, 0, anchor=tk.NW, image=img) # Fifth copy?
root.mainloop()
Run Code Online (Sandbox Code Playgroud)

如何以最少的份数获得相同的结果?

理想情况下,我会创建一个numpy数组,它是Tkinter PhotoImage对象使用的相同字节的视图,有效地为我提供了一个PhotoImage可变的像素值,并使更新Tkinter显示更便宜和快速.我不知道如何从Tkinter中提取这个指针.

也许有一种通过ctypes的方式,正如这里暗示的那样?

这个PhotoImage.put()方法似乎很慢,但也许我错了,那是一条前进的道路?

我尝试制作一个bytearray()包含ppm标题和图像像素值,然后使用numpy.frombuffer()查看图像像素值作为一个numpy数组,但我认为PhotoImage构造函数想要一个bytes()对象,而不是一个bytearray()对象,而且我认为Tkinter复制字节的它data输入内部格式(32位RGBA?).我猜这比上面的配方节省了一份?

Nov*_*vel 5

我可以通过使用 PIL 和一个标签将它减少到 1 个(也许 2 个)副本:

import numpy as np
import tkinter as tk
from PIL import Image, ImageTk

a = np.random.randint(low=255, size=(100, 100, 3), dtype=np.uint8) # Original
root = tk.Tk()
img = ImageTk.PhotoImage(Image.fromarray(a)) # First and maybe second copy.
lbl = tk.Label(root, image=img)
lbl.pack()
root.mainloop()
Run Code Online (Sandbox Code Playgroud)

然而,这仍然不是可变的。如果你想要,我认为你需要通过自己在画布上放置一个像素来重新创造一个图像。我在这个项目中做过一次,发现最快的更新是 matplotlib 动画,它非常适合你,因为你已经在使用 np 数组。

我使用tk.CanvasPIL Image(使用 putpixel())和matplotlib 的代码


iva*_*eev 3

  • 你可以消除第一个和第二个副本

您可以numpy.ndarray通过以下方式获得任意数据numpy.frombuffer

shape=(100,100,3)
ppm_header = b'P6\n%i %i\n255\n'%(shape[0], shape[1])
ppm_bytes = ppm_header + b'\0'*(shape[0]*shape[1]*shape[2])
array_image = np.frombuffer(ppm_bytes, dtype=np.uint8, offset=len(ppm_header)).reshape(shape)
Run Code Online (Sandbox Code Playgroud)
  • 第三个和第四个副本是不可避免的(见下文),但第三个副本在调用后立即被丢弃

  • 第五份副本实际上并未制作(另见下文)

  • 绘图阶段涉及通过窗口系统的绘图API复制到屏幕,这也是不可避免的。


Tcl 是一种像 Python 一样安全、垃圾收集的语言,并且 Tcl 对象不支持“缓冲区协议”,也不支持将内存用于它们不拥有的数据(尽管对象可以共享)。