论文复现代码《基于自适应哈夫曼编码的密文可逆信息隐藏算法》导演剪辑版

前言

本篇是论文《基于自适应哈夫曼编码的密文可逆信息隐藏算法》复现代码的精简版本。

内含调试过程的代码在这里:

论文复现代码《基于自适应哈夫曼编码的密文可逆信息隐藏算法》调试版-CSDN博客

论文的解析文章在这里:

论文简述基于自适应哈夫曼编码的密文可逆信息隐藏算法(基于位图压缩的加密图像可逆信息隐藏算法)_zrc007007的博客-CSDN博客

复现代码

import numpy as np
from skimage import io
import matplotlib.pyplot as plt
import math
from math import inf


class HuffCode:
    def compare(self, num1, num2):
        # 比较像素值的相同比特位数
        count = 0
        for i in range(8):
            divisor = pow(2, 7-i)
            if int(num1 // divisor) == int(num2 // divisor):
                count += 1
                num1 = num1 - divisor
                num2 = num2 - divisor
            else:
                break
        return count

    def count(self, origindata,  data):
        # 生成图像相同像素值的比特位数的比较结果
        result = {}
        resultMap = np.zeros_like(data)
        resultMap[0][0] = -1
        for i in range(1, len(data)):
            resultMap[i][0] = -1
            resultMap[0][i] = -1
        for i in range(1, len(data)):
            for j in range(1, len(data[0])):
                num = self.compare(origindata[i][j], data[i][j])
                resultMap[i][j] = num
                result[num] = result.get(num, 0) + 1
        return resultMap, result


# 初始化哈夫曼结点
class Huffmannode(object):
    def __init__(self):
        self.parent=0
        self.left=0
        self.right=0
        self.weight=0


# 选择最小的结点下标
def select_node(huffman):
    # 俩个结点直接返回不需要找最小俩个结点
    if len(huffman)==2:
        return 0,1
    min=semin=inf# 初始化成无穷大
    f=s=-1
    for i in range(len(huffman)):
        if huffman[i].parent==0:
            if min>huffman[i].weight:
                semin=min
                s=f
                min=huffman[i].weight
                f=i
            elif semin>huffman[i].weight:
                semin=huffman[i].weight
                s=i
    return f,s


# 编码
def Huffman_code(origin_dict):
    # 给结点赋权重
    n=len(origin_dict)
    m=2*n-1
    huffman=[]
    for i in origin_dict:
        temp_huffmannode=Huffmannode()
        temp_huffmannode.weight=origin_dict[i]
        huffman.append(temp_huffmannode)
    # 构建Huffman树,选择俩个最小的结点合并
    for i in range(n,m):
        f,s=select_node(huffman)
        temp_huffmannode=Huffmannode()
        temp_huffmannode.weight=huffman[f].weight+huffman[s].weight
        temp_huffmannode.right=f  # 小的放在右边
        temp_huffmannode.left=s
        huffman[f].parent=huffman[s].parent=i
        huffman.append(temp_huffmannode)

    # 0,1编码,右0,左1
    codeing_dict = dict.fromkeys(origin_dict, None)
    for i in range(0,n):
        s=''
        k=i
        parent=huffman[i].parent
        while parent!=0:
            if huffman[parent].left==k:
                s+='1'
                k=parent
                parent=huffman[parent].parent
            else:
                s+='0'
                k=parent
                parent=huffman[parent].parent
        codeing_dict[list(origin_dict.keys())[i]]=list(reversed(s))
    for k in codeing_dict.items():
        codeing_dict[k[0]] = ''.join(k[1])

    return codeing_dict


class MED:
    # 示例数据
    def __init__(self):
        self.data = [
            [162, 162, 162, 161, 162, 157, 163, 161],
            [162, 162, 162, 161, 162, 157, 163, 161],
            [162, 162, 162, 161, 162, 157, 163, 161],
            [162, 162, 162, 161, 162, 157, 163, 161],
            [162, 162, 162, 161, 162, 157, 163, 161],
            [164, 164, 158, 155, 161, 159, 159, 160],
            [160, 160, 163, 158, 160, 162, 159, 156],
            [159, 159, 155, 157, 158, 159, 156, 157]
        ]

    def detect(self, data):
        # 中值预测
        result = np.zeros_like(data)
        result[0][0] = data[0][0]
        for i in range(1, len(data)):
            result[i][0] = data[i][0]
            result[0][i] = data[0][i]
        for i in range(1, len(data)):
            for j in range(1, len(data[0])):
                a = data[i - 1][j - 1]
                b = data[i - 1][j]
                c = data[i][j - 1]
                if a <= min(b, c):
                    result[i][j] = max(b, c)
                elif a >= max(b, c):
                    result[i][j] = min(b, c)
                else:
                    result[i][j] = b + c - a
        return result


def crypt(im, miu, x0):
    '''
    logistic混沌加密
    :param im: 图像
    :param miu: μ参数
    :param x0: x0参数
    :return: 返回密文图像
    '''
    h, w = im.shape
    c_ = np.zeros_like(im)
    c_[0][0] = x0
    c=[]
    c.append(x0)
    result = np.zeros_like(im)
    result[0][0] = (x0 * pow(2, 48)) % 256
    for x in range(1, h * w):
        i = x // w
        j = x % w
        c.append(miu * c[x - 1] * (1 - c[x - 1]))
        c_[i][j] = (c[x] * pow(2, 48)) % 256
    for i in range(h):
        for j in range(w):
            result[i][j] = im[i][j] ^ c_[i][j]
    return result


def transform(img):
    column, row = img.shape
    result = np.zeros_like(img, dtype='int_')
    for i in range(column):
        for j in range(row):
            result[i][j] = int(img[i][j] * 255)
    return result


def int2bin(num, leng):
    '''
    整数int转二进制格式bin
    Transform a integer to a string whose length is leng.
    :param num: numbers waiting for transforming
    :param leng: the length of hoped result string
    :return: a string of leng length
    '''
    if leng < 1:
        raise Exception("Length isn't positive integer!")
    place = 1
    result = ''
    while place <= leng:
        result = str(num % 2) + result
        num = num // 2
        if num == 0:
            return (leng - place) * '0' + result
        place += 1
    raise Exception("Length of binary string isn't enough!")


def bin2int(string, leng):
    '''
    二进制格式bin转整数int
    Transfer a string of length of leng to a positive integer
    :param string: string waiting to be transferred
    :param leng: the length of reading
    :return: a positive integer less than 2^leng
    '''
    if leng < 1:
        raise Exception("Length isn't positive integer!")
    if len(string) < leng:
        raise Exception("Length of string under transferring isn't enough!")
    place = 1
    result = 0
    while place <= leng:
        result += int(string[place - 1]) * pow(2, leng - place)
        place += 1
    return result


def binstrcomplmt(string, length, complement):
    '''
    给二进制格式补位,补到前面。比如补0就给complement传0,补1就传1
    Complement strings with which we decide.
    :param string: string waiting to be complemented
    :param length: what we hope it to length
    :param complement: using what character to complement it
    :return: string already complemented
    '''
    return (length - len(string)) * complement + string


def binstrbackcomplmt(string, length, complement):
    '''
    给二进制格式补位,补到后面
    Complement strings with which we decide on its backhand.
    :param string: string waiting to be complemented
    :param length: what we hope it to length
    :param complement: using what character to complement it
    :return: string already complemented
    '''
    return string + (length - len(string)) * complement


def generatec(dict):
    '''
    生成嵌入信息中的c1到c8
    generate c info
    :param dict: Huffman code diction
    :return:
    '''
    result = ''
    c = []
    for i in range(9):
        c.append(0)
        c[i] = len(dict.get(i, ''))
        result += int2bin(c[i], 4)
    return result


def generateh(dict):
    '''
    生成嵌入信息中的h1到h8
    generate h info
    :param dict: Huffman code diction
    :return:
    '''
    result = ''
    h = []
    for i in range(9):
        h.append('')
        h[i] = binstrcomplmt(dict.get(i, '0'), 8, '0')
        result += h[i]
    return result


def countLm(countDict, lenDict):
    '''
    生成论文中说的Lm,变成Eta还需要转换一下
    Counting Lm
    :param countDict: diction of Huffman codes' appearance
    :param lenDict: Huffman code diction
    :return:
    '''
    count = 0
    for i in range(9):
        count += countDict.get(i, 0) * len(lenDict.get(i, ''))
    return count


def generatef(Lm, shapeOfImg):
    '''
    生成嵌入信息中的f
    generate f, which is Lm's bits' num's binary format
    :param Lm:
    :param shapeOfImg:
    :return:
    '''
    return int2bin(Lm, math.ceil(np.log2(shapeOfImg[0])) + math.ceil(np.log2(shapeOfImg[1])) + 2)


def generateEta(dic, labelmap):
    '''
    生成嵌入信息中的Eta
    Transferred label map's binary format
    :param dic:
    :param labelmap:
    :return:
    '''
    result = ''
    for i in range(1, labelmap.shape[0]):
        for j in range(1, labelmap.shape[1]):
            result += dic[labelmap[i][j]]
    return result


def generatelabelinfo(HuffDict, CountDict, LabelMap):
    '''
    生成总的嵌入信息
    Generate embedding information of label map
    :param HuffDict: Huffman code diction
    :param CountDict: Length of Huffman codes' diction
    :param LabelMap: The label map
    :return: Embedding information of label map
    '''
    return generatec(HuffDict) + generateh(HuffDict) + generatef(countLm(CountDict,HuffDict), LabelMap.shape)\
           + generateEta(HuffDict, LabelMap)


def embedinfo(LabelInfo, EncryptedImg, LabelMap, data):
    # 嵌入位图信息
    ReferencePixels = ''
    result = np.array(EncryptedImg)
    for i in range(EncryptedImg.shape[0]):
        ReferencePixels += int2bin(EncryptedImg[0][i], 8)
        result[0][i] = bin2int(LabelInfo, 8)
        LabelInfo = LabelInfo[8:]  # 截掉8个
    for i in range(1, EncryptedImg.shape[1]):
        ReferencePixels += int2bin(EncryptedImg[i][0], 8)
        result[i][0] = bin2int(LabelInfo, 8)
        LabelInfo = LabelInfo[8:]
    EmbeddingInfo = LabelInfo + ReferencePixels + data
    count = 0
    row = count // (EncryptedImg.shape[1] - 1) + 1
    column = count % (EncryptedImg.shape[1] - 1) + 1
    t = LabelMap[row][column]
    maximum = (EncryptedImg.shape[0] - 1) * (EncryptedImg.shape[1] - 1)
    while len(EmbeddingInfo) > 0:
        if 0 <= t <= 6:
            result[row][column] = EncryptedImg[row][column] % pow(2, 7 - t)\
                                  + bin2int(binstrbackcomplmt(EmbeddingInfo[:t+1], 8, '0'), 8)
            EmbeddingInfo = EmbeddingInfo[t+1:]
        elif 7 <= t <= 8:
            result[row][column] = bin2int(binstrbackcomplmt(EmbeddingInfo[:8], 8, '0'), 8)
            EmbeddingInfo = EmbeddingInfo[8:]
        count += 1
        if count >= maximum:
            raise Exception("There's no room for embedding!")
        row = count // (EncryptedImg.shape[1] - 1) + 1
        column = count % (EncryptedImg.shape[1] - 1) + 1
        t = LabelMap[row][column]
    return result


def HuffDecode(HuffDict, info):
    # 哈夫曼解码
    for i in range(9):
        if info[:len(HuffDict.get(i, ''))] == HuffDict.get(i, -1):
            return i, len(HuffDict[i])
    raise Exception("No string matches!")


def extractpixel(pixel, t):
    # 根据t值,恢复像素位数
    if 0 <= t <= 7:
        return int2bin(pixel, 8)[:t+1]
    elif t == 8:
        return int2bin(pixel, 8)
    else:
        raise Exception("T out of range!")


def calctcut(t):
    # 根据t值,返回像素数
    if 0 <= t <= 7:
        return t + 1
    elif t == 8:
        return 8
    else:
        raise Exception("T out of range!")


def extract1(encrypted):
    # 提取第一行、第一列
    info = ''
    LenDict = {}
    HuffDict = {}
    for i in range(encrypted.shape[0]):
        info += int2bin(encrypted[0][i], 8)
    for i in range(1, encrypted.shape[1]):
        info += int2bin(encrypted[i][0], 8)
    for i in range(9):
        LenDict[i] = bin2int(info[:4], 4)
        info = info[4:]
    for i in range(9):
        if LenDict[i] != 0:
            HuffDict[i] = info[8-LenDict[i]:8]
        info = info[8:]
    return HuffDict, info


def extract_exp(encrypted, HuffDict, info, datalen, Label):  # Label是用来测试位图和算出来的是不是一样的,可以删掉
    # 提取位图
    LabelMap = np.zeros_like(encrypted)
    decrypted1 = np.array(encrypted)
    LmLen = math.ceil(np.log2(encrypted.shape[0])) + math.ceil(np.log2(encrypted.shape[1])) + 2
    Lm = bin2int(info[:LmLen], LmLen)
    info = info[LmLen:]
    infoLen = Lm + ((encrypted.shape[0] - 1) + (encrypted.shape[1] - 1) - 1) * 8 + datalen
    count = len(info)  # 已提取到的信息长度
    place = 0
    pursuit = 0
    gonext = 0
    count1 = 1
    while count < infoLen:
        row = place // (encrypted.shape[1] - 1) + 1
        column = place % (encrypted.shape[1] - 1) + 1
        if count1 == 1 and count < Lm:
            t, CodeLen = HuffDecode(HuffDict, info)
            LabelMap[row][column] = t
            info = info[CodeLen:] + extractpixel(encrypted[row][column], t)
            count += calctcut(t)
            place += 1
        else:
            pursuit = 1
            count1 = 0
        if pursuit == 1 and gonext == 0:
            record = place
            while place < (encrypted.shape[0] - 1) * (encrypted.shape[1] - 1):
                row = place // (encrypted.shape[1] - 1) + 1
                column = place % (encrypted.shape[1] - 1) + 1
                t, CodeLen = HuffDecode(HuffDict, info)
                LabelMap[row][column] = t
                info = info[CodeLen:] + extractpixel(encrypted[row][column], t)
                place += 1
            place = record
            row = place // (encrypted.shape[1] - 1) + 1
            column = place % (encrypted.shape[1] - 1) + 1
            gonext = 1
        if gonext == 1 and count - Lm < (encrypted.shape[0] - 1) * (encrypted.shape[1] - 1) * 8 + datalen:
            info += extractpixel(encrypted[row][column], LabelMap[row][column])
            count += calctcut(LabelMap[row][column])
            place += 1
    for i in range(encrypted.shape[0]):
        LabelMap[0][i] = -1
        decrypted1[0][i] = bin2int(info[:8], 8)
        info = info[8:]
    for i in range(1, encrypted.shape[1]):
        LabelMap[i][0] = -1
        decrypted1[i][0] = bin2int(info[:8], 8)
        info = info[8:]
    data = info[:datalen]
    return LabelMap, decrypted1, data


def reverse(char):
    # 反转0和1
    if char == '0':
        return '1'
    elif char == '1':
        return '0'
    else:
        raise Exception("Not 0 or 1!")


def detect(data, i, j):
    '''
    测试用预测函数
    :param data:
    :return:
    '''
    a = data[i - 1][j - 1]
    b = data[i - 1][j]
    c = data[i][j - 1]
    if a <= min(b, c):
        result = max(b, c)
    elif a >= max(b, c):
        result = min(b, c)
    else:
        result = b + c - a
    return result


def recovery(img, LabelMap):
    # 恢复图像
    result = np.zeros_like(img)
    for i in range(img.shape[0]):
        result[0][i] = img[0][i]
    for i in range(1, img.shape[1]):
        result[i][0] = img[i][0]
    for i in range(1, img.shape[0]):
        for j in range(1, img.shape[1]):
            px = detect(result, i, j)
            t = LabelMap[i][j]
            if t == 8:
                result[i][j] = px
            elif 0 <= t <= 7:
                result[i][j] = bin2int(int2bin(px, 8)[:t] + '0' * (8 - t), 8)\
                               + int(reverse(int2bin(px, 8)[t:t+1])) * pow(2, 7 - t) + img[i][j] % pow(2, 7 - t)
            else:
                raise Exception("T out of range!")
    return result


if __name__ == "__main__":
    MED = MED()
    # origindata = MED.data
    origindata = io.imread("../img/lena_grey.tif")
    origindata = transform(origindata)
    origindata = np.array(origindata)
    MEDdata = MED.detect(origindata)
    print("The origin data is:")
    print(np.array(origindata))
    print("The predicted data is:")
    print(MEDdata)
    print(HuffCode().compare(159, 160))
    resultMap, countingResult = HuffCode().count(origindata, MEDdata)
    print("The label map is:")
    print(resultMap)
    print("The counting result of Huffman coding is:", countingResult)

    code_dict = Huffman_code(countingResult)
    print(code_dict)
    generatec(code_dict)
    print(generatec(code_dict))
    print(len(generatec(code_dict)))
    print(generateh(code_dict), len(generateh(code_dict)))

    lm = countLm(countingResult, code_dict)
    print("Lm is:", lm)
    f = generatef(lm, MEDdata.shape)
    print("f is:", f)
    print("length of f is:", len(f))
    Eta = generateEta(code_dict, resultMap)
    print("The length of Eta is:", len(Eta))
    print("Eta is:")
    print(Eta)

    labelInfo = generatelabelinfo(code_dict, countingResult, resultMap)
    print("The length of labelInfo is:", len(labelInfo))
    print("Label info:")
    print(labelInfo)
    secret = '0110010000' * 50  # 用于测试的秘密信息
    embedded = embedinfo(labelInfo, crypt(origindata, 3.6, 0.5), resultMap, secret)
    print(embedded)
    huff_code, info1 = extract1(embedded)
    print("The length of rest of info waiting for extracting is:", len(info1))
    print(int2bin(100, 8))
    print(extractpixel(100, 0))
    # resultMap2, decrypted1, data= extract2(embedded, huff_code, info1, 4)
    resultMap2, decrypted1, data = extract_exp(embedded, huff_code, info1, len(secret), resultMap)  # 嵌入秘密信息
    print("The encrypted img is:\n", crypt(origindata, 3.6, 0.5))
    print("The decrypted1 img is:\n", decrypted1)
    print(resultMap2)
    print(resultMap)
    print(data)
    if secret == data:
        print("THE extract is successful!")
    img1 = crypt(decrypted1, 3.6, 0.5)
    print("img1 is:\n", img1)
    '''
    print("Origin:")
    print(origindata)
    print("Detect img:")
    print(detect(img1))
    print("Detected origin data:")
    print(detect(origindata))
    '''
    res = img1 - origindata
    print("result compare with origin data:\n")
    print(res)
    img2 = recovery(img1, resultMap2)
    print(img2)
    res = img2 - origindata
    print("img2 compares to origin:\n", res)
    plt.set_cmap(cmap='gray')
    plt.subplot(221)
    plt.imshow(origindata)
    plt.title("Origin imagery")
    plt.subplot(222)
    plt.imshow(embedded)
    plt.title("Embedded imagery")
    plt.subplot(223)
    plt.imshow(img1)
    plt.title("Preliminary decrypted imagine")
    plt.subplot(224)
    plt.imshow(img2)
    plt.title("Fully recovered imagine")
    plt.show()

猜你喜欢

转载自blog.csdn.net/m0_46948660/article/details/134643857