Python使用OpenCV图片去水印多种方案实现

news2025/2/21 17:36:07

1. 前言

本文为作者学习记录,使用Python结合OpenCV,总结了几种常见的水印去除方式,简单图片去水印效果良好,但是复杂图片有点一言难尽,本文部分代码仅供参考,并不能针对所有水印通用,需要根据具体水印颜色、位置等情况进行分析调整代码。

2. 颜色介绍

本文总共使用了两种格式的颜色,一种是BGR,另一种是HSV

关于如何获取BGR的颜色,可以直接使用截图或者吸取颜色的工具吸取即可。如下图:

有一点需要注意:Python里面用的是BGR,截图工具给的是RGB,这个使用的使用需要调整下顺序

image-20250211151343585

HSV是由色调(H),饱和度(S),亮度(V)组成,如何获取HSV的值,这里提供一段Python的代码获取。其中'image/3_water.jpg'替换成你的图片路径。详细如下:

import cv2
import numpy as np
from matplotlib import pyplot as plt
 
image=cv2.imread('image/3_water.jpg')
HSV=cv2.cvtColor(image,cv2.COLOR_BGR2HSV)
def getpos(event,x,y,flags,param):
    if event==cv2.EVENT_LBUTTONDOWN: #定义一个鼠标左键按下去的事件
        print(HSV[y,x])
 
cv2.imshow("imageHSV",HSV)
cv2.imshow('image',image)
cv2.setMouseCallback("imageHSV",getpos)
cv2.waitKey(0)

效果图如下,左边的为原图,右边的为转为HSV的图片,点击你需要获取颜色的位置,会在控制台打印对应的颜色值。

image-20250211151907580

3. 添加水印

先准备几张测试图片,给这些图片打上水印。这里以下面的图片经行演示。

image-20250211152340137

添加水印代码如下:

import cv2
import numpy as np
 
