要求:
具体实现:
"""
@author: Bre Athy
@contact: https://www.zhihu.com/people/you-yi-shi-de-hu-xi
@productware: PyCharm
@file: main.py
@time: 2020/5/15 10:05
"""
from PIL import Image
import os,math,numpy as np,cv2
watermask = "E:\Code\InformationHiding\Lena\watermask.png"
output = "E:\Code\InformationHiding\LSB\output"
lena = "E:\Code\InformationHiding\Lena\LENA\lena512.bmp" # 原图
embeddedGrey = os.path.join(output, "result.bmp")
originWm = os.path.join(output, "原生的水印二值图片.bmp")
extractWm = os.path.join(output, "提取出的水印二值图片.bmp")
def fillMartix(matrix, height, width):
# 填充矩阵
matrix2 = []
for x in range(height):
listX = []
for y in range(width):
listX.append(matrix[x%len(matrix)][y%len(matrix[0])])
matrix2.append(listX)
return matrix2
def getMatrix(img):
# 获取矩阵
img_data = img.load()
matrix = []
for x in range(img.height):
listX = [img_data[y,x] for y in range(img.width) ]
matrix.append(listX)
return matrix
def putLSB(OriMartrix, WmMatrix, n = -1):
# LSB嵌入:从原图左上角放置二值水印
# OriMartrix 原图的灰度矩阵
# WmMatrix 二值图像的灰度矩阵
# n 要替换的位数,0表示第1位,-1表示最后一位
Height = len(OriMartrix)
height = len(WmMatrix)
if not height:return OriMartrix
Width = len(OriMartrix[0])
width = len(WmMatrix[0])
if not width: return OriMartrix
if height > Height or width > Width:
print("水印矩阵长宽应该小于灰度矩阵!")
return
n = n%8
newMartrix = []
for row in range(Height):
listX = []
for col in range(Width):
if row < height and col < width:
w = '0'
o = "{:08b}".format(OriMartrix[row][col])
if WmMatrix[row][col]: w='1'
listX.append(int(o[:n] + w + o[(n+1):], 2))
else:
listX.append(OriMartrix[row][col])
newMartrix.append(listX)
return newMartrix
def getLSB(OldMartrix, height = 0, width = 0, n = -1):
# LSB提取:从原图左上角放置二值水印
# OldMartrix 嵌入水印的灰度图片的矩阵
# width,height 获取要裁剪的像素长和宽, 如果为0表示裁剪全图
# n 水印的位于的位数,0表示第1位,-1表示最后一位
if width > len(OldMartrix[0]) or height > len(OldMartrix):
print("水印长宽应该小于灰度矩阵!")
return
if width == 0 and height == 0:
width = len(OldMartrix[0])
height = len(OldMartrix)
n = n%8
NewMartrix = []
for row in range(height):
listX = []
for col in range(width):
if "{:08b}".format(OldMartrix[row][col])[n] == '1':listX.append(255)
else:listX.append(0)
NewMartrix.append(listX)
return NewMartrix
def psnr(img1, img2):
# PSNR计算
mse = np.mean((img1 / 255. - img2 / 255.) ** 2)
if mse < 1.0e-10:
return 100
PIXEL_MAX = 1
return 20 * math.log10(PIXEL_MAX / math.sqrt(mse))
def main():
# 获取原图像的大小并转为灰度矩阵
Ori = Image.open(lena).convert('L')
OriMartrix = getMatrix(Ori)
# 将水印图片加载为等大二值矩阵
Wm = Image.open(watermask).convert('1')
# 保存二值图片
Wm.save(originWm)
# 填充水印的像素为原图的相同大小
WmMatrix = fillMartix(getMatrix(Wm), Ori.height, Ori.width)
# 嵌入水印
NewMatrix = putLSB(OriMartrix, WmMatrix)
Img = Image.fromarray(np.array(NewMatrix)).convert("L")
# 保存嵌入图
Img.save(embeddedGrey)
print("******************** LSB嵌入 ********************")
print("原图像:", lena)
print("水印原图像:",watermask)
print("水印二值图像:", originWm)
print("嵌入水印的图像:", embeddedGrey)
# 读取嵌入图并转为灰度矩阵
Old = Image.open(embeddedGrey).convert('L')
OldMartrix = getMatrix(Old)
# 提取水印图
WmMatrix = getLSB(OldMartrix)
WmImg = Image.fromarray(np.array(WmMatrix)).convert("1")
# 保存提取的水印图
WmImg.save(extractWm)
print("******************** LSB提取 ********************")
print("提取的水印二值图像:", extractWm)
# 不可见性,PSNR
# cv2涉及的路径不能包含中文
print("******************** 不同的图的PSNR ********************")
inputDir = "E:\Code\InformationHiding\Lena\workplace"
for bmp in os.listdir(inputDir):
path1 = os.path.join(inputDir, bmp)
Ori = getMatrix(Image.open(path1).convert('L'))
Wm = fillMartix(getMatrix(Image.open(watermask).convert('1')), len(Ori), len(Ori[0]))
Gen = Image.fromarray(np.array(putLSB(Ori, Wm))).convert('L')
path2 = os.path.join(output, bmp)
Gen.save(path2)
oriImg = cv2.imread(path1)
genImg = cv2.imread(path2)
print(psnr(oriImg, genImg), bmp)
# 相同的图信息量不同的PSNR
print("******************** 对lena图嵌入不同信息量时的PSNR ********************")
OriMartrix = getMatrix(Image.open(lena).convert('L'))
Wm = getMatrix(Image.open(watermask).convert('1'))
for i in range(0, 110, 10):
WmMatrix = fillMartix(OriMartrix, int(len(OriMartrix)*i*0.01), len(OriMartrix[0]))
Gen = Image.fromarray(np.array(putLSB(OriMartrix, WmMatrix))).convert('L')
path3 = os.path.join(output, "lena " + str(i)+".bmp")
Gen.save(path3)
oriImg = cv2.imread(lena)
genImg = cv2.imread(path3)
print(i, psnr(oriImg, genImg))
# 错误率
# 比较水印的像素矩阵,计算不同的像素个数/总个数
wm1 = getMatrix(Image.open(originWm).convert("1"))
wm2 = getMatrix(Image.open(extractWm).convert("1"))
e,h,w = 0, len(wm1), len(wm1[0])
for i in range(h):
for j in range(w):
if wm1[i][j] != wm2[i][j]:
e += 1
print("******************** 错误率 ********************")
print("错误元素一共", e,"个,错误率:", e/h/w)
# 鲁棒性,加噪或压缩【保存为jpg】后查看水印错误率, 此处计算的压缩后的值
jpgPath = os.path.join(output, "result.jpg")
Old.save(jpgPath)
jpg = Image.open(jpgPath).convert('L')
wm2 = getLSB(getMatrix(jpg))
extractJpg = os.path.join(output, "压缩图片提取出的水印.bmp")
Image.fromarray(np.array(WmMatrix)).convert("1").save(extractJpg)
e, h, w = 0, len(wm1), len(wm1[0])
for i in range(h):
for j in range(w):
if wm1[i][j] != wm2[i][j]:
e += 1
print("******************** 鲁棒性测试 ********************")
print("压缩后的图片:", jpgPath)
print("提取的水印:", extractJpg)
print("错误元素一共", e, "个,错误率:", e / h / w)
if __name__ == "__main__":
main()
实机演示:
附注:
- PSNR值得计算是参考了网上的代码,才不得已引入了cv2这个库
- 这个最好载Matlab中实现,可是我懒得在学一门语言,于是就用python做了
- 项目用到的资源图片会在另外两个实验做完后打包放在一起,这里只放了源码