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

news2025/3/18 6:52:25

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()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2317068.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用dify+deepseek部署本地知识库

使用difydeepseek部署本地知识库 一、概述二、安装windows docker desktop1、确认系统的Hyper-v功能正常启用2、docker官网下载安装windows客户端3、安装完成后的界面如下所示 三、下载安装ollama四、部署本地deepseek五、本地下载部署dify5.1 下载dify的安装包5.2 将dify解压到…

【算法day13】最长公共前缀

最长公共前缀 https://leetcode.cn/problems/longest-common-prefix/submissions/612055945/ 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 “”。 class Solution { public:string longestCommonPrefix(vector<string&g…

Java高频面试之集合-13

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本baby今天来报道了&#xff01;哈哈哈哈哈嗝&#x1f436; 面试官&#xff1a;为什么 hash 函数能降哈希碰撞&#xff1f; 哈希函数通过以下核心机制有效降低碰撞概率&#xff0c;确保不同输入尽可能映…

RGV调度算法(三)--遗传算法

1、基于时间窗 https://wenku.baidu.com/view/470e9fd8b4360b4c2e3f5727a5e9856a57122693.html?_wkts_1741880736197&bdQuery%E7%8E%AF%E7%A9%BF%E8%B0%83%E5%BA%A6%E7%AE%97%E6%B3%95 2.2019年MathorCup高校数学建模挑战赛B题 2019-mathorcupB题-环形穿梭机调度模型&a…

YOLOv8轻量化改进——Coordinate Attention注意力机制

现在针对YOLOv8的架构改进越来越多&#xff0c;今天尝试引入了Coordinate Attention注意力机制以改进对小目标物体的检测效率。 yolov8的下载和安装参考我这篇博客&#xff1a; 基于SeaShips数据集的yolov8训练教程_seaships处理成yolov8-CSDN博客 首先我们可以去官网找到CA注…

基于SpringBoot+Vue的驾校预约管理系统+LW示例参考

1.项目介绍 系统角色&#xff1a;管理员、普通用户、教练功能模块&#xff1a;用户管理、管理员管理、教练管理、教练预约管理、车辆管理、车辆预约管理、论坛管理、基础数据管理等技术选型&#xff1a;SpringBoot&#xff0c;Vue等测试环境&#xff1a;idea2024&#xff0c;j…

ONNX:统一深度学习工作流的关键枢纽

引言 在深度学习领域&#xff0c;模型创建与部署的割裂曾是核心挑战。不同框架训练的模型难以在多样环境部署&#xff0c;而 ONNX&#xff08;Open Neural Network Exchange&#xff09;作为开放式神经网络交换格式&#xff0c;搭建起从模型创建到部署的统一桥梁&#xff0c;完…

蓝桥杯————23年省赛 ——————平方差

3.平方差 - 蓝桥云课 一开始看题我还没有意识到问题的严重性 我丢&#xff0c;我想 的是用两层循环来做&#xff0c;后来我试了一下最坏情况&#xff0c;也就是l1 r 1000000000 结果运行半天没运行出来&#xff0c;我就知道坏了&#xff0c;孩子们&#xff0c;要出事&#…

一、串行通信基础知识

一、串行通信基础知识 1.处理器与外部设备通信有两种方式 并行通信&#xff1a;数据的各个位用多条数据线同时传输。&#xff08;传输速度快&#xff0c;但占用引脚资源多。&#xff09; 串行通信&#xff1a;将数据分成一位一位的形式在一条数据线上逐个传输。&#xff08;线路…

自带多个接口,完全免费使用!

做自媒体的小伙伴们&#xff0c;是不是经常为语音转文字的事儿头疼&#xff1f; 今天给大家推荐一款超实用的语音转文字软件——AsrTools&#xff0c;它绝对是你的得力助手&#xff01; AsrTools 免费的语音转文字软件 这款软件特别贴心&#xff0c;完全免费&#xff0c;而且操…

Qt QML解决SVG图片显示模糊的问题

前言 在QML中直接使用SVG图片&#xff0c;使用Image控件加载资源&#xff0c;显示出来图片是模糊的&#xff0c;很影响使用体验。本文介绍重新绘制SVG图片&#xff0c;然后注册到QML中使用。 效果图&#xff1a; 左边是直接使用Image加载资源显示的效果 右边是重绘后的效果 …

【Linux我做主】基础命令完全指南上篇

Linux基础命令完全指南【上篇】 Linux基础命令完全指南github地址前言命令行操作的引入Linux文件系统树形结构的根文件系统绝对路径和相对路径适用场景Linux目录下的隐藏文件 基本指令目录和文件相关1. ls2. cd和pwdcdpwd 3. touch4. mkdir5. cp6. mv移动目录时覆盖写入的两种特…

Designing Dashboards with SAP Analytics Cloud

Designing Dashboards with SAP Analytics Cloud

项目实战系列:基于瑞萨RA6M5构建多节点OTA升级-系统设计<一>

项目背景 原嵌入式控制系统采用分布式模块化架构&#xff0c;由12个功能板卡&#xff08;通信控制、信号采集、驱动执行等&#xff09;组成。系统维护阶段存在以下痛点&#xff1a; 低效的本地烧录机制&#xff1a;各板卡固件升级需通过JTAG接口逐一手动连接JLINK仿真器&#x…

《AI大模型趣味实战》 No3:快速搭建一个漂亮的AI家庭网站-相册/时间线/日历/多用户/个性化配色/博客/聊天室/AI管家(下)

《AI大模型趣味实战》 No3&#xff1a;快速搭建一个漂亮的AI家庭网站-相册/时间线/日历/多用户/个性化配色/博客/聊天室/AI管家(下) 摘要 本文介绍了家庭网站V1.3版本的更新内容&#xff0c;主要聚焦于AI管家功能的优化与完善。V1.3版本对AI管家模块进行了全面升级&#xff0…

c++基础知识-图论进阶

一、拓扑排序 1、基础知识 1&#xff09;什么是拓扑排序 对一个有向无环图G进行拓扑排序&#xff0c;是将G中所有顶点排成一个线性序列&#xff0c;使得图中任意一对顶点u和v&#xff0c;若&#xff0c;则u在线性序列中出现在v之前。 2&#xff09;拓扑排序的操作方法 重复执行…

[Java实战]Spring Boot服务CPU 100%问题排查:从定位到解决

Spring Boot服务CPU 100%问题排查&#xff1a;从定位到解决 1. 引言 当Spring Boot服务出现CPU占用率100%时&#xff0c;系统性能会急剧下降&#xff0c;甚至导致服务不可用。本文将通过真实代码案例&#xff0c;详细讲解如何快速定位问题根源&#xff0c;并提供解决方案。无…

1.6 极限存在准则

1.夹逼定理&#xff08;迫敛定理&#xff09; 1.1 数列型 1.1.1 准则 1.2 函数型 2. 两个重要极限

大华SDK协议在智联视频超融合平台中的接入方法

一. 大华SDK协议详解 &#xff08;一&#xff09;、大华SDK协议概述 大华SDK&#xff08;Software Development Kit&#xff09;协议是大华股份为开发者提供的一套软件开发工具包&#xff0c;旨在帮助开发者快速集成大华设备&#xff08;如摄像头、NVR、DVR等&#xff09;的功…