def create_watermark(image_path):
    # 读取图像
    image = cv2.imread(image_path)
    
    # 设置水印文本
    watermark_text = 'www.xiaoxiaofeng.com'
    
    # 设置字体
    font = cv2.FONT_HERSHEY_SIMPLEX
    # 设置字体大小
    font_scale = 0.5
    # 注意,这里是BGR 蓝色(B)、绿色(G)和红色(R)
    font_color = (62, 62, 187)  
    # 设置字体粗细
    thickness = 1
    
    # 获取图像的高度和宽度
    height, width = image.shape[:2]
    # 设置文本位置(右下角)
    text_position = ( width - 200, height - 20 )  # 例如,在右下角
    
    # 添加水印文本
    cv2.putText(image, watermark_text, text_position, font, font_scale, font_color, thickness, cv2.LINE_AA)
    
    # 显示图像
    cv2.imshow('Watermarked Image', image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    # 保存图像
    cv2.imwrite('image/2_water.jpg', image)

create_watermark('image/2.jpg')

添加完水印的效果如下图:

image-20250211153725231

4. 去除水印

关于去除水印,效果最好的应该还是训练模型,用模型去除效果肯定比较好,但是对于简单的水印,也没必要去训练模型,而且训练模型的门槛比较高,自己的"超配00年代"的电脑就别想了。

这里针对几种常见的水印,简单的描述下去水印的思想,以及实现代码。

4.1 文档类图片去水印

关于图片准备,这里直接用word创建了一个带水印的文档,然后截图,图片如下:

3

实现目标:去除中间背景的中的【笑小枫】的水印。

实现思想:背景为白色,字体颜色为黑色,水印颜色为灰色,因此可以将水印的灰色替换为白色即可

方案一:

通过观测,水印的颜色大多为(214, 214, 214),但是边缘的锯齿处有部分颜色为214-245之间,这里定义3个色彩相加之和位于 640-740之间的,全部替换为白色,当然这样可能会误伤一部分颜色,具体需要针对多种方案比较选用。

import numpy as np
import cv2

def remote_water_mark_1(image):
    # 读取图像
    image = cv2.imread(image)
    # 显示原始图像
    cv2.imshow('Original Image', image)

    # 设置替换颜色为白色
    replace_color = (255, 255, 255)

    # 获取图片大小
    height, width = image.shape[:2]
    for i in range(height):
        for j in range(width):
            # 获取当前像素点的颜色值
            varP = image[i, j]
            # 如果当前像素点的颜色值总和在640到760之间,则替换为白色
            if sum(varP) > 640 and sum(varP) < 740:
                image[i, j] = replace_color
    # 显示处理后的图像
    cv2.imshow('Result Image', image)
    cv2.waitKey(0)
   

remote_water_mark_1('image/3.jpg')

去水印后的效果图如下:

image-20250211160123464

可以看到,图片底部的水印去掉了,仔细看图片中的文字颜色有点细微变化,这就是因为部分像素颜色被误伤了导致的。下面看下方案二,可以解决这个问题。

方案二:

和方案一的思想一致,但是不再使用BGR的颜色处理图片,而是使用HSV对图片进行处理。通过创建颜色范围的掩码,替换掉对应像素的颜色,具体代码如下:

import numpy as np
import cv2

def remote_water_mark_2(image):
    # 读取图像
    image = cv2.imread(image)
    # 显示原始图像
    cv2.imshow('Original Image', image)
    # 将图像从BGR颜色空间转换为HSV颜色空间
    hsv_image  = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

    # 定义要替换的颜色范围(在 HSV 空间中)
    lower_blue = np.array([0, 0, 214])
    upper_blue = np.array([0, 0, 245])

    # 在HSV图像中,根据定义的颜色范围创建掩码
    mask = cv2.inRange(hsv_image, lower_blue, upper_blue)
    
    # 显示掩码图像
    cv2.imshow('mask Image', mask)

    # 将掩码中对应位置的颜色替换为红色(在 HSV 空间中),白底在HSV空间就是红色。
    hsv_image[mask > 0] = [0, 0, 255]

    # 如果需要,将图片转换回 BGR 颜色空间
    result_image = cv2.cvtColor(hsv_image, cv2.COLOR_HSV2BGR)

    # 显示结果图像
    cv2.imshow('Result Image', result_image)
    cv2.waitKey(0)
   
remote_water_mark_2('image/3.jpg')

去水印后的效果图如下,可以通过mask掩码图像(黑色背景,白色水印图)清晰的看到水印的形状。这里可以根据mask掩码调整颜色区间的参数。可以看到右边图片成功去除水印,并且文字颜色一致。

image-20250211160822858

4.2 固定位置水印去除方式

上文提到了,水印背景颜色如果和图片颜色类型,可能会误伤,出现意向不到问题。所有针对可以确认固定位置的水印,我们尽量处理固定位置,减少对图片的损伤。

如下图:水印固定在右下角,颜色和字体颜色基本一致,这样如果全图处理,整张图片就面目全非了。所以我们可以针对右下角固定的位置,特殊处理。

image-20250211162412306

代码如下:

import numpy as np
import cv2

def remote_water_mark_3(image):
    # 读取图像
    image = cv2.imread(image)

    # 显示原始图像
    cv2.imshow('Original Image', image)

    # 获取图像的高度和宽度
    height, width = image.shape[:2]

    # 定义 ROI 的坐标和大小
    x, y, w, h = width-280, height-40, 280, 40
    roi = image[y:y+h, x:x+w]

    # 将 ROI 从 BGR 颜色空间转换为 HSV 颜色空间
    hsv_image  = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)

    # 定义要替换的颜色范围,范围尽量越小越好(在 HSV 空间中)
    lower_blue = np.array([0, 0, 0])
    upper_blue = np.array([0, 0, 240])

    # 创建掩码
    mask = cv2.inRange(hsv_image, lower_blue, upper_blue)

    # 替换颜色
    hsv_image[mask > 0] = [0, 0, 255]  # 将匹配的颜色替换为红色(在 HSV 空间中)

    # 如果需要,将图片转换回 BGR 颜色空间
    roi = cv2.cvtColor(hsv_image, cv2.COLOR_HSV2BGR)

    # 将处理后的 ROI 放回原图
    image[y:y+h, x:x+w] = roi

    # 显示处理后的图像
    cv2.imshow('Result Image', image)
    cv2.waitKey(0)
   
remote_water_mark_2('image/1_water.jpg')

处理后的效果如下图所示

image-20250211163159620

可以看见水印已经去掉了。因为这里背景是白色,所以很好处理,但如果背景不是白色,而是一些其他颜色改怎么处理呢?接下来看下面

4.3 复杂背景色的水印处理

针对复杂背景色的图片,处理时肯定会一定程度的损伤图片了,如果需要相对精准,可以训练模型处理,单纯使用代码,还是有一定程序限制。如果水印位置不固定,数量不固定,大小不固定等等,代码局限性就很大了,纯openCV代码暂还没找到方案,这里还是以简单的示例。

先看下面这张相对简单的图片

image-20250211163454386

方案一

使用inpaint函数修复图片,代码如下:

import numpy as np
import cv2

