[python] 基于blind-watermark库添加图片盲水印

article/2025/11/7 13:41:27

blind-watermark是一个能够给图片添加/解析基于频域的数字盲水印的Python库。图像水印image watermark是指在图片里添加文本或图形,以标记图片的来源。但是图像水印会破坏原图。因此另外一种水印形式,即图像盲水印blind image watermark在实践中更多地用于标记图像来源。图像盲水印是一种肉眼不可见的水印,以不可见的形式添加到原始图像中,不会对原始图像的质量产生很大影响。图像盲水印的具体原理见给你的图片加上盲水印。

blind-watermark安装命令如下:

pip install blind-watermark

1 使用说明

1.1 嵌入二进制数据

下面的代码会读取图片并加入二进制数据盲水印。

import blind_watermark
# 关闭输出消息
blind_watermark.bw_notes.close()
from blind_watermark import att
from blind_watermark import WaterMark
import cv2
from blind_watermark import WaterMarkCore
import numpy as np# 水印的长宽wm_shape
bwm = WaterMark(password_img=1, password_wm=1)# 读取原图
imgpath = 'input.jpg'
bwm.read_img(imgpath)wm = [True, False, True, False, True, False, True, False, True, False]
# 嵌入二进制bit数据
bwm.read_wm(wm, mode='bit')# 打上盲水印
outputpath = 'output.png'
# 保存输出图片
bwm.embed(outputpath)# 解水印需要用到长度
len_wm = len(wm)  
#  抗攻击需要知道原图的shape
ori_img_shape = cv2.imread(imgpath).shape[:2]  

上面的代码会往图片中添加二进制数据的盲水印,对比原图和加入盲水印的图片,可以发现虽然看不到水印,但是实际上图像质量有一定下降。

from PIL import Image  
# 展示原图
image = Image.open(imgpath)
image.show()
# 展示添加盲水印后的图
image = Image.open(outputpath)
image.show()

在这里插入图片描述

在这里插入图片描述

以下代码会从加入盲水印的图像中提取水印结果。

# 注意设定水印的长宽wm_shape
bwm1 = WaterMark(password_img=1, password_wm=1)
# 提取水印
wm_extract = bwm1.extract(outputpath, wm_shape=len_wm, mode='bit')
print("不攻击的提取结果:", wm_extract)assert np.all(wm == wm_extract), '提取水印和原水印不一致'
不攻击的提取结果: [ True False  True False  True False  True False  True False]

以下代码展示了对添加水印的图片进行截图后依然能够提取水印,这种方式只是将非截取区域用白色遮挡,不是真正的截图。

# 截取区域设置
# 截取方式x1, y1, x2, y2 = shape[0] * loc[0][0], shape[1] * loc[0][1], shape[0] * loc[1][0], shape[1] * loc[1][1]
# (x1,y1),(x2,y2)
loc = ((0.3, 0.1), (0.7, 0.9))outputpath_ = '截屏攻击.png'
# 保存截屏后的图片
att.cut_att(input_filename=outputpath, output_file_name=outputpath_, loc=loc)bwm1 = WaterMark(password_wm=1, password_img=1)
wm_extract = bwm1.extract(outputpath_, wm_shape=len_wm, mode='bit')
print("截屏攻击{loc}后的提取结果:".format(loc=loc), wm_extract)
assert np.all(wm == wm_extract), '提取水印和原水印不一致'# 展示添加攻击后的盲水印图
image = Image.open(outputpath_)
image.show()
截屏攻击((0.3, 0.1), (0.7, 0.9))后的提取结果: [ True False  True False  True False  True False  True False]

在这里插入图片描述

以下代码展示了对添加水印的图片进行横向剪裁后依然能够提取水印。

r = 0.5
outputpath = 'output.png'
outputpath_ = '横向裁剪攻击.png'
outputpath_r = '横向裁剪攻击_填补.png'
att.cut_att_width(input_filename=outputpath, output_file_name=outputpath_, ratio=r)
# 需要填补图像,用空白填补图像
att.anti_cut_att(input_filename=outputpath_, output_file_name=outputpath_r,origin_shape=ori_img_shape)# extract:
bwm1 = WaterMark(password_wm=1, password_img=1)
wm_extract = bwm1.extract(outputpath_r, wm_shape=len_wm, mode='bit')
print(f"横向裁剪攻击r={r}后的提取结果:", wm_extract)
横向裁剪攻击r=0.5后的提取结果: [ True False  True False  True False  True False  True False]
# 展示添加横向裁剪攻击后的盲水印图
image = Image.open(outputpath_)
print(image.size)
image.show()
(177, 354)

