1149 字
6 分钟
OpenCV案例:数字水印
1. 介绍
数字水印技术是指将一种特定的信息(水印)采用数字内嵌的方式隐藏到图像、声音、视频等数字媒体中。
- 如果嵌入载体图像内的信息是秘密信息,就实现了信息隐藏。
- 如果嵌入载体图像内的信息是版权信息,就能够实现版权认证。
- 如果嵌入载体图像内的信息是身份信息,就可以实现数字签名。
2. 分类
数字水印技术按照检测过程划分,可以分为 非盲水印 和 盲水印 。非盲水印在检测的过程中,需要对原始数据进行运算处理;盲水印在检测的过程中,不需要额外的信息,直接将水印信息从含水印的图像中提取出来即可。
根据嵌入位置的不同,可以将数字水印划分为 最低有效位水印 和 随机位水印 。最低有效位水印把水印信息嵌入到载体图像的最低有效位中,随机位数字水印则是把水印信息嵌入到载体图像中每个像素的不同二进制位上。
3. 盲数字水印
3.1 最低有效位
最低有效位 (Least Significant Bit, LSB)指的是一个二进制数中的第0位。由位平面分解理论可知,由最低有效位构成的位平面,与原图像的相关性最低,因此将图像的最低有效位替换为水印信息,图像信息丢失地最少。
示例
import cv2import numpy as np
# 读取原始图像信息src = cv2.imread("src.jpg")row, col, ch = src.shape
# 读取水印信息watermark = cv2.imread("watermark.png")idx = watermark[:,:,:]>0watermark[idx] = 1
# 水印嵌入extract = np.ones((row, col, ch), dtype=np.uint8) * 254src_ext = cv2.bitwise_and(src, extract)dst_1 = cv2.bitwise_or(src_ext, watermark)cv2.imwrite("embedding-1.jpg", dst_1)
# 水印提取extract = np.ones((row, col, ch), dtype=np.uint8)dst_2 = cv2.bitwise_and(dst_1, extract)idx = dst_2[:,:,:]>0dst_2[idx] = 255cv2.imwrite("extraction-1.jpg", dst_2)| 原始图像 | 水印图像 | 水印嵌入后的图像 | 提取的水印图像 |
|---|---|---|---|
![]() | ![]() | ![]() | ![]() |
3.2 随机位
随机位水印 通常采用随机生成的一个值在0-7之间、与原始载体图像大小相同的位置矩阵,来决定水印信息在每个像素上的二进制值中的具体嵌入位置。
当嵌入水印的位置处于载体图像像素二进制值较高比特位时,其像素值变化较大;当嵌入水印的位置处于载体图像像素二进制值较低比特位时,其像素值变化不明显。因此,可以将嵌入位再缩小至第0位至第3位,这样嵌入的水印信息不易被察觉,提高了嵌入的安全性。
示例
import cv2import numpy as np
# 读取原始图像信息src = cv2.imread("src.jpg")H, W, C = src.shape
# 生成位置矩阵seed = np.random.randint(0, 10000)np.random.seed(seed)num = np.random.randint(0, 8, size=(H, W, C), dtype=np.uint8)pos = 2 ** num
# 读取水印信息watermark = cv2.imread("watermark.png")watermark = cv2.bitwise_and(watermark, pos)
# 水印嵌入src_ext = cv2.bitwise_and(src, cv2.bitwise_not(pos))dst_1 = cv2.bitwise_or(src_ext, watermark)cv2.imwrite("embedding-2.jpg", dst_1)
# 水印提取dst_2 = cv2.bitwise_and(dst_1, pos)idx = dst_2[:,:,:]>0dst_2[idx] = 255cv2.imwrite("extraction-2.jpg", dst_2)| 原始图像 | 水印图像 | 水印嵌入后的图像 | 提取的水印图像 |
|---|---|---|---|
![]() | ![]() | ![]() | ![]() |
4. 非盲数字水印
本文采取的非盲手段为:将加密图像和水印图像作异或处理。
4.1 最低有效位
示例
import cv2import numpy as np
# 读取原始图像信息src = cv2.imread("src.jpg")row, col, ch = src.shape
# 读取水印信息watermark = cv2.imread("watermark.png")
# 非盲处理watermark =idx = watermark[:,:,:]>0watermark[idx] = 1
# 水印嵌入extract = np.ones((row, col, ch), dtype=np.uint8) * 254src_ext = cv2.bitwise_and(src, extract)dst_1 = cv2.bitwise_or(src_ext, watermark)cv2.imwrite("embedding-3.jpg", dst_1)
# 水印提取extract = np.ones((row, col, ch), dtype=np.uint8)dst_2 = cv2.bitwise_and(dst_1, extract)idx = dst_2[:,:,:]>0dst_2[idx] = 255cv2.imwrite("extraction-3.jpg", dst_2)| 原始图像 | 水印图像 | 非盲处理 | 水印嵌入 | 水印提取 |
|---|---|---|---|---|
![]() | ![]() | ![]() | ![]() | ![]() |
4.2 随机位
示例
import cv2import numpy as np
# 读取原始图像信息src = cv2.imread("src.jpg")row, col, ch = src.shape
# 读取水印信息watermark = cv2.imread("watermark.png")
# 设置随机矩阵seed = np.random.randint(0, 10000)np.random.seed(seed)num = np.random.randint(0, 8, size=(row, col, ch), dtype=np.uint8)pos = 2 ** num
# 非盲处理watermark = cv2.bitwise_and(watermark, pos)ext = cv2.bitwise_and(src, pos)watermark = cv2.bitwise_xor(watermark, ext)cv2.imwrite("operation-2.jpg", watermark)
# 水印嵌入src_ext = cv2.bitwise_and(src, cv2.bitwise_not(pos))dst_1 = cv2.bitwise_or(src_ext, watermark)cv2.imwrite("embedding-4.jpg", dst_1)
# 水印提取dst_2 = cv2.bitwise_and(dst_1, pos)dst_2 = cv2.bitwise_xor(dst_2, ext)idx = dst_2[:,:,:]>0dst_2[idx] = 255cv2.imwrite("extraction-4.jpg", dst_2)| 原始图像 | 水印图像 | 非盲处理 | 水印嵌入 | 水印提取 |
|---|---|---|---|---|
![]() | ![]() | ![]() | ![]() | ![]() |








