最近在开发一个视频LSB水印隐写加密的项目,遇到了一些问题。我的思路是 原视频提取帧 保存成图片 然后将水印LSB隐写进图片 再将经过隐写的图片转为视频。但是转出来的视频,再提取帧进行解密出水印的时候,就会出现下图这样的花屏。但是在图片转视频的之前,图片是可以正常解密出水印的。相关代码已经贴在下面了,求解。想过是视频压缩问题,但是已经试过几种视频格式了,包括I,4,2,0格式,仍然没有解决。
代码下载
https://pan.baidu.com/s/1kqu9ErnTcc-VkgL-LjWePw?pwd=afyx提取码:afyx
帧转视频
import os
import cv2
import time
img_path = './outimages'
# 随便从其中拿到一张图片来代表视频中图片的尺寸
img = cv2.imread('./outimages/1.bmp')
imgInfo = img.shape
size = (imgInfo[1], imgInfo[0])
# 获得文件夹中图片的数量,从而进行循环生成视频文件
img_nums = len(os.listdir(img_path))
fourcc = cv2.VideoWriter_fourcc('M','J','P','G')
# 写入对象 1 file name 2 编码器 3 帧率 4 尺寸大小
videoWrite = cv2.VideoWriter(
os.path.join(img_path,'../', 'videos.avi'), fourcc, 30, size)
# 读取这个文件夹中的每一张图片(按照顺序)然后组合成视频,帧率是每秒 30 帧
for i in range(len(os.listdir(img_path))):
filename = str(i+1) + ".bmp"
filename = os.path.join(img_path, filename)
# print(filename)
img = cv2.imread(filename)
videoWrite.write(img)
视频转帧
import cv2
import shutil
import os
def video2img(input_path, outpath):
cap = cv2.VideoCapture(input_path)
total_frame = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
print('总帧数:', total_frame)
if not os.path.isdir(outpath):
os.makedirs(outpath)
else:
shutil.rmtree(outpath)
os.makedirs(outpath)
counter = 0
if cap.isOpened():
while True:
ret, frame = cap.read()
if not ret:
break
counter += 1
imgname = "%s.bmp" % str(counter)
path = os.path.join(outpath, imgname)
cv2.imwrite(path, frame)
print(counter)
cap.release()
video2img('videos.avi', 'testimages')
加密部分
from PIL import Image
def plus(str):
# 返回指定长度的字符串,原字符串右对齐,前面填充0。
return str.zfill(8)
def getCode(img):
str = ""
# 获取到水印的宽和高进行遍历
for i in range(img.size[0]):
for j in range(img.size[1]):
# 获取水印的每个像素值
rgb = img.getpixel((i, j))
# 将像素值转为二进制后保存
str = str + plus(bin(rgb[0]).replace('0b', ''))
str = str + plus(bin(rgb[1]).replace('0b', ''))
str = str + plus(bin(rgb[2]).replace('0b', ''))
# print(plus(bin(rgb[0]).replace('0b', ''))+"\n")
# print(plus(bin(rgb[1]).replace('0b', '')) + "\n")
# print(plus(bin(rgb[2]).replace('0b', '')) + "\n")
print(str)
return str
def encry(img, code):
# 计数器
count = 0
countname=0
# 二进制像素值的长度,可以认为要写入图像的文本长度,提取(解密)时也需要此变量
codeLen = len(code)
print(codeLen)
# 获取到图像的宽、高进行遍历
for i in range(img.size[0]):
for j in range(img.size[1]):
# 获取到图片的每个像素值
data = img.getpixel((i, j))
# 如果计数器等于长度,代表水印写入完成
if count == codeLen:
break
# 将获取到的RGB数值分别保存
r = data[0]
g = data[1]
b = data[2]
"""
下面的是像素值替换,通过取模2得到最后一位像素值(0或1),
然后减去最后一位像素值,在将code的值添加过来
"""
r = (r - r % 2) + int(code[count])
count += 1
if count == codeLen:
img.putpixel((i, j), (r, g, b))
break
g = (g - g % 2) + int(code[count])
count += 1
if count == codeLen:
img.putpixel((i, j), (r, g, b))
break
b = (b - b % 2) + int(code[count])
count += 1
if count == codeLen:
img.putpixel((i, j), (r, g, b))
break
# 每3次循环表示一组RGB值被替换完毕,可以进行写入
if count % 3 == 0:
img.putpixel((i, j), (r, g, b))
#countname+=1
#savename="outimages/%s.bmp" % str(countname)
#img.save(savename)
return img
# 获取图像对象
for i in range(1,158):
imgname = "images/%s.bmp" % str(i)
savename = "outimages/%s.bmp" % str(i)
img1 = Image.open(imgname)
img2 = Image.open('wate1r.bmp')
# 将图像转换为RGB通道,才能分别获取R,G,B的值
rgb_im1 = img1.convert('RGB')
rgb_im2 = img2.convert('RGB')
# 将水印的像素值转为文本
code = getCode(rgb_im2)
# 将水印写入图像
encry(rgb_im1, code).save(savename)
解密部分
from PIL import Image
def deEncry(img, length):
# 获取图片的宽和高
width = img.size[0]
height = img.size[1]
# 计数器
count = 0
# 结果文本,从图片中提取到的附加值(加密时附加在每个RGB通道后的二进制数值)
wt = ""
# 遍历图片
for i in range(width):
for j in range(height):
# 获取像素点的值
rgb = img.getpixel((i, j))
# 提取R通道的附加值
if count % 3 == 0:
count += 1
wt = wt + str(rgb[0] % 2)
if count == length:
break
# 提取G通道的附加值
if count % 3 == 1:
count += 1
wt = wt + str(rgb[1] % 2)
if count == length:
break
# 提取B通道的附加值
if count % 3 == 2:
count += 1
wt = wt + str(rgb[2] % 2)
if count == length:
break
if count == length:
break
return wt
def showImage(wt):
str1 = []
for i in range(0, len(wt), 8):
# 以每8位为一组二进制,转换为十进制
str1.append(int(wt[i:i + 8], 2))
# 图片大于水印图片2个像素,便于对比
img_out = Image.new("RGB", (802, 47))
flag = 0
for m in range(0, 800):
for n in range(0, 45):
img_out.putpixel((m, n), (str1[flag], str1[flag + 1], str1[flag + 2]))
flag += 3
img_out.show()
length = 864000
#for i in range(1,158):
#imgname = "outimages/%s.bmp" % str(i)
imgname="testimages/1.bmp"
img = Image.open(imgname)
rgb_im1 = img.convert('RGB')
wt = deEncry(rgb_im1, length)
showImage(wt)