一个最简单的SVD 嵌入数字水印的代码,
这种水印方法是2002年被提出的,单纯的使用这种方法会导致假阳性问题,因此,一般和别的方法结合使用。
为了更好的表现水印嵌入效果,我将一个bit水印信息嵌入到一块内,这样做最简单,但是效果肯定比较差,主要用来理解这个方法。
代码如下:
首先定义 块SVD 变换:
def block_SVD(M, blk_size):
row_block = M.shape[0]//blk_size[0]
col_block = M.shape[1]//blk_size[1]
U = np.empty((row_block, col_block), dtype = object)
S = np.empty((row_block, col_block), dtype = object)
V = np.empty((row_block, col_block), dtype = object)
M = M[:M.shape[0]-M.shape[0]%blk_size[0],
:M.shape[1]-M.shape[1]%blk_size[1]]
rows = []
for i in range(0, M.shape[0], blk_size[0]):
cols = []
for j in range(0, M.shape[1], blk_size[1]):
max_ndx = (min(i+blk_size[0], M.shape[0]),
min(j+blk_size[1], M.shape[1]))
u, s, v = np.linalg.svd(M[i:max_ndx[0], j:max_ndx[1]])
U[i//blk_size[0],j//blk_size[1]] = u
S[i//blk_size[0],j//blk_size[1]] = s
V[i//blk_size[0],j//blk_size[1]] = v
return U,S,V
然后是块ISVD变换(我自己起的名字),其实就是矩阵相乘的快处理,用来复原图像:
def block_ISVD(U, S, V, blk_size):
row_U, col_U = U.shape
U = U.flatten()
S = S.flatten()
V = V.flatten()
im_tmp = np.empty_like(U)
im_wm = np.empty((row_U*blk_size[0],col_U*blk_size[1]))
for i in range(U.size):
tmp = np.matmul(U[i],np.diag(S[i]))
im_tmp[i] = np.matmul(tmp,V[i])
im_tmp = im_tmp.reshape(row_U, col_U)
for i in range(row_U):
for j in range(col_U):
im_wm[i*blk_size[0]:(i+1)*blk_size[0], j*blk_size[1]:(j+1)*blk_size[1]] = im_tmp[i,j]
return im_wm
然后是提取的嵌入操作
def SVD_embed(im, wm,strength, blk_size):
U,S,V = block_SVD(im, blk_size)
S_new = embed(S, wm, strength)
im_wm = block_ISVD(U, S_new, V, blk_size)
return im_wm
def SVD_extract(im_wm, im, strength, block_size ):
_,S,_ = block_SVD(im, block_size)
_,S_wm,_ = block_SVD(im_wm, block_size)
tmp = S_wm - S
wm_ex = np.empty_like(tmp, dtype = int)
for i in range(tmp.shape[0]):
for j in range(tmp.shape[1]):
wm_ex[i,j] = np.sum(tmp[i,j])
wm_ex = wm_ex / block_size[0]/strength
return wm_ex
def embed(S, wm, strength):
wm = wm.flatten()
row, col = S.shape
S = S.flatten()
for i in range(S.size):
S[i]=S[i]+ strength * wm[i]
S = S.reshape(row,col)
return S
最后是主函数:
# SVD Watermark
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
from skimage import data
strength = 0.1
im = data.camera()
wm = Image.open('watermark.png')
wm = wm.resize((64,64))
wm = np.array(wm)
im_wm = SVD_embed(im, wm, strength, (8,8))
wm_ex = SVD_extract(im_wm, im, strength, (8,8))
fig, (ax_wm, ax_im, ax_im_wm, ax_wm_ex)=plt.subplots(nrows = 1,ncols = 4, figsize = [20,20])
ax_wm.imshow(wm, cmap = plt.cm.gray)
ax_wm.set_xlabel('wm')
ax_im.imshow(im, cmap = plt.cm.gray)
ax_im.set_xlabel('im')
ax_im_wm.imshow(im_wm, cmap = plt.cm.gray)
ax_im_wm.set_xlabel('im_wm')
ax_wm_ex.imshow(wm_ex, cmap = plt.cm.gray)
ax_wm_ex.set_xlabel('wm_ex')
下面是效果展示,可以看出,由于水印强度比较大,还是有比较强的噪声出现,不喜欢噪声的同学可以将强度设置的小一点。