基于无损编码实现的视频数字水印
在重新整理了图像数字水印的代码后总结出两种简单易行的方案,其一基于离散小波变换与奇异值分解,其二基于离散小波变换与(2,2)视觉加密。从提取效果来看,第一种方式效果更好,第二种方式中用到的视觉加密则较为少见。
由于个人水平受限,两种方式均未能实现盲提取。
将图像数字水印转为视频数字水印的关键在于转化过程中如何保留有效信息,在此选择最简单的处理方式,就是选用FFV1视频无损编码,视频导出格式为.mov。之前也尝试过LAGS,HYFU但没有成功。
从原视频中抽取帧后得到载体图像,向载体图像中嵌入水印(水印已经过置乱处理)
import cv2 import pywt import numpy as np from PIL import Image from numpy import linalg as la def size(num, times): for i in range(times): num = round((num/2+1)) return num Img_path = 'C:\\Users\\20_2.bmp' #1920*1080 waterImg_path = 'C:\\Users\\logo.bmp' Img = cv2.imread(Img_path) waterImg = cv2.imread(waterImg_path, 0) R = Img.shape[0] C = Img.shape[1] r = waterImg.shape[0] c = waterImg.shape[1] RC_new = max(R, C) Img = cv2.resize(Img, (RC_new, RC_new)) Img_new = Img[:, :, [2, 1, 0]] # Img_new = cv2.cvtColor(Img, cv2.COLOR_BGR2YUV) (r, g, b) = cv2.split(Img_new) rc_new = size(RC_new, 3) waterImg = cv2.resize(waterImg, (rc_new, rc_new)) coeffs1 = pywt.wavedec2(r, 'db2', level=3) [ca, cb, cc, cd] = coeffs1 (ch3, cv3, cd3) = cb (ch2, cv2, cd2) = cc (ch1, cv1, cd1) = cd [Ua, Sa, Va] = la.svd(ca) [Uw, Sw, Vw] = la.svd(waterImg) a = 0.2 SW = Sa + a * Sw size = rc_new S = np.zeros([size, size]) for i in range(size): S[i][i] = SW[i] tmp = np.dot(Ua, S) cA = np.dot(tmp, Va) r1_new = pywt.waverec2([cA, cb, cc, cd], 'db2') merged = np.ones(Img_new.shape, dtype=np.uint8) merged[:, :, 2] = b merged[:, :, 1] = g merged[:, :, 0] = r1_new Img = Image.fromarray(merged) Img = Img.resize((C, R), Image.ANTIALIAS) #重新调整大小,抗锯齿 Img.save('C:\\Users\\dwt_svd.bmp')
之后,读取原视频文件并将处理后的图像插入对应帧中。
import cv2 def frame(path): video = cv2.VideoCapture(path) width = video.get(cv2.CAP_PROP_FRAME_WIDTH) height = video.get(cv2.CAP_PROP_FRAME_HEIGHT) size = (int(width), int(height)) fps = video.get(cv2.CAP_PROP_FPS) videoWriter = cv2.VideoWriter('C:/Users/57882/Desktop/an.mov', cv2.VideoWriter_fourcc('F','F','V','1'), fps, size)# 无损 n = 1 if video.isOpened(): # 判断是否正常打开FV rval, frame = video.read() else: rval = False timeF = 1 # 视频帧计数间隔频率,这里用到原视频的每一帧 i = 0 while rval: rval, frame = video.read() if (n % timeF == 0 and n != 20): i += 1 videoWriter.write(frame) elif(n % timeF == 0 and n == 20): newframe = cv2.imread('C:/Users/dwt_svd.bmp') #嵌入水印后的图像 videoWriter.write(newframe) n = n + 1 cv2.waitKey(0) video.release() return True path = 'D:/video.mov' # 原视频 frame(path)import cv2 def frame(path): video = cv2.VideoCapture(path) width = video.get(cv2.CAP_PROP_FRAME_WIDTH) height = video.get(cv2.CAP_PROP_FRAME_HEIGHT) size = (int(width), int(height)) fps = video.get(cv2.CAP_PROP_FPS) videoWriter = cv2.VideoWriter('C:/Users/an.avi', cv2.VideoWriter_fourcc('F','F','V','1'), fps, size)# 无损 n = 1 if video.isOpened(): # 判断是否正常打开FV rval, frame = video.read() else: rval = False timeF = 1 # 视频帧计数间隔频率,这里用到原视频的每一帧 i = 0 while rval: rval, frame = video.read() if (n % timeF == 0 and n != 20): i += 1 videoWriter.write(frame) elif(n % timeF == 0 and n == 20): newframe = cv2.imread('C:/Users/dwt_svd.bmp') #嵌入水印后的图像 videoWriter.write(newframe) n = n + 1 cv2.waitKey(0) video.release() return True path = 'D:/video.mov' # 原视频 frame(path)
因为没有合适解码,视频本身虽然没有问题,但在绝大多数电脑上应该是无法播放的,却不影响截图取帧。
再次获取到对应帧后进行水印提取,提取代码与嵌入代码部分内容是一样的。
import cv2 import pywt import numpy as np from PIL import Image from numpy import linalg as la def size(num, times): for i in range(times): num = round((num/2+1)) return num watermarked_path = 'C:\\Users\\new_20.bmp' # 含水印的图像 Img_path = 'C:\\Users\\20_2.bmp' # 载体图像 waterImg_path = 'C:\\Users\\logo.bmp' # 水印图像 watermarkedImg = cv2.imread(watermarked_path) Img = cv2.imread(Img_path) waterImg = cv2.imread(waterImg_path, 0) R = watermarkedImg.shape[0] C = watermarkedImg.shape[1] r = waterImg.shape[0] c = waterImg.shape[1] RC_new = max(R, C) watermarkedImg = cv2.resize(watermarkedImg, (RC_new, RC_new)) Img = cv2.resize(Img, (RC_new, RC_new)) watermarked_new = watermarkedImg[:, :, [2, 1, 0]] (r1, g1, b1) = cv2.split(watermarked_new) Img_new = Img[:, :, [2, 1, 0]] (r2, g2, b2) = cv2.split(Img_new) rc_new = size(RC_new, 3) waterImg = cv2.resize(waterImg, (rc_new, rc_new)) coeffs1 = pywt.wavedec2(r1, 'db2', level=3) [cA, cB, cC, cD] = coeffs1 (cH3, cV3, cD3) = cB (cH2, cV2, cD2) = cC (cH1, cV1, cD1) = cD coeffs2 = pywt.wavedec2(r2, 'db2', level=3) [ca, cb, cc, cd] = coeffs2 (ch3, cv3, cd3) = cb (ch2, cv2, cd2) = cc (ch1, cv1, cd1) = cd [Ua, SW, Va] = la.svd(cA) [Uw, Sw, Vw] = la.svd(waterImg) [Ua, Sa, Va] = la.svd(ca) a = 0.2 Sw = (SW - Sa)/a size = rc_new S = np.zeros([size, size]) for i in range(size): S[i][i] = Sw[i] tmp = np.dot(Uw, S) waterImg_new = np.dot(tmp, Vw) Img = Image.fromarray(waterImg_new) Img = Img.resize((r, c), Image.ANTIALIAS) #重新调整大小,抗锯齿 Img = Img.convert("L") Img.save('C:\\Users\\watermark.bmp')