python 實現LSB信息隱藏算法

要求:
在這裏插入圖片描述

具體實現:

"""
@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做了
  • 項目用到的資源圖片會在另外兩個實驗做完後打包放在一起,這裏只放了源碼
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章