在这里插入图片描述

# 展示添加横向裁剪攻击_填补后的盲水印图,缺失区域用白色填充,以保持和原图尺寸一致
image = Image.open(outputpath_r)
print(image.size)
image.show()
(354, 354)

在这里插入图片描述

以下代码展示了对添加水印的图片进行遮挡后依然能够提取水印。

outputpath_ = '遮挡攻击.png'
n = 60
att.shelter_att(input_filename=outputpath, output_file_name=outputpath_, ratio=0.1, n=n)# 提取
bwm1 = WaterMark(password_wm=1, password_img=1)
wm_extract = bwm1.extract(outputpath_, wm_shape=len_wm, mode='bit')
print(f"遮挡攻击{n}后的提取结果:", wm_extract)
assert np.all(wm == wm_extract), '提取水印和原水印不一致'# 展示添加攻击后的盲水印图
image = Image.open(outputpath_)
image.show()
遮挡攻击60后的提取结果: [ True False  True False  True False  True False  True False]

在这里插入图片描述

以下代码展示了对添加水印的图片进行旋转后依然能够提取水印,但是需要将旋转后的图片再旋转回来。

outputpath_ = '旋转攻击.png'
outputpath_r = '旋转攻击还原.png'
att.rot_att(input_filename=outputpath, output_file_name=outputpath_, angle=45)
att.rot_att(input_filename=outputpath_, output_file_name=outputpath_r, angle=-45)# 提取水印
bwm1 = WaterMark(password_wm=1, password_img=1)
wm_extract = bwm1.extract(outputpath_r, wm_shape=len_wm, mode='bit')
print("旋转攻击后的提取结果:", wm_extract)
assert np.all(wm == wm_extract), '提取水印和原水印不一致'# 展示添加攻击后的盲水印图
image = Image.open(outputpath_)
image.show()
旋转攻击后的提取结果: [ True False  True False  True False  True False  True False]

在这里插入图片描述

总之,blind_watermark提供了很稳定的盲水印添加和恢复方式,还有其他不同的攻击效果,比如亮度椒盐缩放。具体可以查看代码blind_watermark_bit。但是要注意的是,对于特定图像,添加某些图像处理效果blind_watermark是没法准确提取水印的。

1.2 嵌入图片数据

下面的代码会读取图片并加入水印图片,水印图片不能大于1.936kb,恢复后的水印图片会丢失色彩信息。

import cv2from blind_watermark import WaterMarkbwm = WaterMark(password_wm=1, password_img=1)
# 读取原图
imgpath = 'input.jpg'
bwm.read_img(filename = imgpath)
# 设置水印图片,水印图片不能大于1.936kb
markimgpath = 'watermark.bmp'
bwm.read_wm(markimgpath, mode='img')outputpath = 'output.png'
# 打上盲水印
bwm.embed(outputpath)
wm_shape = cv2.imread(markimgpath, flags=cv2.IMREAD_GRAYSCALE).shapebwm1 = WaterMark(password_wm=1, password_img=1)
# 注意需要设定水印的长宽wm_shape
wm_extract = bwm1.extract(outputpath, wm_shape=wm_shape, out_wm_name='wm_extracted.png', mode='img')
# 展示盲水印图
image = Image.open(outputpath)
image.show()

在这里插入图片描述

# 展示添加的水印图
image = Image.open(markimgpath)
image.show()

在这里插入图片描述

# 展示提取的水印图
image = Image.open('wm_extracted.png')
image.show()

在这里插入图片描述

1.3 嵌入文字数据

下面的代码会读取图片并加入文字数据盲水印,这种方式也是最常见添加水印方法。

bwm = WaterMark(password_img=1, password_wm=1)
imgpath = 'input.jpg'
bwm.read_img(imgpath)
wm = 'hello 世界!'
bwm.read_wm(wm, mode='str')
outputpath = 'output.png'
bwm.embed(outputpath)len_wm = len(bwm.wm_bit)  # 解水印需要用到长度
print('Put down the length of wm_bit {len_wm}'.format(len_wm=len_wm))ori_img_shape = cv2.imread(outputpath).shape[:2]  # 解水印
bwm1 = WaterMark(password_img=1, password_wm=1)
wm_extract = bwm1.extract(outputpath, wm_shape=len_wm, mode='str')
print("不攻击的提取结果:", wm_extract)assert wm == wm_extract, '提取水印和原水印不一致'
Put down the length of wm_bit 119
不攻击的提取结果: hello 世界!

