一、说明
据我所知,内置的 Tkinter Canvas 类比例不会自动缩放图像。如果您无法使用自定义小部件,则可以缩放原始图像并在调用缩放函数时将其替换在画布上。
二、实现图像放大镜技术细节
我如何将放大和缩小添加到以下脚本中,我想将其绑定到鼠标滚轮。如果您在 Linux 上测试此脚本,请不要忘记将 MouseWheel 事件更改为 Button-4 和 Button-5。
- 下面的代码片段可以合并到您的原始类中。它执行以下操作:缓存 Image.open() 的结果。
- 添加 redraw() 函数来计算缩放后的图像并将其添加到画布中,并且还会删除先前绘制的图像(如果有)。
- 使用鼠标坐标作为图像放置的一部分。我只是将 x 和 y 传递给 create_image 函数,以显示图像位置如何随着鼠标移动而移动。您可以将其替换为您自己的中心/偏移计算。
- 这使用 Linux 鼠标滚轮按钮 4 和 5(您需要将其推广到 Windows 等)。
三、代码实现
from tkinter import *from PIL import Image,ImageTkclass LoadImage:def __init__(self,root):frame = Frame(root)self.canvas = Canvas(frame,width=900,height=900)self.canvas.pack()frame.pack()File = "d001.jpg"self.orig_img = Image.open(File)self.img = ImageTk.PhotoImage(self.orig_img)self.canvas.create_image(0,0,image=self.img, anchor="nw")self.zoomcycle = 0self.zimg_id = Noneroot.bind("",self.zoomer)self.canvas.bind("",self.crop)def zoomer(self,event):if (event.delta > 0):if self.zoomcycle != 4: self.zoomcycle += 1elif (event.delta < 0):if self.zoomcycle != 0: self.zoomcycle -= 1self.crop(event)def crop(self,event):if self.zimg_id: self.canvas.delete(self.zimg_id)if (self.zoomcycle) != 0:x,y = event.x, event.yif self.zoomcycle == 1:tmp = self.orig_img.crop((x-45,y-30,x+45,y+30))elif self.zoomcycle == 2:tmp = self.orig_img.crop((x-30,y-20,x+30,y+20))elif self.zoomcycle == 3:tmp = self.orig_img.crop((x-15,y-10,x+15,y+10))elif self.zoomcycle == 4:tmp = self.orig_img.crop((x-6,y-4,x+6,y+4))size = 300,200self.zimg = ImageTk.PhotoImage(tmp.resize(size))self.zimg_id = self.canvas.create_image(event.x,event.y,image=self.zimg)if __name__ == '__main__':root = Tk()root.title("Crop Test")App = LoadImage(root)root.mainloop()
四、关于内存问题
更新我对不同的比例进行了一些测试,发现调整大小/创建图像使用了相当多的内存。我在配备 32GB RAM 的 Mac Pro 上使用 540×375 JPEG 进行了测试。以下是不同比例因子使用的内存:
1x (500, 375) 14 M
2x (1000, 750) 19 M
4x (2000, 1500) 42 M
8x (4000, 3000) 181 M
16x (8000, 6000) 640 M
32x (16000, 12000) 1606 米
64x (32000, 24000) …
达到约 7400 M 并耗尽内存,_memcpy 中的 EXC_BAD_ACCESS
鉴于上述情况,更有效的解决方案可能是确定将显示图像的视口的大小,计算鼠标坐标中心周围的裁剪矩形,使用矩形裁剪图像,然后仅缩放裁剪部分。这应该使用常量内存来存储临时图像。否则,您可能需要使用第 3 方 Tkinter 控件来为您执行此裁剪/窗口缩放。
更新 2 工作但过于简化的裁剪逻辑,只是为了让您开始:
def redraw(self, x=0, y=0):if self.img_id: self.canvas.delete(self.img_id)iw, ih = self.orig_img.size# calculate crop rectcw, ch = iw / self.scale, ih / self.scaleif cw > iw or ch > ih:cw = iwch = ih# crop it_x = int(iw/2 - cw/2)_y = int(ih/2 - ch/2)tmp = self.orig_img.crop((_x, _y, _x + int(cw), _y + int(ch)))size = int(cw * self.scale), int(ch * self.scale)# drawself.img = ImageTk.PhotoImage(tmp.resize(size))self.img_id = self.canvas.create_image(x, y, image=self.img)gc.collect()
只是为了其他发现这个问题的人的利益,我附上了我的最终测试代码,该代码使用画中画/放大镜缩放。它基本上只是对已经发布的样本偏差的更改。看到它也很酷:)。
正如我之前所说,如果您在 Linux 上使用此脚本,请不要忘记将 MouseWheel 事件更改为 Button-4 和 Button-5。显然,您需要在显示“INSERT JPG FILE PATH”的位置插入 .JPG 路径。