要求:
具體實現:
"""
@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做了
- 項目用到的資源圖片會在另外兩個實驗做完後打包放在一起,這裏只放了源碼