def remote_water_mark_6(image):
    # 读取图像
    image = cv2.imread(image)
    # 显示原始图像
    cv2.imshow('Original Image', image)
    height, width = image.shape[:2]
    # 创建一个掩码,标记水印区域
    mask = np.zeros(image.shape[:2], np.uint8)
    height, width = image.shape[:2]
    # 假设水印在这个区域,绘制矩形掩码
    cv2.rectangle(mask, (width-220, height-50), (width, height), 255, -1)

    # 使用inpaint函数修复图像
    denoised_image = cv2.inpaint(image, mask, 1, cv2.INPAINT_TELEA)

    # 显示结果图像
    cv2.imshow('Result Image', denoised_image)
    cv2.waitKey(0)
    
remote_water_mark_6('image/2_water.jpg')

处理后的效果图如下,可以看到水印处理掉了,但是图片水印处有略微变形。

image-20250211163946101

针对下面这张图片进行测试:

image-20250211164442813

可以看到水印虽然去掉了,但是水印出糊的很厉害

image-20250211164533551

方案二

此方案为自己写的,暂未经大量图片测试,但测试了部分图片,效果还可,因此放在这里做个比较

实现思想:实现步骤基本于上面一致,针对水印颜色区间像素进行颜色替换,替换的颜色值,这里取周边像素颜色出现次数最多的颜色做替换。然后对区域图片进行噪点处理,尽量中和处理部分于原图像的匹配度,如果水印下方是文字类型,不可以进行噪点处理

具体代码如下:

具体使用时需要根据水印位置和颜色调整对应的参数

import numpy as np
import cv2
from collections import Counter

def find_most_frequent_color(color_list):
    """
    找到颜色列表中出现频率最高的颜色。
    Args:
        color_list (list): 颜色列表,每个颜色可以是一个包含三个整数的列表,分别表示RGB值。
    Returns:
        tuple: 出现频率最高的颜色,以元组形式返回,包含三个整数,分别表示RGB值。
    """
	# 将颜色列表中的每个颜色转换为元组形式
    color_list = [tuple(color) for color in color_list]  
	# 使用 Counter 统计每种颜色出现的次数
    color_counts = Counter(color_list)
	# 获取出现次数最多的颜色,并返回该颜色
    return color_counts.most_common(1)[0][0]

def remote_water_mark_5(image):
    # 读取图像
    image = cv2.imread(image)
    # 显示原始图像
    cv2.imshow('Original Image', image)
    height, width = image.shape[:2]
    # 定义 ROI(水印的位置,需要根据实际情况调整)
    x, y, w, h = width-200, height-35, 200, 35  # ROI 的坐标和大小
    roi = image[y:y+h, x:x+w]
    for yy in range(25, -1, -1):
        for xx in range(195):
            # 获取当前像素的颜色
            pixel_color = roi[yy, xx]
            pixel_color_sum = sum(pixel_color[:3])
            # 检查当前像素的颜色是否与要替换的颜色匹配,需要根据实际情况调整
            if (pixel_color_sum > 300 and pixel_color_sum < 650):
                if sum(roi[yy, xx]) == sum(roi[yy, xx-1]) and sum(roi[yy, xx]) == sum(roi[yy+1, xx]):
                    continue
                is_fix = False
                color_set = [roi[yy, xx]]
                for i in range(5, -1, -1):
                    if is_fix:
                        break
                    temp = roi[yy + i, xx + i]
                    temp1 = roi[yy - i, xx - i]
                    color_set.append(temp)
                    color_set.append(temp1)
                    color_set.append( roi[yy + i, xx + 2])
                    color_set.append( roi[yy + 2, xx + i])
                    color_set.append( roi[yy - i, xx - 2])
                    color_set.append( roi[yy - 2, xx - i])
                    color_set.append( roi[yy + i, xx - i])
                    color_set.append( roi[yy - i, xx + i])
                    if sum(temp) == sum(temp1):
                        roi[yy, xx] = temp
                        is_fix = True
                    elif i == 1:
                        most_frequent_color = find_most_frequent_color(color_set)
                        roi[yy, xx] = most_frequent_color
    # 滤波窗口大小,如果背景处是文字,不可以进行噪点处理,不然会糊掉
    kernel_size = 3
    # 对 ROI 进行中值滤波
    roi = cv2.medianBlur(roi, kernel_size)

    # 将处理后的 ROI 放回原图
    image[y:y+h, x:x+w] = roi
    # 显示处理后的图像
    cv2.imshow('Result Image', image)
    cv2.waitKey(0)
    
