python - Tkinter:图像循环减慢
问题描述
我编写了一个 Tkinter GUI,它应该通过循环遍历每个 3D 图像的 2D 切片来显示显示 3D 图像。我阅读了如何实现这样的图像循环,建议使用after()
递归函数。原则上,这是我的实现:
def load_image(self):
self.stack = read_3D_image(path)
slice = self.stack[self.slice_no]
im = Image.fromarray(slice)
self.photo = ImageTk.PhotoImage(image=im)
self.canvas.create_image(0, 0, image=self.photo, anchor=tk.NW)
if self.forward is True:
self.slice_no += 1
if self.slice_no == 21:
self.forward = False
if self.forward is False:
self.slice_no -= 1
if self.slice_no == 0:
self.forward = True
root.after(10, self.load_image)
这在一段时间内运行良好,但几分钟后,循环明显变慢。我想这是因为迭代次数多。有没有办法来解决这个问题?
编辑:我注意到这一点:当程序运行时,图像循环将在大约 10 分钟后减慢到原始频率的一半左右。当我运行第二个实例时,它的循环运行同样慢。然后当我关闭第一个实例时,第二个实例循环立即运行得更快。我从 Eclipse 启动。
更新了完整代码
import glob
import os.path
import tkinter as tk
from PIL import Image, ImageTk
import numpy as np
import helpers
class App():
def __init__(self, master):
super().__init__()
self.frame = tk.Frame(master)
master.bind("<KP_1>", lambda e: self.is_cell())
master.bind("<KP_2>", lambda e: self.is_not_cell())
self.frame.pack()
self.goto_button = tk.Button(
self.frame, text="Go to:", command=self.goto)
self.goto_button.pack(side=tk.TOP)
self.goto_entry = tk.Entry(self.frame, width=5)
self.goto_entry.pack(side=tk.TOP)
self.img_side_length = 100
self.canvas = tk.Canvas(
master=self.frame, width=self.img_side_length, height=self.img_side_length)
self.canvas.pack()
self.img_label = tk.Label(self.frame, text="Bla")
self.img_label.pack(side=tk.TOP)
self.no_cell_button = tk.Button(
self.frame, text="2: Not cell!", command=self.is_not_cell)
self.no_cell_button.pack(side=tk.RIGHT)
self.cell_button = tk.Button(
self.frame, text="1: Cell!", command=self.is_cell)
self.cell_button.pack(side=tk.RIGHT)
self.img_path = "/storage/images/"
self.img_list = glob.glob(os.path.join(self.img_path, "*"))
self.img_list.sort()
self.slice_no = 0
self.img_no = 0
self.forward = True
self.no_of_imgs = len(self.img_list)
self.stack = []
self.image_id = self.canvas.create_image(0, 0, anchor=tk.NW)
self.stack = helpers.read_image_sequence(self.img_list[self.img_no])
self.classifications = np.zeros(self.no_of_imgs)
self.out_path = "/dev/null"
self.loop_image()
def loop_image(self):
data = self.stack[self.slice_no]
im = Image.fromarray(data)
im = im.resize((self.img_side_length, self.img_side_length))
self.photo = ImageTk.PhotoImage(image=im)
self.canvas.itemconfigure(self.image_id, image=self.photo)
if self.forward is True:
self.slice_no += 1
if self.slice_no == 21:
self.forward = False
if self.forward is False:
self.slice_no -= 1
if self.slice_no == 0:
self.forward = True
root.after(10, self.loop_image)
def next_image(self):
self.img_no += 1
self.stack = helpers.read_image_sequence(self.img_list[self.img_no])
self.img_label['text'] = self.img_list[self.img_no].split("/")[-1]
def previous_image(self):
self.img_no -= 1
self.stack = helpers.read_image_sequence(self.img_list[self.img_no])
self.img_label['text'] = self.img_list[self.img_no].split("/")[-1]
def is_cell(self):
self.classifications[self.img_no] = 1
with open(self.out_path, "a") as f:
f.write(str(self.img_no) + "," + str(1) + "\n")
print(self.classifications)
self.next_image()
def is_not_cell(self):
self.classifications[self.img_no] = 2
with open(self.out_path, "a") as f:
f.write(str(self.img_no) + "," + str(2) + "\n")
print(self.classifications)
self.next_image()
def goto(self):
self.img_no = int(self.goto_entry.get())
root = tk.Tk()
app = App(root)
root.mainloop()
解决方案
您每秒创建 100 个图像并将它们堆叠在一起。10 分钟后,60,000 张图像堆叠在一起。当画布上有数以万计的项目时,即使只有一个是不可见的,画布也会出现性能问题。
无需在画布上创建越来越多的图像,只需修改现有图像:
def __init__(self):
...
self.canvas = tk.Canvas(...)
...
# create the image object which will display the image
self.image_id = self.canvas.create_image(0, 0, anchor=tk.NW)
def load_image(self):
...
self.photo = ImageTk.PhotoImage(image=im)
# reconfigure the canvas item to show the new image
canvas.itemconfigure(self.image_id, image=self.photo)
...
推荐阅读
- flowtype - flow.js createSlice 访问通用键
- append - 需要在 TCL 中 cat 多个文件
- amazon-s3 - Streamlit 集成和限制
- python - 如何从 impala 数据集市显示列名
- c# - 如何公开 C# .net 核心 Web Api 端点以供其他应用程序调用?
- powerbi - 在 Power BI 中筛选表行
- javascript - 如何从 Obj 加载下拉菜单
- spring-boot - 如何将以下 mongo 集合构建到 redis 缓存中?
- node.js - 错误:无法启动浏览器进程!傀儡师
- html - 无法在html中获取多个复选框的值