You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用canvas.scale()实现canvas.create_image()创建图像的缩放功能

修复方案与代码实现

问题根因

你原来的缩放逻辑不生效核心是三个问题:

  • 你在update方法中每15ms就重新调用create_image绘制原始尺寸的新帧,之前对画布做的scale操作结果直接被新绘制的原图覆盖
  • 反复调用create_image会在画布上叠加大量无用图像对象,既占用内存也会导致显示异常
  • 原代码缺少import tkinter as tk的导入声明,且用了内置关键字input作为变量名,运行会直接报错

推荐实现方案(缩放作用于源图像,更稳定)

我们直接把缩放逻辑作用在读取到的摄像头帧上,用PIL的resize方法实现,同时支持鼠标事件绑定和按钮触发两种方式,不会被刷新逻辑覆盖:

核心修改点

  1. 初始化时新增缩放系数变量,存储当前缩放比例,新增缩放上下限避免画面过小/过大
  2. 仅首次绘制时调用create_image,后续刷新直接更新已有图像对象的内容,避免重复创建造成内存泄漏
  3. 缩放操作直接修改缩放系数,帧刷新时自动按照当前系数对原始图像做缩放
  4. 缩放方法兼容事件触发和按钮触发两种调用方式

修改后完整代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.09 16:51:02