python - 将 C 或 numpy 数组转换为具有最少副本数的 Tkinter PhotoImage
问题描述
我知道通过 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()
我怎样才能以最少的份数获得相同的结果?
理想情况下,我会创建一个 numpy 数组,它是 TkinterPhotoImage
对象正在使用的相同字节的视图,有效地为我提供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()
但是,这仍然不是可变的。如果你想要的话,我认为你需要自己在画布上放置一个像素来重塑图像。我在这个项目中做过一次,发现最快的更新是 matplotlib 动画,因为你已经在使用 np 数组,所以它对你非常有效。
我使用tk.Canvas、PIL 图像(使用 putpixel())和matplotlib的代码。
推荐阅读
- java - 在 Java 11 中关闭打开的程序后如何反序列化对象
- ruby-on-rails - Rails 5:ajax 发布请求无法正常工作
- react-native - scrollToLocation 在 react native 的 sectionlist ref 中未定义,在 flatlist 中也没有
- php - Ubuntu 18.04 Laravel 5.7 nginx 1.14 Laravel 路由不工作
- xaml - 如何使用 GeometryDrawing 在 wpf 中绘制“齿轮”形状
- angular - Angular 7:“找不到具有未指定名称属性的控件”
- linux - 获取按 mtime 排序的文件名
- xml - 使用 xpath 提取 xml 值
- react-native - undefined is not an object(evalating 'this.props.componentId') error in react native
- vue.js - Vue触发功能与子意外参数