remote_water_mark_5('image/4_water.jpg')

测试效果如下图所示:

image-20250211165621190

image-20250211165511406

下面这个图,去除了噪点处理的代码。

image-20250211170237258

4.4 训练模型(未完成)

作者尝试过进行训练模型处理,但在训练数据时,电脑CPU咔咔100%,因此放弃。这里就不附代码供大家参考,因为没有训练出模型,不知道效果如何,也不知道对不对。

5. 本文总结

本文写了几种去水印的方案,具体如何选择,小伙伴们可以根据实际情况去选择,本文也是我使用python去水印一路研究过来的总结。

因为刚刚接触python不多,如有错误之处,大家可以帮忙指出来,感谢!

希望有帮助的朋友帮忙点个赞赞,作者后续会陆续学习分享Java和Python相关知识,有兴趣的朋友可以添加好友一起研究学习。

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

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

相关文章

基于角色访问控制的UML 表示02

一个用户可以成为很多角色的成员&#xff0c;一个角色可以有许多用户。类似地&#xff0c;一个角色可以有多个权限&#xff0c;同一个权限可以被指派给多个角色。每个会话把一个用户和可能的许多角色联系起来。一个用户在激发他或她所属角色的某些子集时&#xff0c;建立了一个…

CEF132 编译指南 Linux 篇 - 获取 CEF 源代码:源码同步详解(五)

1. 引言 在完成所有必要工具的安装和配置之后&#xff0c;我们来到了整个 CEF 编译流程中至关重要的环节&#xff1a;获取 CEF 源代码。CEF 源码的获取过程需要我们特别关注同步策略和版本管理&#xff0c;以确保获取的代码版本正确且完整。本篇将详细指导你在 Linux 系统上获…

Golang关于结构体组合赋值的问题

现在有一个结构体&#xff0c;其中一个属性组合了另外一个结构体&#xff0c;如下所示&#xff1a; type User struct {Id int64Name stringAge int64UserInfo }type UserInfo struct {Phone stringAddress string }如果要给 User 结构体的 Phone 和 Address 赋值的话&am…

django上传文件

1、settings.py配置 # 静态文件配置 STATIC_URL /static/ STATICFILES_DIRS [BASE_DIR /static, ]上传文件 # 定义一个视图函数&#xff0c;该函数接收一个 request 参数 from django.shortcuts import render # 必备引入 import json from django.views.decorators.http i…

【银河麒麟高级服务器操作系统】服务器卡死后恢复系统日志丢失-分析及处理全过程

了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;https://product.kylinos.cn 开发者专区&#xff1a;https://developer.kylinos.cn 文档中心&#xff1a;https://document.kylinos.cn 服务器环境以及配置 【机型】 处理器&#xff…

通过 VBA 在 Excel 中自动提取拼音首字母

在excel里面把表格里的中文提取拼音大写缩写怎么弄 在Excel中&#xff0c;如果你想提取表格中的中文字符并转换为拼音大写缩写&#xff08;即每个汉字的拼音首字母的大写形式&#xff09;&#xff0c;可以通过以下步骤来实现。这项工作可以分为两个主要部分&#xff1a; 提取拼…

动态规划dp_4

一.背包 如果求组合数就是外层for循环遍历物品&#xff0c;内层for遍历背包。 如果求排列数就是外层for遍历背包&#xff0c;内层for循环遍历物品。 二.题 1. 思路&#xff1a;dp五部曲&#xff0c;思路在注释 /* dp[i]表示&#xff1a;到达第 i 个台阶有dp[i]种方法 状态转…

对贵司需求的PLC触摸的远程调试的解决方案

远程监控技术解决方案 一、需求痛点分析 全球设备运维响应滞后&#xff08;平均故障处理周期>72小时&#xff09;客户定制化需求频繁&#xff08;每月PLC程序修改需求超50次&#xff09;人力成本高企&#xff08;单次跨国差旅成本约$5000&#xff09;多品牌PLC兼容需求&am…

Python用PyMC3马尔可夫链蒙特卡罗MCMC对疾病症状数据贝叶斯推断

全文链接&#xff1a;https://tecdat.cn/?p39937 本文聚焦于马尔可夫链蒙特卡罗&#xff08;MCMC&#xff09;方法在贝叶斯推断中的Python实现。通过介绍MCMC的基础原理、在贝叶斯推断中的应用步骤&#xff0c;展示了其在解决复杂分布采样问题上的强大能力。同时&#xff0c;借…

网络工程师 (39)常见广域网技术