当然对存入水印后的图片进行图像变换也是可以恢复水印结果,具体使用可以参考blind_watermark_str。

以下代码展示了对添加水印的图片进行椒盐效果添加后依然能够提取水印。

# 往水印图片添加椒盐效果
# ratio是椒盐概率,太高恢复不了
ratio = 0.02
outputpath_ = '椒盐攻击.png'
att.salt_pepper_att(input_filename=outputpath, output_file_name=outputpath_, ratio=ratio)# 提取
wm_extract = bwm1.extract(outputpath_, wm_shape=len_wm, mode='str')
print(f"椒盐攻击ratio={ratio}后的提取结果:", wm_extract)
assert np.all(wm == wm_extract), '提取水印和原水印不一致'# 展示添加椒盐水印后的盲水印图
image = Image.open(outputpath_)
image.show()
椒盐攻击ratio=0.02后的提取结果: hello 世界!

在这里插入图片描述

以下代码展示了对添加水印的图片进行纵向剪裁后依然能够提取水印。

# 纵向剪裁图片
r = 0.4
outputpath = 'output.png'
outputpath_ = '纵向裁剪攻击.png'
outputpath_r = '纵向裁剪攻击_填补.png'
att.cut_att_height(input_filename=outputpath, output_file_name=outputpath_, ratio=r)
# 需要填补图像,用空白填补图像
att.anti_cut_att(input_filename=outputpath_, output_file_name=outputpath_r,origin_shape=ori_img_shape)# extract:
bwm1 = WaterMark(password_wm=1, password_img=1)
wm_extract = bwm1.extract(outputpath_r, wm_shape=len_wm, mode='str')
print(f"纵向裁剪攻击r={r}后的提取结果:", wm_extract)
纵向裁剪攻击r=0.4后的提取结果: hello 世界!
# 展示添加纵向裁剪攻击后的盲水印图
image = Image.open(outputpath_)
print(image.size)
image.show()
(354, 141)

在这里插入图片描述

# 展示添加纵向裁剪攻击_填补后的盲水印图,缺失区域用白色填充,以保持和原图尺寸一致
image = Image.open(outputpath_r)
print(image.size)
image.show()
(354, 354)

在这里插入图片描述

2 参考

  • blind-watermark
  • 给你的图片加上盲水印
  • blind_watermark_bit
  • blind_watermark_str

http://chatgpt.dhexx.cn/article/X5ytlQBF.shtml

相关文章

图片盲水印软件

bulid_watermark_gui Blind&Invisible Watermark (图片盲水印,提取水印无须原图!) 增加图形界面 项目地址:github开源 软件下载地址:无需环境软件下载 how to use pip install -r requirements.txt 运行main.py…

隐写术(盲水印):从入门到出门

0. 前言 我在做 Blind Watermark 这个库的时候,翻阅了大量材料,学到了关于隐写术、盲水印的很多知识,现在梳理了一遍,发出来。 本文结构: 简介:隐写术的应用场景、分类、特点隐写术:介绍几种…

图片隐写,盲水印,加密logo

