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?).我猜这比上面的配方节省了一份?
我可以通过使用 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.Canvas、PIL Image(使用 putpixel())和matplotlib 的代码。
您可以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 对象不支持“缓冲区协议”,也不支持将内存用于它们不拥有的数据(尽管对象可以共享)。
Run Code Online (Sandbox Code Playgroud)img = tk.PhotoImage(data=ppm_bytes) # Third and fourth copies?
在进行大多数 Tcl 调用时,Python 变量首先被转换为等效的 Tcl 对象(首先转换为不涉及复制字节串的C 值,然后将其传递给涉及复制的Tcl 构造函数),然后将这些对象传递给Tcl_EvalObjv.
在 Tcl 方面,photo(PhotoImage()包装)还解析输入数据(-data字符串参数),因此也无法重用其内存块。即使它是原始位图,因为 Tcl 字符串没有“查看”功能。
因此,对于字节串,Pythob 到 Tcl 调用阶段涉及位图数据的一份强制副本,并且在图像构建阶段还有一份。(好的一面是,调用后会丢弃一份副本(Tcl 调用的参数字符串)。)
Run Code Online (Sandbox Code Playgroud)canvas.create_image(0, 0, anchor=tk.NW, image=img) # Fifth copy?
这里不涉及副本,画布只是在其合成数据中保存对图像对象的引用。
现在,canvas create image需要一个 Tcl 图像标识符作为image参数;所以也没有办法PhotoImage()。
(绘图阶段)
xlib使用绘制位图的命令,不涉及额外的副本。
这里需要注意的是,画布甚至无法直接访问生成的屏幕像素。相反,使用后端的图形 API 进行绘图(例如您可能熟悉的xlibWinGDIGetDC()等)。BitBlt()例如,在 Windows 中,xlib使用XCopyAreaBitBlt.
| 归档时间: |
|
| 查看次数: |
920 次 |
| 最近记录: |