使用 python 和 tkinter 库,我想创建一个程序,打开一个图像,然后在其上面绘制一个完全黑色的图层,然后使用鼠标单击+移动,删除该黑色图层的一部分(将其视为一个桌面游戏中的战争迷雾)或恢复部分黑色层。
当我想删除部分黑色图层时,我基本上绘制一个透明的形状,而当我恢复黑色图层时,我只是绘制一个全黑的形状。 这是我当前的实现:
import tkinter as tk
from tkinter import filedialog
from PIL import Image, ImageTk, ImageDraw
class ImageEraserApp:
def __init__(self, root):
self.root = root
self.root.title("Image Eraser App")
# Open image file dialog
self.image_path = filedialog.askopenfilename(
filetypes=[("Image files", "*.jpg *.jpeg *.png *.bmp *.gif")]
)
if not self.image_path:
self.root.quit()
# Load original image and create initial resized image
self.original_image = Image.open(self.image_path)
self.resized_image = self.original_image.copy()
self.tk_image = ImageTk.PhotoImage(self.resized_image)
# Create canvas to display image
self.canvas = tk.Canvas(self.root)
self.canvas.pack(fill=tk.BOTH, expand=True)
self.canvas_image = self.canvas.create_image(0, 0, anchor=tk.NW, image=self.tk_image)
# Initialize black layer for erasing and painting
self.black_layer = Image.new('RGBA', self.original_image.size, (0, 0, 0, 255))
self.layer_draw = ImageDraw.Draw(self.black_layer)
self.tk_black_layer = ImageTk.PhotoImage(self.black_layer)
# Eraser size
self.eraser_size = 20
# Bind mouse events
self.canvas.bind("<B1-Motion>", self.erase)
self.canvas.bind("<B3-Motion>", self.paint_black)
self.canvas.bind("<MouseWheel>", self.adjust_eraser_size)
# Display initial black layer
self.black_layer_id = self.canvas.create_image(0, 0, anchor=tk.NW, image=self.tk_black_layer)
def adjust_eraser_size(self, event):
# Adjust the eraser size based on mouse wheel scroll
if event.delta > 0:
self.eraser_size = min(100, self.eraser_size + 20)
elif event.delta < 0:
self.eraser_size = max(20, self.eraser_size - 20)
def erase(self, event):
# Draw on the black layer to "erase" it
self.layer_draw.ellipse([
(event.x - self.eraser_size, event.y - self.eraser_size),
(event.x + self.eraser_size, event.y + self.eraser_size)
], fill=(0, 0, 0, 0))
# Update the black layer on the canvas
self.tk_black_layer = ImageTk.PhotoImage(self.black_layer)
self.canvas.itemconfig(self.black_layer_id, image=self.tk_black_layer)
def paint_black(self, event):
# Draw on the black layer to "paint" it
self.layer_draw.ellipse([
(event.x - self.eraser_size, event.y - self.eraser_size),
(event.x + self.eraser_size, event.y + self.eraser_size)
], fill=(0, 0, 0, 255))
# Update the black layer on the canvas
self.tk_black_layer = ImageTk.PhotoImage(self.black_layer)
self.canvas.itemconfig(self.black_layer_id, image=self.tk_black_layer)
if __name__ == "__main__":
root = tk.Tk()
app = ImageEraserApp(root)
root.mainloop()
现在,随着我绘制的形状数量的增加,应用程序变得越来越无响应(如果我尝试在此基础上进行一些调整大小操作/放大/缩小,情况会变得更糟(最容易注意到的时候加载非常大的图像)
我的问题是如何优化它?我尝试实现它的方法是否是解决此类问题的正确方法?
def erase(self, event):
if self.prev_x and self.prev_y:
self.mask_draw.line([(self.prev_x, self.prev_y), (event.x, event.y)], fill=(0, 0, 0, 0), width=self.eraser_size * 2)
self.update_mask(event.x, event.y, self.mask_draw.ellipse, (0, 0, 0, 0))
self.prev_x = event.x
self.prev_y = event.y
def paint_black(self, event):
if self.prev_x and self.prev_y:
self.mask_draw.line([(self.prev_x, self.prev_y), (event.x, event.y)], fill=(0, 0, 0, 255), width=self.eraser_size * 2)
self.update_mask(event.x, event.y, self.mask_draw.ellipse, (0, 0, 0, 255))
self.prev_x = event.x
self.prev_y = event.y
def reset_prev_pos(self, event):
self.prev_x = None
self.prev_y = None
要跟踪过去的鼠标位置,请使用
prev_x
和 prev_y
变量绘制一条连接前一个坐标和当前坐标的线。
平滑擦除和绘画
erase和paint_black方法使用self.mask_draw.line从上一个位置到当前位置绘制一条线。这确保了两个位置之间的每个点都会受到影响,从而形成平滑的轨迹。 要在释放鼠标按钮后重置之前的位置,请使用
reset_prev_pos
方法,该方法连接到 <ButtonRelease-1>
和 <ButtonRelease-3>
事件。
这些修改应该会带来更一致的擦除和绘画体验,具有连续的轨迹而不是不连续的部分。