Tkinter中叠加图像导出时Logo位置偏移问题求助
问题描述
- 开发目标:实现一款具备以下功能的程序
- 上传背景图像
- 叠加Logo(第二张图像)
- 拖拽及调整Logo大小
- 将结果保存为PNG格式
- 遇到的问题:导出时Logo位置偏移,放在右下角会向左上方偏移,越靠近中间误差越小。尝试通过将
.paste()坐标除以画布精确尺寸解决,但无效。 - 导出函数逻辑:先利用bg_img的宽高创建空白Image,将bg_img粘贴到(0,0)(此部分功能正常);获取Logo在画布上的中心坐标,转换为相对于bg_img的坐标,减去Logo尺寸的一半得到左上角坐标,将Logo转为RGBA格式后粘贴到img_export的计算坐标处。
问题代码
from tkinter import * from tkinter import filedialog from PIL import ImageTk, ImageOps, Image # Save PNG File _______________________ def export(): global bg_img, logo_img, logo_id width = bg_img.width height = bg_img.height img_export = Image.new('RGBA', (width, height)) img_export.paste(bg_img, (0,0)) logo_x, logo_y = canvas.logo_coords logo_x = int(width / 1001 * (logo_x - logo_img.width // 2)) logo_y = int(height / 1001 * (logo_y - logo_img.height // 2)) print(f"coordinates = {logo_x, logo_y}") l_img_alpha = logo_img.convert("RGBA") img_export.paste(logo_img, (logo_x, logo_y)) file = filedialog.asksaveasfilename(defaultextension='.png') img_export.save(file, "PNG") # Logo Upload & Placement ______________________ def start_drag(event): widget = event.widget widget.startX = event.x widget.startY = event.y widget.logo_coords = (widget.startX, widget.startY) def drag(event): widget = event.widget deltaX = event.x - widget.startX deltaY = event.y - widget.startY widget.move(widget.logo_id, deltaX, deltaY) widget.logo_coords = (widget.startX, widget.startY) print(widget.logo_coords) widget.startX = event.x widget.startY = event.y def render_logo(): global logo_img, logo_id logo_rendered = ImageTk.PhotoImage(logo_img) if hasattr(canvas, 'logo_id'): x, y = canvas.coords(logo_id) canvas.delete(logo_id) logo_id = canvas.create_image(x, y, image=logo_rendered) else: logo_id = canvas.create_image(500, 500, image=logo_rendered) canvas.logo_rendered = logo_rendered canvas.logo_id = logo_id canvas.tag_bind(logo_id, "<ButtonPress-1>", start_drag) canvas.tag_bind(logo_id, "<B1-Motion>", drag) def enlarge(): global logo_img incr_width = int(logo_img.width * 1.33) incr_height = int(incr_width*logo_img.height/logo_img.width) logo_img = logo_img.resize((incr_width, incr_height), Image.LANCZOS) render_logo() def reduce(): global logo_img smaller = (int(logo_img.height * 0.88), int(logo_img.width * 0.88)) logo_img.thumbnail(smaller) render_logo() def upload_logo(event=None): global logo_img, bg_img, user_prompt file_formats = [('Jpg', '*.jpg'), ('PNG', '*.png'), ('JPEG', '*.jpeg')] logo_path = filedialog.askopenfilename(filetypes=file_formats) logo_img = Image.open(logo_path) logo_img.thumbnail((100,100)) render_logo() canvas.logo_coords = (500,500) if logo_img: user_prompt.destroy() enlarge_button = Button(text="enlarge logo", command=enlarge, highlightbackground="#6D3939") enlarge_button.place(x=555, y=0) reduce_button = Button(text="reduce logo", command=reduce, highlightbackground="#6D3939") reduce_button.place(x=333, y=0) export_button = Button(text="Export", command=export, highlightbackground="#6D3939") export_button.place(x=444, y=955) # Image Upload & Render Background ______________________ def upload_picture(event=None): global bg_img, user_prompt file_formats = [('Jpg', '*.jpg'), ('PNG', '*.png'), ('JPEG', '*.jpeg')] bg_path = filedialog.askopenfilename(filetypes=file_formats) bg_img = Image.open(bg_path) if bg_img.height > 909 or bg_img.width > 909: bg_img.thumbnail((909, 909)) else: bg_img = ImageOps.contain(bg_img, (909, 909)) bg_img_rendered = ImageTk.PhotoImage(bg_img) canvas.bg_img_rendered = bg_img_rendered canvas.create_image(500,500, image=bg_img_rendered) print(bg_img.width, bg_img.height, bg_img) upload_button.destroy() user_prompt = Button(text="add your logo", command=upload_logo, highlightbackground="#6D3939") user_prompt.place(x=444, y=0) # User Interface ______________________ window = Tk() window.title("Watermark") window.resizable(True, True) canvas = Canvas(window, width=1001, height=1001, bg="#6D3939") canvas.grid(column=1, row=1) upload_button = Button(text='upload your image', command=upload_picture, highlightbackground="#6D3939") upload_button.place(x=444, y=444) window.mainloop()
解决方案
偏移问题的核心是画布上背景图的显示逻辑与实际导出图像的坐标映射错误,同时存在拖拽时坐标记录错误、尺寸参数顺序错误两个次要问题,修复如下:
1. 修正Logo坐标记录逻辑
拖拽时需直接获取Logo的中心坐标,而非鼠标点击位置:
def start_drag(event): widget = event.widget widget.startX = event.x widget.startY = event.y # 直接获取Logo的中心坐标 widget.logo_coords = widget.coords(widget.logo_id) def drag(event): widget = event.widget deltaX = event.x - widget.startX deltaY = event.y - widget.startY widget.move(widget.logo_id, deltaX, deltaY) # 更新为最新的Logo中心坐标 widget.logo_coords = widget.coords(widget.logo_id) widget.startX = event.x widget.startY = event.y
2. 修正导出时的坐标映射
背景图在1001x1001画布上是居中显示的,需先计算背景图的偏移量,再将Logo的画布坐标转换为背景图相对坐标:
def export(): global bg_img, logo_img, logo_id width = bg_img.width height = bg_img.height img_export = Image.new('RGBA', (width, height)) img_export.paste(bg_img, (0,0)) # 获取Logo在画布上的中心坐标 canvas_logo_x, canvas_logo_y = canvas.logo_coords # 计算背景图在画布上的左上角偏移量 bg_offset_x = (1001 - bg_img.width) // 2 bg_offset_y = (1001 - bg_img.height) // 2 # 转换为背景图上的中心坐标 logo_center_x = canvas_logo_x - bg_offset_x logo_center_y = canvas_logo_y - bg_offset_y # 计算Logo左上角坐标 logo_x = int(logo_center_x - logo_img.width / 2) logo_y = int(logo_center_y - logo_img.height / 2) # 可选:限制坐标不超出背景图范围 logo_x = max(0, min(logo_x, width - logo_img.width)) logo_y = max(0, min(logo_y, height - logo_img.height)) print(f"coordinates = {logo_x, logo_y}") # 带透明通道粘贴,确保Logo透明区域生效 l_img_alpha = logo_img.convert("RGBA") img_export.paste(l_img_alpha, (logo_x, logo_y), mask=l_img_alpha) file = filedialog.asksaveasfilename(defaultextension='.png') img_export.save(file, "PNG")
3. 修正缩小Logo的尺寸参数顺序
thumbnail方法的参数是(width, height),原代码写反了:
def reduce(): global logo_img # 修正为(width, height)顺序 smaller = (int(logo_img.width * 0.88), int(logo_img.height * 0.88)) logo_img.thumbnail(smaller) render_logo()
内容的提问来源于stack exchange,提问作者Dominic J
相关产品推荐
相关产品推荐

