使用 Tkinter Canvas 小部件添加放大镜功能?

一、说明

据我所知,内置的 Tkinter Canvas 类比例不会自动缩放图像。如果您无法使用自定义小部件,则可以缩放原始图像并在调用缩放函数时将其替换在画布上。

二、实现图像放大镜技术细节

我如何将放大和缩小添加到以下脚本中,我想将其绑定到鼠标滚轮。如果您在 Linux 上测试此脚本,请不要忘记将 MouseWheel 事件更改为 Button-4 和 Button-5。

  1. 下面的代码片段可以合并到您的原始类中。它执行以下操作:缓存 Image.open() 的结果。
  2. 添加 redraw() 函数来计算缩放后的图像并将其添加到画布中,并且还会删除先前绘制的图像(如果有)。
  3. 使用鼠标坐标作为图像放置的一部分。我只是将 x 和 y 传递给 create_image 函数,以显示图像位置如何随着鼠标移动而移动。您可以将其替换为您自己的中心/偏移计算。
  4. 这使用 Linux 鼠标滚轮按钮 4 和 5(您需要将其推广到 Windows 等)。

三、代码实现

python 复制代码
from tkinter import *
from PIL import Image,ImageTk

class 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 = 0
        self.zimg_id = None

        root.bind("<MouseWheel>",self.zoomer)
        self.canvas.bind("<Motion>",self.crop)

    def zoomer(self,event):
        if (event.delta > 0):
            if self.zoomcycle != 4: self.zoomcycle += 1
        elif (event.delta < 0):
            if self.zoomcycle != 0: self.zoomcycle -= 1
        self.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.y
            if 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,200
            self.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 上使用 540x375 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 工作但过于简化的裁剪逻辑,只是为了让您开始:

python 复制代码
 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 rect
        cw, ch = iw / self.scale, ih / self.scale
        if cw > iw or ch > ih:
            cw = iw
            ch = 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)
        # draw
        self.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 路径。

相关推荐
囚生CY2 分钟前
【学习笔记】蒙特卡洛与强化学习
笔记·python·学习
Null箘25 分钟前
从零创建一个 Django 项目
后端·python·django
云空29 分钟前
《解锁 Python 数据挖掘的奥秘》
开发语言·python·数据挖掘
玖年1 小时前
Python re模块 用法详解 学习py正则表达式看这一篇就够了 超详细
python
岑梓铭1 小时前
(CentOs系统虚拟机)Standalone模式下安装部署“基于Python编写”的Spark框架
linux·python·spark·centos
游客5201 小时前
opencv中的各种滤波器简介
图像处理·人工智能·python·opencv·计算机视觉
Eric.Lee20211 小时前
moviepy将图片序列制作成视频并加载字幕 - python 实现
开发语言·python·音视频·moviepy·字幕视频合成·图像制作为视频
Dontla1 小时前
vscode怎么设置anaconda python解释器(anaconda解释器、vscode解释器)
ide·vscode·python
qq_529025292 小时前
Torch.gather
python·深度学习·机器学习
数据小爬虫@2 小时前
如何高效利用Python爬虫按关键字搜索苏宁商品
开发语言·爬虫·python