如何使用canvas.scale()实现canvas.create_image()创建图像的缩放功能
修复方案与代码实现
问题根因
你原来的缩放逻辑不生效核心是三个问题:
- 你在
update方法中每15ms就重新调用create_image绘制原始尺寸的新帧,之前对画布做的scale操作结果直接被新绘制的原图覆盖 - 反复调用
create_image会在画布上叠加大量无用图像对象,既占用内存也会导致显示异常 - 原代码缺少
import tkinter as tk的导入声明,且用了内置关键字input作为变量名,运行会直接报错
推荐实现方案(缩放作用于源图像,更稳定)
我们直接把缩放逻辑作用在读取到的摄像头帧上,用PIL的resize方法实现,同时支持鼠标事件绑定和按钮触发两种方式,不会被刷新逻辑覆盖:
核心修改点
- 初始化时新增缩放系数变量,存储当前缩放比例,新增缩放上下限避免画面过小/过大
- 仅首次绘制时调用
create_image,后续刷新直接更新已有图像对象的内容,避免重复创建造成内存泄漏 - 缩放操作直接修改缩放系数,帧刷新时自动按照当前系数对原始图像做缩放
- 缩放方法兼容事件触发和按钮触发两种调用方式
修改后完整代码
import tkinter as tk from tkinter import ttk import cv2 from PIL import ImageTk from PIL import Image import numpy as np from sense_hat import SenseHat import tkinter.font as tkFont baf_pixel = 0 input_flag = False # 替换原input变量名,避免覆盖内置函数 class APP: def __init__(self,window,video_source=0): self.input_flag = False self.sense = SenseHat() self.pitch, self.roll, self.yaw = self.sense.get_orientation().values() self.window = window self.window.title('Relaskop_pi') self.window.geometry('800x480') self.window.attributes("-alpha",1) self.video_source = video_source self.cap = MyVideoCap(self.video_source) # 新增缩放相关变量 self.zoom_factor = 1.0 self.min_zoom = 0.2 self.max_zoom = 3.0 self.image_id = None # 存储画布上的图像ID,避免重复创建 #create canvas that contain the camera self.canvas = tk.Canvas(self.window,width=525,height=480) self.canvas.place(x = 135,y = 0) # 按钮控制缩放(不需要事件绑定可以删掉下面bind的部分) self.zoom_in_btn = tk.Button(self.window, text="放大", command=self.zoomMag) self.zoom_in_btn.place(x=10, y=300, width=50, height=30) self.zoom_out_btn = tk.Button(self.window, text="缩小", command=self.zoomNarrow) self.zoom_out_btn.place(x=70, y=300, width=50, height=30) self.update() self.canvas.bind("<ButtonPress-1>", self.move_start) self.canvas.bind("<B1-Motion>", self.move_to) self.canvas.bind("<Button-4>", self.zoomMag)#鼠标滚轮放大 self.canvas.bind("<Button-5>", self.zoomNarrow)#鼠标滚轮缩小 self.window.mainloop() # 拖拽移动画布逻辑 def move_start(self, event): self.canvas.scan_mark(event.x, event.y) def move_to(self, event): self.canvas.scan_dragto(event.x, event.y, gain = 1) # 缩放逻辑 def zoomNarrow(self, event=None): if self.zoom_factor > self.min_zoom: self.zoom_factor *= 0.9 def zoomMag(self, event=None): if self.zoom_factor < self.max_zoom: self.zoom_factor *= 1.1 # 画面刷新逻辑 def update(self): ret, frame = self.cap.get_frame() if ret == True: font_p = tkFont.Font(family = "Biaukai", size = 12) lab_p=tk.Label(self.window, text="pitch:"+ str(round(270-self.pitch, 2)), justify='left',width=12,height=2,font= font_p,bg='red',anchor='nw').place(x=0,y=173) lab_r=tk.Label(self.window, text="roll:" + str(round(self.roll, 1)), justify='left',width='10',height='1',font=('Biaukai',14),bg='red',anchor='nw').place(x=0,y=202) lab_y=tk.Label(self.window, text="yaw:" + str(round(self.yaw, 1)), justify='left',width='10',height='1',font=('Biaukai',14),bg='red',anchor='nw').place(x=0,y=231) self.pitch, self.roll, self.yaw = self.sense.get_orientation().values() if self.input_flag == True: self.getBAF() # 原始帧转PIL图像后按当前比例缩放 img=Image.fromarray(frame) new_width = int(img.width * self.zoom_factor) new_height = int(img.height * self.zoom_factor) resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS) self.imgtk=ImageTk.PhotoImage(image=resized_img) # 复用已有图像ID更新内容 if self.image_id is None: self.image_id = self.canvas.create_image(0, 0, image = self.imgtk, anchor = tk.NW) else: self.canvas.itemconfig(self.image_id, image=self.imgtk) self.window.after(15,self.update) # 补充原逻辑缺失的getBAF方法,你可以自行填充内部计算逻辑 def getBAF(self): global baf_pixel pass class MyVideoCap: def __init__(self, video_source=0): #open the video source self.cap=cv2.VideoCapture(video_source) if not self.cap.isOpened(): raise ValueError("Unable to open video source", video_source) def get_frame(self): if self.cap.isOpened(): ret, frame=self.cap.read() frame=cv2.flip(frame,1) if ret == True: return (ret, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) else: return (ret, None) else: return (False, None) # Release the video source when the object is destroyed def __del__(self): if self.cap.isOpened(): self.cap.release() if __name__=="__main__": APP(tk.Tk())
内容的提问来源于stack exchange,提问作者Leo Hsu
相关产品推荐
相关产品推荐

