当前位置: 首页 > news >正文

python实现简单的图片去水印工具

python实现简单的图片去水印工具

使用说明:

点击"打开图片"选择需要处理的图片

在图片上拖拽鼠标选择水印区域(红色矩形框)

点击"去除水印"执行处理

点击"保存结果"保存处理后的图片

运行效果

先简要说明本程序用到的python库:

(1)from PIL import Image, ImageTk, ImageDraw 和 import PIL,需要Pillow。

Pillow 是一个图像处理库,用于处理图像的打开、操作和保存。它不是 Python 标准库的一部分,是第三方库需要单独安装。

(2)import cv2,需要OpenCV。

OpenCV 是一个计算机视觉库,用于图像和视频处理、目标检测等任务。它不是 Python 标准库的一部分,是第三方库需要单独安装。

(3)import numpy as np,需要NumPy 。

NumPy 是一个科学计算库,用于高效处理多维数组和矩阵运算。它第三方库,需要单独安装。

(4)import tkinter as tk 和 from tkinter import filedialog, messagebox,需要tkinter。

tkinter 是 Python 的标准 GUI 库,用于创建图形用户界面。它是 Python 标准库的一部分,不需要单独安装。

(5)import os,需要os。

os 是 Python 的标准库,用于操作操作系统相关的功能,如文件和目录操作。它也是 Python 标准库的一部分,不需要单独安装。

Python第三方扩展库Pillow 更多情况可见 https://blog.csdn.net/cnds123/article/details/126141838

Python第三方库OpenCV (cv2) 更多情况可见 https://blog.csdn.net/cnds123/article/details/126547307

Python第三方扩展库NumPy 更多情况可见 https://blog.csdn.net/cnds123/article/details/135844660

源码如下:

import tkinter as tk
from tkinter import filedialog, messagebox
from PIL import Image, ImageTk, ImageDraw
import PIL
import cv2
import numpy as np
import os

