Digital Watermarking / Invisible Watermarking / Forensic Watermarking / Robust Watermark
1. 基本概念
1.1 Digital Watermarking
Digital Watermarking,即:
数字水印
核心思想:
原始内容
+
水印信息
↓
水印算法
↓
带水印内容
水印信息可以是:
版权 ID
用户 ID
Session ID
设备 ID
订单 ID
访问记录 ID
时间
随机 Token
例如:
Watermark ID = 83927154
服务器数据库保存:
83927154
↓
user_id = 10086
session_id = 8af3...
device_id = PC-001
time = 2026-10-02 15:31
之后如果发现泄露图片:
泄露图片
↓
水印检测
↓
83927154
↓
查询数据库
↓
找到对应访问记录
2. Visible Watermark
Visible Watermark:
可见水印
例如:
CONFIDENTIAL
User: 10086
2026-10-02
直接覆盖在图片或者网页上。
优点:
实现简单
识别简单
威慑作用强
缺点:
影响视觉效果
容易被裁剪
容易被 Photoshop 删除
3. Invisible Watermark
Invisible Watermark:
隐形水印
特点:
人眼:
基本看不出来
程序:
可以检测
例如原始像素:
120 120 120
120 120 120
120 120 120
修改后:
120 121 120
119 120 121
120 119 120
视觉上几乎没有区别。
但是这些变化可以编码:
101101001011...
最终代表:
Watermark ID = 83927154
4. Forensic Watermarking
Forensic Watermarking:
取证水印
重点不是:
证明这张图片属于哪个公司
而是:
追踪:
谁
在什么时候
通过哪个 Session
访问了这个内容
典型系统:
┌─────────────┐
│ 用户登录系统 │
└──────┬──────┘
│
↓
Session = ABC
│
↓
生成唯一 Watermark ID
│
↓
┌──────────────────┐
│ Watermark Server │
└────────┬─────────┘
│
↓
图片 / 视频 / 页面
│
↓
用户设备显示
数据库:
watermark_id
user_id
session_id
device_id
ip
user_agent
timestamp
content_id
例如:
watermark_id = 829173
user_id = 10086
session_id = A91F...
device_id = PC-01
content_id = DOC-888
发生泄露:
泄露照片
↓
Extract Watermark
↓
829173
↓
数据库
↓
PC-01
User 10086
Session A91F
因此:
水印本身不一定包含完整用户信息。
通常只需要包含一个随机 ID。
5. 为什么推荐 Watermark ID
不要直接编码:
user=10086
device=PC01
time=20261002
更合理的是:
Watermark ID
83927154
然后:
83927154
↓
数据库
↓
完整记录
优点:
水印 Payload 更小
更容易做纠错
不直接暴露用户信息
数据库信息可以扩展
Token 可以随机化
6. Robust Watermark
Robust Watermark:
鲁棒数字水印
目标是:
即使图片被修改,依然能够识别水印。
例如:
原图
↓
JPEG 压缩
↓
缩放
↓
裁剪
↓
调亮
↓
加噪声
↓
截图
↓
手机拍屏
↓
仍然能够恢复 Watermark ID
这就是:
Robustness
鲁棒性
7. 水印算法的核心矛盾
水印通常需要同时满足:
Imperceptibility
不可感知性
Robustness
鲁棒性
Capacity
容量
三者存在明显权衡。
例如:
水印强度 ↑
鲁棒性 ↑
但
图片质量 ↓
反过来:
水印强度 ↓
视觉质量 ↑
但
识别成功率 ↓
因此核心问题就是:
在“看不出来”和“破坏以后还能识别”之间寻找平衡。
8. 空间域水印
最简单的方法:
直接修改像素。
例如:
Pixel = 120
编码 bit 0:
120
编码 bit 1:
121
或者经典 LSB:
120
二进制:
01111000
最后一位:
0
编码:
bit = 1
修改为:
01111001
也就是:
121
9. LSB 水印
LSB:
Least Significant Bit
最低有效位。
算法:
Pixel:
10110110
↑
LSB
需要写入:
1
变成:
10110111
像素只变化:
+1
人眼基本无法察觉。
10. LSB 的问题
LSB 非常脆弱。
例如:
JPEG 压缩
缩放
模糊
截图
手机拍照
都可能破坏 LSB。
因此:
LSB ≠ Robust Watermark
它适合:
学习
隐写实验
无损 PNG
不适合真正:
Screen-Camera Forensic Watermark
11. 为什么要使用频域
鲁棒水印通常不会直接修改某一个像素。
而是:
Image
↓
Transform
↓
Frequency Domain
↓
修改频率系数
↓
Inverse Transform
↓
Watermarked Image
常见:
DCT
DFT
DWT
即:
Discrete Cosine Transform
Discrete Fourier Transform
Discrete Wavelet Transform
12. DCT
DCT:
Discrete Cosine Transform
JPEG 本身大量使用 DCT 思想。
通常把图片划分:
8 × 8
块:
┌─────────┐
│ 8 × 8 │
│ pixels │
└─────────┘
执行:
DCT
得到:
Frequency Coefficients
可以粗略理解成:
┌──────────────────────┐
│ Low │ Medium │ High │
└──────────────────────┘
13. 为什么不能随便修改频率
低频:
包含图片主要视觉结构
修改太多:
容易被人眼发现
高频:
细节
纹理
噪声
修改后虽然不明显,但:
JPEG
Blur
Resize
Camera
很容易把这些信息消掉。
所以传统鲁棒水印经常关注:
中频区域
即:
低频:
稳定
但视觉敏感
中频:
相对稳定
视觉影响适中
高频:
视觉影响小
但容易丢失
14. DCT 水印基本结构
例如:
Image
↓
RGB → YCbCr
↓
取 Y
↓
分成 8×8 Block
↓
DCT
↓
选择中频系数
↓
嵌入 bit
↓
IDCT
↓
恢复 RGB
15. 为什么经常使用亮度 Y
RGB:
R
G
B
可以转换:
Y
Cb
Cr
其中:
Y = Luminance
Cb = Blue difference
Cr = Red difference
水印算法可以选择:
Y
或者根据具体设计使用色度通道。
选择哪个通道,需要考虑:
视觉敏感度
JPEG 压缩
显示设备
Camera Sensor
16. 一种简单 DCT 编码方式
假设选择两个 DCT 系数:
C1
C2
编码:
bit = 1
要求:
C1 > C2 + T
编码:
bit = 0
要求:
C2 > C1 + T
其中:
T = watermark strength
检测:
if C1 > C2:
bit = 1
else:
bit = 0
这样不需要知道原始图片。
属于:
Blind Watermark Detection
17. Blind Watermark
Blind Watermark:
提取水印的时候:
不需要原图
例如:
泄露图片
↓
Detector
↓
Watermark ID
而不是:
原始图片
+
泄露图片
↓
比较
↓
Watermark
对于取证系统:
Blind Detection
通常非常重要。
18. Spread Spectrum
另一个重要技术:
Spread Spectrum
扩频水印。
不要把一个 bit:
1
放在一个位置。
而是通过伪随机序列:
PN Sequence
扩散到很多位置。
例如:
bit = 1
↓
+1 -1 +1 +1 -1 -1 +1 ...
嵌入:
Image + α × PN
其中:
α = 水印强度
19. Spread Spectrum 的优势
即使:
一部分图片损坏
一部分被裁剪
JPEG 压缩
噪声
剩余信号仍可能:
Correlation
检测出来。
检测:
corr(image, PN)
如果:
correlation > threshold
认为:
bit = 1
因此扩频思想很适合:
Robust Watermark
20. 重复嵌入
假设:
Watermark ID
10110101
不要只写一次。
可以:
10110101
10110101
10110101
10110101
10110101
分布到不同区域:
┌─────────────────────────┐
│ WM WM WM │
│ │
│ WM WM │
│ │
│ WM WM WM │
│ │
│ WM WM │
└─────────────────────────┘
这样:
Crop
以后仍可能恢复。
Screen-Camera 研究中确实存在将消息重复嵌入多个局部特征区域,并通过频域系数进行调制的方案。
21. Error Correction Code
真正的鲁棒系统通常不会直接:
ID → watermark
而是:
ID
↓
CRC
↓
ECC
↓
Interleave
↓
Watermark
ECC:
Error Correcting Code
例如:
BCH
Reed-Solomon
LDPC
假设:
原始:
101101001011
经过攻击:
101001001011
↑
bit error
ECC 有机会恢复:
101101001011
22. 为什么手机拍屏幕特别困难
普通:
Image
↓
JPEG
只是数字处理。
Screen-Camera:
Digital Image
↓
Display
↓
LCD / OLED
↓
环境光
↓
Camera Lens
↓
CMOS Sensor
↓
ISP
↓
JPEG / HEIC
经历大量变化。
研究通常把 Screen-Camera 的影响概括为多种复合失真,包括几何变化、Gamma/亮度变化、噪声以及低通/模糊等。
23. Screen-Camera 常见失真
Perspective
手机斜着拍:
原屏幕:
┌─────────────┐
│ │
│ │
└─────────────┘
照片:
┌────────────┐
/ /
/____________/
产生:
Perspective Distortion
Moiré
屏幕像素排列:
LCD Pixels
与:
Camera Sensor Pixels
相互干涉:
Moiré Pattern
摩尔纹
Blur
可能产生:
Motion Blur
Defocus Blur
Optical Blur
Color / Gamma
还可能出现:
Brightness
Contrast
Gamma
White Balance
Color Shift
24. Screen-Camera Robust Watermark
所以真正的:
Screen-Camera Robust Watermark
一般需要:
Watermark
+
Synchronization
+
Perspective Correction
+
ECC
+
Repeated Embedding
+
Frequency Domain
相关方案已经使用局部特征进行同步、透视校正,再从 DFT 等频域信息中恢复消息;近年来也有直接在屏幕渲染链路中实时加入消息模板和同步模板的方案。
25. Synchronization
同步非常重要。
假设:
Watermark
原来位于:
x = 100
y = 100
拍照以后:
旋转
缩放
透视
裁剪
原位置已经不存在。
Detector 首先必须解决:
水印在哪里?
因此:
Synchronization Pattern
可以和:
Message Watermark
同时存在。
结构:
┌──────────────────────────┐
│ SYNC SYNC│
│ │
│ MESSAGE │
│ │
│ SYNC SYNC│
└──────────────────────────┘
26. Perspective Correction
检测:
Screen Corners
得到:
P1
P2
P3
P4
计算:
Homography Matrix H
然后:
Camera Image
↓
Perspective Transform
↓
Rectified Screen
屏幕恢复成接近:
┌───────────────────┐
│ │
│ Screen │
│ │
└───────────────────┘
再检测水印。
Screen-Camera 水印论文中常把透视校正作为检测流程的一部分。
27. 完整的取证水印系统
推荐架构:
User Login
│
↓
Authentication
│
↓
Session ID
│
↓
Generate Random Token
│
↓
Watermark ID
│
┌─────────────┴─────────────┐
↓ ↓
Database ECC Encoder
│
↓
Bit Sequence
│
↓
Watermark Encoder
│
↓
Image / Screen
泄露检测:
Phone Photo
↓
Screen Detection
↓
Perspective Correction
↓
Synchronization
↓
Frequency Transform
↓
Watermark Detection
↓
ECC Decode
↓
CRC Verify
↓
Watermark ID
↓
Database
↓
Access Record
28. 推荐 Payload
例如使用:
64-bit random watermark ID
而不是:
username
IP
device
例如:
uint64:
1381927391827391
数据库:
watermark_id BIGINT
user_id
session_id
device_id
content_id
created_at
29. Go 实现方案
Go 非常适合实现:
HTTP Server
Watermark Service
Image Processing
Token Generation
Database Mapping
API
并发图片处理
Go 标准库本身提供:
image
image/color
image/jpeg
image/png
JPEG 编解码可以直接通过 image/jpeg 完成。
补充图像处理能力可以使用:
golang.org/x/image
Go 官方维护的 x/image 提供了额外图像相关包。
30. Go 项目结构
推荐:
watermark/
│
├── cmd/
│ └── server/
│ └── main.go
│
├── internal/
│
│ ├── watermark/
│ │ ├── encoder.go
│ │ ├── decoder.go
│ │ ├── dct.go
│ │ ├── ecc.go
│ │ └── sync.go
│ │
│ ├── imageproc/
│ │ ├── perspective.go
│ │ ├── resize.go
│ │ └── grayscale.go
│ │
│ ├── token/
│ │ └── token.go
│ │
│ └── storage/
│ └── watermark.go
│
└── go.mod
31. Go:生成 Watermark ID
不要使用:
math/rand
生成安全 Token。
使用:
crypto/rand
示例:
package watermark
import (
"crypto/rand"
"encoding/hex"
)
func GenerateID() (string, error) {
buf := make([]byte, 16)
if _, err := rand.Read(buf); err != nil {
return "", err
}
return hex.EncodeToString(buf), nil
}
可能生成:
91f8c0c06be441249829ef71b3f972ea
数据库:
91f8c0c06be441249829ef71b3f972ea
↓
User
Session
Device
Content
Time
32. Go:读取图片
package watermark
import (
"image"
_ "image/jpeg"
_ "image/png"
"os"
)
func LoadImage(path string) (image.Image, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
img, _, err := image.Decode(f)
return img, err
}
生产环境处理不可信上传图片时,应先检查图片尺寸等资源消耗风险;Go 的 x/image 文档也特别建议对任意来源图片先考虑 DecodeConfig。
33. Go:最简单的教学版水印
首先实现一个:
Spatial Watermark
用来理解:
Bit → Pixel
不要把它当成真正的 Screen-Camera Robust Watermark。
核心思想:
R = 原始像素
bit = 0
→ R 调整成偶数
bit = 1
→ R 调整成奇数
代码:
func setLSB(v uint8, bit byte) uint8 {
if bit == 1 {
return v | 1
}
return v & 0xFE
}
提取:
func getLSB(v uint8) byte {
return v & 1
}
34. 字符串转 Bit
func bytesToBits(data []byte) []byte {
bits := make([]byte, 0, len(data)*8)
for _, b := range data {
for i := 7; i >= 0; i-- {
bit := (b >> i) & 1
bits = append(bits, bit)
}
}
return bits
}
例如:
A
ASCII:
65
二进制:
01000001
35. LSB Encoder
伪代码:
bits := bytesToBits(payload)
for each pixel {
bit := bits[index]
pixel.R = setLSB(pixel.R, bit)
index++
}
这样:
Watermark ID
↓
[]byte
↓
[]bit
↓
Pixel LSB
36. LSB Decoder
读取:
bit := pixel.R & 1
组合:
01000001
恢复:
A
最后恢复:
Watermark ID
37. 为什么教学完成后应该放弃 LSB
测试:
PNG
↓
LSB Encode
↓
PNG
通常:
✓
但是:
PNG
↓
JPEG
很可能:
✗
再经过:
Screen
↓
Camera
基本更不能依赖它。
所以第二阶段:
LSB
↓
DCT
38. Go DCT 实现
二维 DCT:
F(u,v) =
C(u)C(v)
Σx Σy
f(x,y)
cos(...)
cos(...)
工程实现可以先写:
func DCT8(block [8][8]float64) [8][8]float64
和:
func IDCT8(block [8][8]float64) [8][8]float64
处理:
Image
↓
8×8 Blocks
↓
DCT8()
↓
Modify Coefficients
↓
IDCT8()
↓
Image
39. DCT Embed
假设选择:
C[2][3]
C[3][2]
定义:
A = C[2][3]
B = C[3][2]
bit:
1
要求:
A > B
bit:
0
要求:
B > A
加入:
strength = 10
保证:
abs(A-B) >= strength
40. Go 伪代码
func EmbedBit(
block *[8][8]float64,
bit byte,
strength float64,
) {
a := block[2][3]
b := block[3][2]
if bit == 1 {
if a <= b+strength {
block[2][3] = b + strength
}
} else {
if b <= a+strength {
block[3][2] = a + strength
}
}
}
检测:
func ExtractBit(block [8][8]float64) byte {
if block[2][3] > block[3][2] {
return 1
}
return 0
}
41. 不要连续写 Payload
不要:
Block 1 → bit 1
Block 2 → bit 2
Block 3 → bit 3
Block 4 → bit 4
否则:
Crop
很容易破坏连续数据。
应该:
Payload
↓
ECC
↓
Interleave
↓
Pseudo Random Block Selection
↓
Repeated Embed
42. PRNG Block Selection
可以:
Secret Key
↓
PRNG
↓
Block Positions
例如:
Key:
82A6...
产生:
Block:
81
728
93
1821
301
...
这样不知道 Key:
不知道哪些 Block 包含水印
43. Watermark Key
系统至少区分:
Watermark ID
和
Watermark Secret Key
例如:
ID:
83927154
可以最终被检测出来。
Key:
K = 256-bit secret
只存在服务器。
Key 用于:
PRNG
Block Selection
Spread Spectrum
Authentication
不要:
把 Secret Key 放进图片
44. 推荐 Payload 格式
例如:
VERSION
+
TOKEN
+
CRC
结构:
┌─────────┬─────────────────────┬──────────┐
│ Version │ Watermark Token │ CRC │
├─────────┼─────────────────────┼──────────┤
│ 8 bit │ 64 bit │ 32 bit │
└─────────┴─────────────────────┴──────────┘
然后:
Payload
↓
ECC
↓
Interleave
↓
Spread
↓
Embed
45. Decoder
完整 Decoder:
Input Image
↓
Normalize
↓
Perspective Correction
↓
Synchronization
↓
Block Detection
↓
DCT / DFT
↓
Correlation / Bit Detection
↓
Deinterleave
↓
ECC Decode
↓
CRC
↓
Token
如果:
CRC OK
才接受:
Watermark ID
46. Web 系统实现
访问:
GET /document/123
服务器:
Authenticate User
↓
Generate Watermark Token
↓
Save DB
↓
Generate Watermarked Image
↓
Return
数据库:
watermark_token
document_id
user_id
session_id
device_id
ip
user_agent
created_at
47. 动态生成图片
例如:
GET /protected/image/123
流程:
request
↓
session
↓
watermark ID
↓
load original
↓
embed watermark
↓
encode JPEG
↓
response
可以缓存:
content_id
+
watermark_id
对应的结果,避免每次重新计算。
48. Screen-Camera 版本
真正想实现:
手机拍电脑屏幕
↓
仍然检测 ID
架构应该升级:
Watermark ID
↓
ECC
↓
Spread Spectrum
↓
Repeated Embedding
↓
DCT / DFT Watermark
↓
Sync Pattern
↓
Display
↓
Screen
↓
Camera
↓
Perspective Detect
↓
Homography Correction
↓
Sync Detection
↓
Watermark Decode
研究方案已经证明,可以通过同步结构、自动透视校正以及频域检测构造专门针对拍屏场景的系统。
49. 为什么 DCT 版仍然不一定能抗拍屏
这是非常重要的一点:
DCT ≠ 自动获得 Screen-Camera Robustness
普通:
DCT Watermark
可能只能抵抗:
JPEG
Resize
Noise
Blur
Screen-Camera 还需要解决:
Perspective
Rotation
Scale
Crop
Moiré
Gamma
Camera Noise
Synchronization
所以:
DCT
+
ECC
+
Spread Spectrum
+
Sync
+
Perspective Correction
才更接近真正的系统。
50. 深度学习水印
近年来另一个方向是:
Encoder Neural Network
↓
Watermarked Image
↓
Noise Layer
↓
Decoder Neural Network
训练的时候人为模拟:
JPEG
Perspective
Blur
Noise
Brightness
Moiré
Screen-Camera
然后:
Encoder
学习:
水印应该藏在哪里才最不容易丢失。
已有 Screen-Shooting 研究使用 DCT、CNN/注意力机制以及模拟攻击噪声层联合训练;还有研究专门模拟透视、模糊、光照和摩尔纹等拍屏失真。
51. Go 在深度学习方案中的位置
不建议:
Go
↓
训练神经网络
更加实际:
Python / PyTorch
负责:
Model Training
导出:
ONNX
然后:
Go Server
↓
ONNX Runtime
↓
Encoder / Decoder
系统:
Python
Dataset
↓
Training
↓
Model
↓
ONNX
--------------------------------
Go
HTTP API
↓
Session
↓
Token
↓
ONNX Model
↓
Watermarked Image
↓
Response
52. 测试鲁棒性
不要只测试:
Encode
↓
Decode
应该建立:
Attack Pipeline
例如:
Original
↓
Watermark
↓
Attack
↓
Decoder
↓
BER
53. JPEG Test
测试:
JPEG Quality
95
90
80
70
60
50
观察:
Bit Error Rate
54. Resize Test
测试:
100%
75%
50%
25%
然后恢复尺寸:
Resize Back
再检测。
55. Crop Test
测试:
5%
10%
20%
30%
裁剪。
56. Rotation Test
测试:
1°
2°
5°
10°
57. Perspective Test
模拟手机:
┌─────────────────┐
↓
┌──────────────┐
/ /
/______________/
随机移动:
four corners
产生 Homography。
58. Camera Test
最终一定需要真实测试:
Monitor A
Monitor B
Laptop
Phone
Tablet
摄像设备:
iPhone
Android
不同 Camera
距离:
30 cm
50 cm
1 m
2 m
角度:
0°
15°
30°
45°
环境:
Bright
Dark
Indoor
Outdoor
59. 评价指标
至少记录:
PSNR
SSIM
BER
Detection Rate
PSNR:
原图
vs
水印图
判断视觉损失。
BER:
Bit Error Rate
例如:
原始:
101101001101
检测:
101101011101
↑
统计错误 bit。
60. 推荐学习实现路线
不要一开始直接实现 Screen-Camera。
建议:
阶段 1
PNG
+
LSB
理解:
Payload
Encode
Decode
↓
阶段 2
8×8 DCT
理解:
Frequency Domain
Coefficient
Strength
↓
阶段 3
DCT
+
Repeated Embedding
解决:
Crop
↓
阶段 4
DCT
+
Spread Spectrum
解决:
Noise
Compression
↓
阶段 5
ECC
+
CRC
解决:
Bit Error
↓
阶段 6
Synchronization Pattern
解决:
Rotation
Scale
↓
阶段 7
Homography
解决:
Perspective
↓
阶段 8
真实:
Monitor
↓
Phone Camera
↓
Decoder
61. 最终推荐架构
如果目标是:
Web 页面中的图片针对每个访问设备加入唯一水印,之后即使手机拍摄电脑屏幕,也尽可能追踪原始 Session。
建议:
Web Request
│
↓
User Session
│
↓
Random Watermark ID
│
┌───────────┴───────────┐
↓ ↓
Database ECC
↓
CRC / ECC
↓
Spread Spectrum
↓
DCT / DFT Embed
↓
Repeated Watermark
↓
Sync / Positioning
↓
Browser
↓
Screen
↓
Phone Camera
↓
Perspective Correction
↓
Watermark Detector
↓
ECC Decode
↓
Watermark ID
↓
Database
↓
User / Session / Device
62. 一句话理解整个技术
普通 Web Tracking:
谁请求了这张图片?
Forensic Watermarking:
这张泄露图片当初是显示给谁的?
Robust Watermark:
图片被压缩、缩放、裁剪之后,
还能不能回答上面的问题?
Screen-Camera Robust Watermark:
即使不用截图,
而是拿手机拍摄电脑屏幕,
还能不能回答上面的问题?
核心思想:
用户/设备
↓
Session
↓
唯一 Watermark ID
↓
ECC + Spread Spectrum
↓
DCT / DFT
↓
重复嵌入 + 同步信息
↓
图片/屏幕
↓
各种破坏
↓
校正 + 水印检测
↓
Watermark ID
↓
后台数据库
↓
原访问记录
这才是:
Digital Watermarking + Invisible Watermarking + Forensic Watermarking + Robust Watermarking
组合起来形成的完整取证追踪系统。
浙公网安备 33010602011771号