1.定义 隐写术算是一种加密技术,权威的 wiki 说法是“ 隐写术是一门关于信息隐藏的技巧与科学,所谓信息隐藏指的是不让除预期的接收者之外的任何人知晓信息的传递事件或者信息的内容。” example: 钱图(钱上面的水印&#xff0…

CTF盲水印详解

原创稿件征集 邮箱:eduantvsion.com QQ:3200599554 黑客极客技术、信息安全热点安全研究分析等安全相关的技术文章 稿件通过并发布还能收获 200-800元不等的稿酬 前言 在CTF杂项题型中,盲水印的出现频率是相当高的,但大多数人处于…

盲水印(Blind-WaterMark)

盲水印是一种肉眼不可见的水印方式,对图片资源使用图片盲水印或者文字水印,借此避免数字媒体未经授权的复制和拷贝,可通过对原图进行解码操作,得到水印图来证明版权归属。 这类盲水印是怎样实现的呢?原理并不复杂&…

Windows系统通过CMD连接MySql

1.按压WindowsR快捷键后输入"cmd" 2.点击"确定"按钮后打开命令行 3.通过输入"cd"MySql安装的bin目录,按压"Enter"后进入MySql安装的bin目录 4.输入"mysql -h localhost -u root -p",此处-h后为对应ho…

cmd中无法连接MySQL

本人电脑是win10系统,安装的是MySQL5.7版本的,最近在登录MySQL的时候老师出现下面的错误,有的时候重启电脑耶也会出现这样的情况 1.先检查你的环境变量是否配置好了; (如果没有配置好,请参考一下的步骤&am…

如何使用cmd安装MySQL

步骤一: 在官网MySQL: 点击download显示下载页面: 点击 MySQL community download(这个适个人合开发者,免费的,之前的MySQL Enterprise Edition是企业版付费需要 ,MySQL cluster CGE 是免费版但…

navicat或者cmd远程连接mysql数据库

问题产生: 一般情况下,MySql数据库是不允许进行远程连接操作的,强行使用Navicat连接会报出下面的错误。 服务器连接错误主机XXX不允许连接到此的MySQL的服务器 方法步骤: 1、远程登录授权 在服务器端进行操作,下…

cmd窗口无法连接MySQL的解决方法

1.将下载Mysql中的bin目录的路径配置到Path环境变量中; 2.winR,输入services.msc,到服务窗口找到mysql服务,其中mysql的服务名可能是mysqlxx而不是mysql,比如我的是mysql80,找到之后点击,然后在…

cmd启动MySQL

命令行启动MySQL step1:打开命令行:win R step2:输入如下命令,root与1234分别为MySQL的用户名与密码 mysql -uroot -p1234出现如下信息即为启动成功 如果连接失败 显示如下错误信息: ERROR 2003 (HY000): Cant …

cmd的mysql命令_MySQL cmd命令

1.MySQL 登录: mysql -h localhost -u root -p test Enter password: ***** -h 指定数据库主机名,默认时登录主机。-u指定用户名(即安装数据库时的设置的用户名)。-p指明登录需要密码,如果没有密码,可省略-p参数。 -p 后面的test并不是密码,而是要登录的数据库名。如果-p后…

cmd命令创建连接MySQL数据库

欢迎大家关注我的公众号【老周聊架构】,Java后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。 一、连接MYSQL 格式: mysql -h 主机地址 -u 用户名 -p 用户密码1、 连接到本机上的MYSQL。 首先打开DOS窗口&#…

解决在cmd情况下无法连接MySQL情况(无脑操作教程)

在输入外部命令:mysql -u root -p的时候,出现下面的情况: (网图,侵删) 问题分析:可能是MySQL路径问题没有解决; 解决方法:1.打开电脑高级设置; 2.点击环境变…

cmd控制台连接MySQL数据库_如何利用CMD连接本机mysql数据库

从事数据工作的IT人员,很多人都会与Mysql数据库打交道。因为mysql数据库是一个开源的数据库,利用它来学习数据库的人也有很多。笔者今天就给大家分享一下,安装好mysql之后,怎么利用CMD连接本机mysql数据库。 1.首先第一步是需要我…

cmd指令连接mysql数据库,以及创建数据库与表

cmd指令连接mysql数据库,以及创建数据库与表 1, 打开命令提示符cmd(直接搜索cmd回车) 2,进入mysql,在cmd命令行输入mysql -uroot -p,然后输入你的mysql密码,成功后进入数据库 3,查看mysql中的…

如何连接Mysql

利用services.msg命令 ctrlr,输入命令:services.msc 在“服务”中找到并开启MySQL即可 利用cmd命令 ①按住快捷键winr,打开“运行”命令。 ②输入cmd,运行进入命令提示符界面。 ③在cmd中,输入mysql -u root -p…

CMD连接MySQL,本地phpAdmin登陆

Ⅰ.cmd连接数据库 方法一 1.WINR输入CMD,进入到数据库安装的盘路径(cd\,会进入c盘根目录,再d:就进入d盘了,再cd 进入你数据库的安装路径) 2.输入mysql -P 端口号 -h mysql的主机名\ip -u root&#xff0…

Windows下cmd窗口连接mysql

mysql安装完毕后,命令窗口连接需配置path环境变量,值为mysql安装的目录/bin。如图,Windows下搜索cmd,回车 在DOS命令窗口输入 mysql -hlocalhost -uroot -p回车 进入mysql数据库,其中-h表示服务器名,local…

从cmd 连接mysql_通过cmd命令连接mysql

通过cmd的命令窗口连接mysql,只需要在命令行中输入 mysql -uroot -p123456 .它会出现这样的提示:mysql不是内部或外部命令。解决办法是在环境变量的path路径下加入 C:Program FilesMySQLMySQL Server 5.6bin。 如下图: 将mysql配置到环境变量…