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 cv2

from blind_watermark import WaterMark

bwm = 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).shape bwm1 = 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 参考

[python] 基于blind-watermark库添加图片盲水印的更多相关文章

  1. gd库复制图片做水印

    将复制源图片的某个位置复制到目标图片中,不能调整大小 imagecopy(目标图片画布,复制源画布,目标画布左上角x,y,源画布左上角x,y,复制图片的宽,高); 允许调整大小 imagecopyre ...

  2. 【CTF】图片隐写术 · 盲水印

    前言 盲水印同样是CTF Misc中极小的一个知识点,刚刚做到一题涉及到这个考点的题目. 感觉还挺有意思的,就顺便去了解了下盲水印技术. 数字水印 数字水印(Digital Watermark)一种应 ...

  3. BugKu 2B+基于python的opencv的安装-------CTF 盲水印的套路

    BugKu杂项-2B 下载图片后,binwalk下跑一跑,发现有个zip,分离. 值得一提的是,这个zip是伪加密的. 但是你在分离的时候,伪加密的图片也给你分离出来了.这两个图片2B和B2肉眼看起来 ...

  4. javaCV开发详解之4:转流器实现(也可作为本地收流器、推流器,新增添加图片及文字水印,视频图像帧保存),实现rtsp/rtmp/本地文件转发到rtmp流媒体服务器(基于javaCV-FFMPEG)

    javaCV系列文章: javacv开发详解之1:调用本机摄像头视频 javaCV开发详解之2:推流器实现,推本地摄像头视频到流媒体服务器以及摄像头录制视频功能实现(基于javaCV-FFMPEG.j ...

  5. python中用Pillow库进行图片处理

    一.Python中 PIL 图像处理库简介 PIL可以做很多和图像处理相关的事情: 图像归档(Image Archives).PIL非常适合于图像归档以及图像的批处理任务.你可以使用PIL创建缩略图, ...

  6. Python测试 ——开发工具库

    Web UI测试自动化 splinter - web UI测试工具,基于selnium封装. selenium - web UI自动化测试. mechanize- Python中有状态的程序化Web浏 ...

  7. Python常用的标准库以及第三方库有哪些?

    20个必不可少的Python库也是基本的第三方库 读者您好.今天我将介绍20个属于我常用工具的Python库,我相信你看完之后也会觉得离不开它们.他们是: Requests.Kenneth Reitz ...

  8. Python常用的标准库以及第三方库

    Python常用的标准库以及第三方库有哪些?   20个必不可少的Python库也是基本的第三方库 读者您好.今天我将介绍20个属于我常用工具的Python库,我相信你看完之后也会觉得离不开它们.他们 ...

  9. Python 常用的标准库以及第三方库有哪些?

    作者:史豹链接:https://www.zhihu.com/question/20501628/answer/223340838来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...

  10. python测试开发工具库汇总(转载)

    Web UI测试自动化 splinter - web UI测试工具,基于selnium封装. selenium - web UI自动化测试. mechanize- Python中有状态的程序化Web浏 ...

随机推荐

  1. 分布式存储系统之Ceph集群CephFS基础使用

    前文我们了解了ceph之上的RBD接口使用相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/16753098.html:今天我们来聊一聊ceph之上的另一 ...

  2. 基于tauri+vue3.x多开窗口|Tauri创建多窗体实践

    最近一种在捣鼓 Tauri 集成 Vue3 技术开发桌面端应用实践,tauri 实现创建多窗口,窗口之间通讯功能. 开始正文之前,先来了解下 tauri 结合 vue3.js 快速创建项目. taur ...

  3. 2022年最新编辑Linux基础知识总结

    文章目录 1.Linux的目录结构 2.远程操作Linux和上传文件到Linux 3.文本编辑 4.快捷键 5.登录.注销.关机.重启 6.用户管理 6.1 .新用户注册 6.2.使用新用户登录 6. ...

  4. C++ Undefined Behavior 详细列表

    Undefined Behavior,即未定义的行为,指程序不可预测的执行效果,一般由错误的代码实现引起.出于效率.兼容性等多方面原因,语言标准不便于定义错误程序的明确行为,而是将其统称为" ...

  5. SpringCloud整合分布式事务Seata 1.4.1 支持微服务全局异常拦截

    项目依赖 SpringBoot 2.5.5 SpringCloud 2020.0.4 Alibaba Spring Cloud 2021.1 Mybatis Plus 3.4.0 Seata 1.4. ...

  6. GCC 指令详解及动态库、静态库的使用

    GCC 指令详解及动态库.静态库的使用 一.GCC 1.1 GCC 介绍 GCC 是 Linux 下的编译工具集,是「GNU Compiler Collection」的缩写,包含 gcc.g++ 等编 ...

  7. IDEA中Java项目创建lib目录并生成依赖

    首先介绍说明一下idea在创建普通的Java项目,是没有lib文件夹的,下面我来带大家来创建一下1.右键点击项目,创建一个普通的文件夹 2.取名为lib 3.把项目所需的jar包复制到lib文件夹下 ...

  8. 使用VsCode调试UE5的PuerTs

    使用VsCode调试UE5的PuerTs 1.下载测试的Demo项目 配置PuerTs的步骤这里不赘述. 2.准备工作 2.1 打开项目 正常来说,直接打开项目可以看到如下画面 如果直接点击运行,可以 ...

  9. Codeforces Global Round 23 D.Paths on the Tree(记忆化搜索)

    https://codeforces.ml/contest/1746/problem/D 题目大意:一棵n节点有根树,根节点为1,分别有两个数组 s[i] 顶点 i 的魅力值 c[i] 覆盖顶点 i ...

  10. 国产图形化的msf——Viper初体验

    目录 免责声明: Viper简介 安装 使用 免责声明: 本文章仅供学习和研究使用,严禁使用该文章内容对互联网其他应用进行非法操作,若将其用于非法目的,所造成的后果由您自行承担,产生的一切风险与本文作 ...