一、HDLC 前言 HDLC&#xff08;High-level Data Link Control&#xff0c;高级数据链路控制&#xff09;是一种面向比特的链路层协议。 &#xff08;一&#xff09;定义与历史背景 HDLC是由国际电信联盟&#xff08;ITU&#xff09;标准化的&#xff0c;它基于IBM公司早期的同…

每日Attention学习23——KAN-Block

模块出处 [SPL 25] [link] [code] KAN See In the Dark 模块名称 Kolmogorov-Arnold Network Block (KAN-Block) 模块作用 用于vision的KAN结构 模块结构 模块代码 import torch import torch.nn as nn import torch.nn.functional as F import mathclass Swish(nn.Module)…

基于Python的Optimal Interpolation (OI) 方法实现

前言 Optimal Interpolation (OI) 方法概述与实现 Optimal Interpolation (OI) 是一种广泛应用于气象学、海洋学等领域的空间数据插值方法。该方法通过结合观测数据与模型预测数据&#xff0c;最小化误差方差&#xff0c;从而实现对空间数据的最优插值。以下是OI方法的一般步骤…

学习数据结构(10)栈和队列下+二叉树(堆)上

1.关于栈和队列的算法题 &#xff08;1&#xff09;用队列实现栈 解法一&#xff1a;&#xff08;参考代码&#xff09; 题目要求实现六个函数&#xff0c;分别是栈初始化&#xff0c;入栈&#xff0c;移除并返回栈顶元素&#xff0c;返回栈顶元素&#xff0c;判空&#xff0…

.NET版Word处理控件Aspose.Words教程:使用 C# 删除 Word 中的空白页

Word 文档中的空白页会使其看起来不专业并扰乱流程。用户会遇到需要删除 Word 中的空白页的情况&#xff0c;但手动删除它们需要时间和精力。在这篇博文中&#xff0c;我们将探讨如何使用 C# 删除 Word 中的空白页。 本文涵盖以下主题&#xff1a; C# 库用于删除 Word 中的空…

《代码随想录》刷题笔记——回溯篇【java实现】

文章目录 组合组合总和 III电话号码的字母组合组合总和组合总和II思路代码实现 分割回文串※思路字符串分割回文串判断效率优化※ 复原 IP 地址优化版本 子集子集 II使用usedArr辅助去重不使用usedArr辅助去重 递增子序列※全排列全排列 II重新安排行程题意代码 N 皇后解数独直…

【JavaEE进阶】验证码案例

目 &#x1f332;实现说明 &#x1f384;Hutool介绍 &#x1f333;准备工作 &#x1f334;约定前后端交互接口 &#x1f6a9;接口定义 &#x1f6a9;实现服务器后端代码 &#x1f6a9;前端代码 &#x1f6a9;整体测试 &#x1f332;实现说明 随着安全性的要求越来越⾼…

Qt Creator 5.0.2 (Community)用久了突然变得很卡

目录 1.现象 2.解决方案 1.现象 很久没有用Qt Creator开发项目了&#xff0c;刚刚结束的项目又是用VS2019开发的&#xff1b;这两天刚好有时间去学习一下Qt&#xff0c;刚好要用Qt Creator&#xff0c;结果一打开就没反应&#xff0c;主界面显示出来要好几分钟&#xff0c;最…

阅读论文笔记《Efficient Estimation of Word Representations in Vector Space》

这篇文章写于2013年&#xff0c;对理解 word2vec 的发展历程挺有帮助。 本文仅适用于 Word2Vect 的复盘 引言 这篇论文致力于探索从海量数据中学习高质量单词向量的技术。当时已发现词向量能保留语义特征&#xff0c;例如 “国王 - 男人 女人≈女王”。论文打算借助该特性&am…

初学PADS使用技巧笔记(也许会继续更新)

操作意图&#xff1a;网上找某个芯片封装又不想自己画&#xff0c;再加上没经验&#xff0c;怎么办&#xff1f; 就以AC-DC芯片PN8036为例&#xff0c;打开嘉立创的的DFM&#xff0c;打开立创商城&#xff0c;输入PN8036&#xff0c;点击数据手册&#xff0c;然后点击直接打开…

从无序到有序:上北智信通过深度数据分析改善会议室资源配置

当前企业普遍面临会议室资源管理难题&#xff0c;预约机制不完善和临时会议多导致资源调度不合理&#xff0c;既有空置又有过度拥挤现象。 针对上述问题&#xff0c;上北智信采用了专业数据分析手段&#xff0c;巧妙融合楼层平面图、环形图、折线图和柱形图等多种可视化工具&a…