【异界方块】Blind Watermark 图片盲水印,提取水印无须原图!
本帖最后由 相见忧 于 2026-6-10 17:29 编辑利用 blind-watermark 为图片批量添加与提取盲水印(Python 教程)
本教程基于 GitHub 开源项目 blind-watermark,支持单张及批量图片的盲水印嵌入与提取,水印内容为字符串或图片,本篇教程只讲字符串。盲水印的特点是人眼不可见、鲁棒性强,即使图片经过裁剪、压缩等处理,仍有可能成功提取。
项目地址:blind-watermark
1.环境准备
编译器:任意支持 Python 的环境(如 PyCharm、VS Code)
Python 版本:3.6 及以上
依赖安装:在终端中执行以下命令
pip install blind-watermark项目结构:新建一个空文件夹,在其中创建以下目录
mkdir pic # 存放原始图片
mkdir output # 存放加水印后的图片最终的项目结构示例:
your_project/
│
├── pic/ # 原始图片
├── output/ # 水印图片
├── embed.py # 单张嵌入脚本
├── extract.py # 单张提取脚本
├── batch_embed.py # 批量嵌入脚本
├── batch_extract.py # 批量提取脚本
└── ...
2. 单张图片:添加与提取水印
2.1 创建脚本文件
新建 embed.py 和 extract.py 两个文件。
2.2 嵌入水印代码(embed.py)
from blind_watermark import WaterMark
import os
# 确保输出文件夹存在
os.makedirs('output', exist_ok=True)
# 创建水印对象
bwm = WaterMark(password_img=1, password_wm=1)
# 读取原始图片(你需要先放一张图片到 pic 文件夹)
bwm.read_img('pic/0001.jpg')
# 水印内容
wm = '这只是一个测试!'
bwm.read_wm(wm, mode='str')
# 嵌入水印
bwm.embed('output/embedded.png')
# 获取水印长度(提取时需要)
len_wm = len(bwm.wm_bit)
print(f'水印嵌入成功!')
print(f'水印长度: {len_wm}')
print(f'输出文件: output/embedded.png')
# 保存水印长度供后续使用
with open('watermark_length.txt', 'w') as f:
f.write(str(len_wm))2.3 提取水印代码(extract.py)
from blind_watermark import WaterMark
# 读取之前保存的水印长度
try:
with open('watermark_length.txt', 'r') as f:
len_wm = int(f.read())
except FileNotFoundError:
print("找不到 watermark_length.txt,请先运行 embed.py")
exit(1)
# 创建水印对象(使用相同密码)
bwm = WaterMark(password_img=1, password_wm=1)
# 提取水印
wm_extract = bwm.extract('output/embedded.png', wm_shape=len_wm, mode='str')
print(f'提取出的水印: {wm_extract}')运行效果示例:
3. 多张图片:添加与提取水印
3.1 创建脚本文件
新建 batch_embed.py 和 batch_extract.py
3.2 批量嵌入代码(batch_embed.py)
from blind_watermark import WaterMark
import glob
import os
# 配置
PASSWORD_IMG = 1
PASSWORD_WM = 1
WATERMARK_TEXT = '这只是一个测试!'
INPUT_FOLDER = 'pic/'
OUTPUT_FOLDER = 'output/'
# 确保输出文件夹存在
os.makedirs(OUTPUT_FOLDER, exist_ok=True)
# 支持的图片格式
image_files = []
for ext in ['*.jpg', '*.jpeg', '*.png', '*.bmp']:
image_files.extend(glob.glob(f'{INPUT_FOLDER}{ext}'))
print(f"找到 {len(image_files)} 张图片")
# 记录所有水印信息
all_info = {}
for i, img_path in enumerate(image_files, 1):
basename = os.path.basename(img_path)
name, ext = os.path.splitext(basename)
output_path = f'{OUTPUT_FOLDER}{name}{ext}'
# 嵌入水印
bwm = WaterMark(password_img=PASSWORD_IMG, password_wm=PASSWORD_WM)
bwm.read_img(img_path)
bwm.read_wm(WATERMARK_TEXT, mode='str')
bwm.embed(output_path)
len_wm = len(bwm.wm_bit)
all_info = len_wm
print(f"[{i}/{len(image_files)}]{basename} -> {output_path} (长度: {len_wm})")
# 保存所有水印信息
with open(f'{OUTPUT_FOLDER}watermark_info.txt', 'w', encoding='utf-8') as f:
for output_path, length in all_info.items():
f.write(f"{output_path}: {length}\n")
print(f"\n 批量处理完成!共处理 {len(image_files)} 张图片")3.3 批量提取代码(batch_extract.py)
from blind_watermark import WaterMark
from blind_watermark import bw_notes
import warnings
import glob
import os
# 关闭欢迎信息和警告
bw_notes.close()
warnings.filterwarnings('ignore')
# 配置
PASSWORD_IMG = 1
PASSWORD_WM = 1
INPUT_FOLDER = 'output/' # 带水印的图片所在文件夹
OUTPUT_INFO_FILE = 'output/watermark_info.txt' # 之前保存的水印信息文件
# 读取水印信息(文件名 -> 水印长度)
watermark_lengths = {}
with open(OUTPUT_INFO_FILE, 'r', encoding='utf-8') as f:
for line in f:
if ': ' in line:
file_path, length = line.strip().split(': ')
# 只取文件名(不含路径)
filename = os.path.basename(file_path)
watermark_lengths = int(length)
print(f"读取到 {len(watermark_lengths)} 条水印信息")
print(f"水印信息文件列表: {list(watermark_lengths.keys())}")
# 获取所有带水印的图片
watermarked_images = []
for ext in ['*.jpg', '*.jpeg', '*.png', '*.bmp']:
watermarked_images.extend(glob.glob(f'{INPUT_FOLDER}{ext}'))
print(f"找到 {len(watermarked_images)} 张带水印的图片")
print(f"图片文件列表: {}")
print("=" * 60)
# 批量提取水印
success_count = 0
for img_path in watermarked_images:
basename = os.path.basename(img_path)
# 检查是否有对应的水印长度信息
if basename not in watermark_lengths:
print(f" {basename} - 找不到水印长度信息,跳过")
continue
len_wm = watermark_lengths
# 提取水印
bwm = WaterMark(password_img=PASSWORD_IMG, password_wm=PASSWORD_WM)
try:
wm_extract = bwm.extract(img_path, wm_shape=len_wm, mode='str')
print(f" {basename} -> 提取水印: {wm_extract}")
success_count += 1
except Exception as e:
print(f" {basename} - 提取失败: {e}")
print(f"\n 批量提取完成!成功提取 {success_count}/{len(watermarked_images)} 张图片的水印")运行效果示例:
若图片内容为空白(图中第二张)(如纯色或无效图像数据),水印将无法被正确识别或提取。
4. 注意事项
密码一致:嵌入和提取时使用的 password_img 和 password_wm 必须相同,否则无法正确提取。
水印长度:提取时需要知道水印的二进制长度,脚本中已通过文件保存,请勿删除。
水印内容:支持字符串、二进制等多种模式,本教程以字符串为例。
小浣熊饲养中心 大佬太厉害了,看晕过去了。暂时先码住了 商人弗霖打开异界的百宝袋,袋口微光一闪,一枚【紫色方块】轻巧地蹦了出来。 批量操作的方便度真的是很高了 感觉这个很适合用来抓那些倒卖资源的人呀 好耶,先收藏了,又拿下一个加水印办法 為了防止盜圖或倒賣加水印是必須的,這款工具極大的提高了效率。 咋这么封神,下次尝试加下水印 能批量操作的话就很方便了,这算是加密的一种简单形式吗?() 好高级 正常水印都挺影响观感 有这个不影响正常看遇到盗图也好维权 大佬真的很牛,看得头都晕了,收藏了收藏了 能批量加水印真的死了非常方便了,保护自身权益还是很重要的 感谢大佬的教程 有点复杂呢 收藏下慢慢看 好高的技术力,字好多晕晕有空再看完 支持惹,加了盲水印的话,部分倒卖婆感觉可以被制裁惹 https://img.gamemale.com/album/202408/03/102121hz01mn3632og0c30.gif这次的插件看起来还是比较复杂的,不用提取水印无须原图的成效非常实用了 這工具對那些經常發資源的大佬們應該很有幫助
感謝樓主分享好用防盜賣工具 相当实用的工具呢,复杂程度虽然较高,但耐心探寻便有所获呢 批量的话就会很方便了,但感觉有点点复杂欸,不过应该能抑制那些盗卖资源的人 盲水印感觉很强大惹,给本子打这种水印不仅不影响观看还能一定程度上防倒卖啥的{:6_169:}
页:
[1]
2