0%

FDWM: 一个实现「不可见」水印的 Python 库

还在念书时,就听说过频域水印。它可以通过将水印内容隐藏在宿主图像的高频区域,从而让人肉眼不可见地打上水印。为了详细了解相关技术,结合 LLM,我开发了一个 Python 库:FDWM(Frequency Domain Watermarking)

技术原理:为什么选择频域?

1. 频域水印的基本原理

数字水印技术主要分为空域和频域两大类。FDWM 选择频域技术,其优势在于:

  • 不可见性更好:在高频区域嵌入水印,人眼很难察觉
  • 鲁棒性更强:对压缩、裁剪等攻击有更好的抵抗能力
  • 容量适中:在不可见性和容量之间取得了很好的平衡

2. 核心算法实现

FDWM 的核心实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. 图像预处理
host = _read_image(host_path, gray=True) # 读取宿主图像
watermark_norm = watermark.astype(np.float32) / 255.0 # 归一化水印

# 2. 频域变换
host_dft = np.fft.fft2(host) # 2D FFT
host_dft_shift = np.fft.fftshift(host_dft) # 频谱中心化

# 3. 水印嵌入(高频区域)
host_dft_shift[r_start:r_end, c_start:c_end] += strength * watermark_norm
host_dft_shift[r_start_sym:r_end_sym, c_start_sym:c_end_sym] += (
strength * np.flipud(np.fliplr(watermark_norm))
)

# 4. 逆变换
host_idft_shift = np.fft.ifftshift(host_dft_shift)
img_back = np.fft.ifft2(host_idft_shift)
img_back = np.real(img_back)

3. 关键技术细节

对称嵌入策略

FDWM 采用对称嵌入策略,在频谱的四个角落同时嵌入水印。

  • 提高水印的鲁棒性
  • 减少视觉影响
  • 增强检测可靠性

自适应文本渲染

对于文本水印,FDWM 实现了智能的字体大小调整:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def _text_to_image(text: str, target_size: Tuple[int, int], ...) -> np.ndarray:
# 自适应字体大小计算
while font_size > 10:
# 计算文本边界框
text_bbox = draw.multiline_textbbox((0, 0), wrapped, font=font, spacing=4)
text_w = text_bbox[2] - text_bbox[0]
text_h = text_bbox[3] - text_bbox[1]

if text_w <= cols and text_h <= rows:
# 文本适合,居中绘制
x = (cols - text_w) // 2
y = (rows - text_h) // 2
draw.multiline_text((x, y), wrapped, fill=255, font=font, spacing=4)
return np.array(img)

font_size -= 2 # 减小字体重试

项目架构

1. 模块结构

1
2
3
4
5
fdwm/
├── __init__.py # 包入口,导出主要函数
├── watermark.py # 核心水印算法实现
├── cli.py # 命令行界面
└── __main__.py # 模块执行入口

2. 设计模式

FDWM 采用了清晰的分层架构:

  • 算法层watermark.py 包含所有核心算法
  • 接口层__init__.py 提供简洁的 API
  • 用户层cli.py 提供命令行工具

3. 配置管理

项目使用现代化的 Python 包配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# pyproject.toml
[project]
name = "fdwm"
version = "0.1.0"
description = "Frequency-domain watermarking library and CLI"
dependencies = [
"numpy>=1.20",
"opencv-python>=4.5",
"Pillow>=10.0",
"pytesseract>=0.3.10",
]

[project.scripts]
fdwm = "fdwm.cli:main"

使用体验:简单到让人惊喜

1. 安装简单

1
pip install fdwm

2. API 简洁

1
2
3
4
5
6
7
8
9
10
import cv2
from fdwm import embed, extract, extract_text

# 图像水印
watermarked_img = embed(host_img, watermark_img, strength=0.1)
extracted_watermark = extract(watermarked_img, watermark_img.shape[:2])

# 文本水印
watermarked_img = embed(host_img, "Hello World", strength=0.1, is_text=True)
extracted_text = extract_text(watermarked_img)

3. 命令行友好

1
2
3
4
5
6
7
8
# 嵌入图像水印
fdwm embed host.jpg watermark.png -o watermarked.jpg

# 嵌入文本水印
fdwm embed host.jpg "Hello World" -o watermarked.jpg --text

# 提取水印
fdwm extract watermarked.jpg watermark.png -o extracted.png

性能与质量保证:做得相当到位

1. 测试覆盖

项目包含完整的测试套件,这个测试写得很有意思:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def test_embed_extract():
"""完整工作流测试:嵌入 -> 提取 -> 计算相关系数"""
# 生成测试图像
generate_host_image(str(host_path))
generate_watermark(str(wm_path))

# 嵌入水印
fdwm.embed(host_path, watermark_path, output_path, strength=5000.0)

# 提取水印
extracted = fdwm.extract(watermarked_path, strength=5000.0)

# 计算相关系数
corr = np.corrcoef(wm_resized.flatten(), extracted.flatten())[0, 1]
assert corr > 0.5, "水印提取相关系数过低"

2. 质量指标

  • 相关系数:原始水印与提取水印的相关系数 > 0.5
  • 不可见性:水印嵌入后图像质量无明显下降
  • 鲁棒性:对常见图像处理操作有良好抵抗能力

总结

FDWM 是一个使用简单、功能完善的数字水印库。它成功地将复杂的频域水印技术封装成易用的 Python 包,为图像版权保护和信息隐藏提供了解决方案。

特别值得一提的事,包括撰写这篇博客本身,整个过程中,我几乎没有手写代码。95% 以上的工作由 LLM 自动完成。


项目地址https://github.com/Liam0205/fdwm
PyPI 包名fdwm
许可证:MIT
作者:Liam Huang

俗话说,投资效率是最好的投资。 如果您感觉我的文章质量不错,读后收获很大,预计能为您提高 10% 的工作效率,不妨小额捐助我一下,让我有动力继续写出更多好文章。