class WatermarkRemoverApp:
    def __init__(self, root):
        self.root = root
        self.root.title("简单的图片去水印工具")
        
        # 初始化变量
        self.original_image = None
        self.processed_image = None
        self.display_ratio = 1.0
        self.selection_rect = None
        self.mask = None
        
        # 创建界面布局
        self.create_widgets()
        
        # 鼠标事件绑定
        self.canvas.bind("<ButtonPress-1>", self.start_selection)
        self.canvas.bind("<B1-Motion>", self.update_selection)
        self.canvas.bind("<ButtonRelease-1>", self.end_selection)

        self.original_file_path = None  # 新增实例变量

    def create_widgets(self):
        # 工具栏
        toolbar = tk.Frame(self.root)
        toolbar.pack(fill=tk.X)
        
        btn_open = tk.Button(toolbar, text="打开图片", command=self.open_image)
        btn_open.pack(side=tk.LEFT, padx=2, pady=2)
        
        btn_process = tk.Button(toolbar, text="去除水印", command=self.remove_watermark)
        btn_process.pack(side=tk.LEFT, padx=2, pady=2)
        
        btn_save = tk.Button(toolbar, text="保存结果", command=self.save_image)
        btn_save.pack(side=tk.LEFT, padx=2, pady=2)
        
        # 图像显示区域
        self.canvas = tk.Canvas(self.root, bg='gray', cursor="cross")
        self.canvas.pack(fill=tk.BOTH, expand=True)

    def open_image(self):
        file_path = filedialog.askopenfilename(filetypes=[("Image Files", "*.jpg *.jpeg *.png *.bmp")])
        if not file_path:
            return
        
        try:
            self.original_image = Image.open(file_path)
            self.original_file_path = file_path  # 保存原始路径
            self.processed_image = None
            self.show_image(self.original_image)
            self.mask = None
        except Exception as e:
            messagebox.showerror("错误", f"无法打开图片:\n{str(e)}")

    def show_image(self, image):
        # 计算缩放比例
        canvas_width = self.canvas.winfo_width()
        canvas_height = self.canvas.winfo_height()
        img_width, img_height = image.size
        
        self.display_ratio = min(
            canvas_width / img_width,
            canvas_height / img_height,
            1.0  # 最大保持原始尺寸
        )
        
        display_size = (
            int(img_width * self.display_ratio),
            int(img_height * self.display_ratio)
        )

        
        # 缩放并显示图像
        if hasattr(Image, 'Resampling'):
            resample_method = Image.Resampling.LANCZOS
        else:
            resample_method = Image.LANCZOS  # 旧版本回退

        display_image = image.resize(display_size, resample_method)
        self.tk_image = ImageTk.PhotoImage(display_image)
        
        self.canvas.delete("all")
        self.canvas.config(
            width=display_size[0],
            height=display_size[1]
        )
        self.canvas.create_image(0, 0, anchor=tk.NW, image=self.tk_image)

    def start_selection(self, event):
        self.selection_rect = (event.x, event.y, event.x, event.y)

    def update_selection(self, event):
        if self.selection_rect:
            x0, y0, _, _ = self.selection_rect
            x1, y1 = event.x, event.y
            self.selection_rect = (x0, y0, x1, y1)
            self.draw_selection_rect()

    def end_selection(self, event):
        if self.selection_rect:
            self.draw_selection_rect()
            # 转换到原始图像坐标
            x0 = int(self.selection_rect[0] / self.display_ratio)
            y0 = int(self.selection_rect[1] / self.display_ratio)
            x1 = int(self.selection_rect[2] / self.display_ratio)
            y1 = int(self.selection_rect[3] / self.display_ratio)
            
            # 创建掩膜
            self.mask = Image.new("L", self.original_image.size, 0)
            draw = ImageDraw.Draw(self.mask)
            draw.rectangle([x0, y0, x1, y1], fill=255)
            
    def draw_selection_rect(self):
        self.canvas.delete("selection")
        x0, y0, x1, y1 = self.selection_rect
        self.canvas.create_rectangle(
            x0, y0, x1, y1,
            outline="red",
            tags="selection"
        )

    def remove_watermark(self):
        if not self.original_image:
            messagebox.showwarning("警告", "请先打开图片")
            return
        if not self.mask:
            messagebox.showwarning("警告", "请先选择水印区域")
            return
            
        try:
            # 转换图像格式
            img = cv2.cvtColor(np.array(self.original_image), cv2.COLOR_RGB2BGR)
            mask = np.array(self.mask)
            
            # 使用OpenCV的inpaint方法
            radius = 10
            result = cv2.inpaint(img, mask, radius, cv2.INPAINT_TELEA)
            
            # 转换回PIL格式
            self.processed_image = Image.fromarray(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))
            self.show_image(self.processed_image)
        except Exception as e:
            messagebox.showerror("错误", f"处理失败:\n{str(e)}")

    def save_image(self):
        if not self.processed_image:
            messagebox.showwarning("警告", "没有可保存的结果")
            return
        
        if not self.original_file_path:
            messagebox.showwarning("警告", "未找到原始文件信息")
            return

        # 生成默认文件名
        original_name = os.path.basename(self.original_file_path)
        default_name = f"RW_{original_name}"
        
        file_path = filedialog.asksaveasfilename(
            defaultextension=".png",
            filetypes=[("PNG", "*.png"), ("JPEG", "*.jpg"), ("BMP", "*.bmp")],
            initialfile=default_name  # 设置默认文件名
        )
        
        if file_path:
            try:
                self.processed_image.save(file_path)
                messagebox.showinfo("成功", "图片保存成功")
            except Exception as e:
                messagebox.showerror("错误", f"保存失败:\n{str(e)}")

if __name__ == "__main__":
    root = tk.Tk()
    app = WatermarkRemoverApp(root)
    root.geometry("800x600")
    root.mainloop()

相关文章:

  • 表单引擎赋能AI:引领未来表单设计新趋势
  • 单片机外设快速入门篇(五)——GPIO篇
  • Houdini :《哪吒2》神话与科技碰撞的创新之旅
  • 【linux】虚拟机执行sudo yum isntall perl报错 could not retrieve mirrorlist htt:
  • 通向AGI的未来之路!首篇2D/视频/3D/4D统一生成框架全景综述(港科大中山等)
  • Elasticsearch使用记录
  • spring动态代理是在生命周期的哪个阶段实现的
  • 矩阵期望 E 的含义:概率
  • 蓝桥杯高频考点——进制转换
  • 【 利用socket来实现简单远控】
  • c++中字符串string常用的函数
  • AMBA-CHI协议详解(二十二)
  • Next-Slides 更新记录
  • AcWing 5960:输出前k大的数 ← 小根堆
  • centos 磁盘重新分割,将原来/home 下部分空间转移到 / 根目录下
  • Idea集成docker通过ca加密实现镜像打包
  • freetros 信号量使用方法实例解析
  • 【 <二> 丹方改良:Spring 时代的 JavaWeb】之 Spring Boot 的自动配置:约定优于配置的设计美学
  • 激光雷达点云改善汇总
  • FastGPT原理分析-数据集创建第一步
  • 习近平同巴西总统卢拉会谈
  • 首映丨纪录电影《滚烫年华》:献给所有奋斗者
  • 山西忻州市人大常委会副主任郭建平接受审查调查
  • 中国海外发展:今年前4个月销售665.8亿元,花费305亿元拿地
  • 中俄就应对美加征所谓“对等关税”等问题进行深入交流
  • 国家主席习近平同普京总统举行小范围会谈