数字图像处理 信息隐藏 基于哈夫曼编码的可逆信息隐藏

本文介绍了在数字图像处理中如何利用哈夫曼编码进行可逆信息隐藏。首先,通过像素预测计算预测值px,接着进行比特位对比生成像素位图。然后,使用加密密钥对图像进行加密。关键步骤在于选择哈夫曼编码,以较短的编码标记出现频率较高的像素,并创建编码与像素的映射关系。在存储映射关系的同时,确保了位的可逆恢复,从而提升了信息隐藏的效率。
摘要由CSDN通过智能技术生成

算法框架
像素预测–求预测值px:
在这里插入图片描述在这里插入图片描述
比特位对比–生成像素位图:
将原像素x和预测误差px转换为8位二进制,然后从高位到低位逐位比较,相同的比特数目即为该像素点的标记。
在这里插入图片描述在这里插入图片描述图像加密:通过加密密钥生成一个和图像大小相同的随机矩阵,将原始图像和随机矩阵进行bit异或加密,得到加密图像。

哈夫曼编码标记:9个哈夫曼编码:00;01;100;101;1100;1101;1110;11110;11111
规则:用较短的编码来标记较多的像素。
位图分布情况Map即为转换过后的位图,根据哈夫曼编码规则建立编码和像素之间的映射关系
在这里插入图片描述取相同的位是因为相同的位直接可以根据位图还原,而第一个不同位取反也能恢复原图,所以可多取一位。
在这里插入图片描述由于编码的映射关系也需要存储,所以这时就体现出了高频低比特的好处,可嵌入位的净增加值得到了提升
在这里插入图片描述其中(32+20)表示哈夫曼编码映射关系和标记图的长度信息。

主函数:
clear
clc
I = imread('Airplane.tiff');

origin_I = double(I);  %double(I)是将读入的图像I的uint8数据转换为double类型的数据
%% 产生二进制秘密数据
num = 10000;
rand('seed',0); %设置种子
D = round(rand(1,num)*1); %产生稳定随机数
%% 使用图片代替随机数据
% J = imread('Jetplane.tiff');
% D = dec2bin(J);%将待嵌入数据转换成二进制形式
% D = strcat(char(D)', '');%将其转换成字符数组
% D = str2num(D(:));%将其转换成整数数组
% num=numel(D);%统计待嵌入数据总长度

%% 设置图像加密密钥及数据加密密钥
Image_key = 1; 
Data_key = 2;
%% 设置参数(方便实验修改)
ref_x = 1; %用来作为参考像素的行数
ref_y = 1; %用来作为参考像素的列数
%% 图像加密及数据嵌入
[encrypt_I,stego_I,emD] = Encrypt_Embed(origin_I,D,Image_key,Data_key,ref_x,ref_y);
%% 数据提取及图像恢复
num_emD = length(emD);
if num_emD > 0  %表示有空间嵌入数据
    %--------在加密标记图像中提取信息--------%
    [Side_Information,Refer_Value,Encrypt_exD,Map_I,sign] = Extract_Data(stego_I,num,ref_x,ref_y);
    if sign == 1 %表示能完全提取辅助信息
        %---------------数据解密----------------%
        [exD] = Encrypt_Data(Encrypt_exD,Data_key);
        %---------------图像恢复----------------%
        [recover_I] = Recover_Image(stego_I,Image_key,Side_Information,Refer_Value,Map_I,num,ref_x,ref_y);
        %---------------图像对比----------------%
        figure;
        subplot(221);imshow(origin_I,[]);title('原始图像');
        subplot(222);imshow(encrypt_I,[]);title('加密图像');
        subplot(223);imshow(stego_I,[]);title('载密图像');
        subplot(224);imshow(recover_I,[]);title('恢复图像');
        %---------------结果记录----------------%
        [m,n] = size(origin_I);
        bpp = num_emD/(m*n);
        %---------------结果判断----------------%
        check1 = isequal(emD,exD);
        check2 = isequal(origin_I,recover_I);
        if check1 == 1
            disp('提取数据与嵌入数据完全相同!')
        else
            disp('Warning!数据提取错误!')
        end
        if check2 == 1
            disp('重构图像与原始图像完全相同!')
        else
            disp('Warning!图像重构错误!')
        end
        %---------------结果输出----------------%
        if check1 == 1 && check2 == 1
            disp(['嵌入容量为 : ' num2str(num_emD)])
            disp(['嵌入率为 : ' num2str(bpp)])
            fprintf(['该测试图像------------ OK','\n\n']);
        else
            fprintf(['该测试图像------------ ERROR','\n\n']);
        end     
    else
        disp('无法提取全部辅助信息!')
        fprintf(['该测试图像------------ ERROR','\n\n']);
    end
else
    disp('辅助信息大于总嵌入量,导致无法存储数据!') 
    fprintf(['该测试图像------------ ERROR','\n\n']);
end

二进制转十进制:
function [value] = Binary_Decimalism(bin2_8)
% 函数说明:将二进制数组转换成十进制整数
% 输出:bin2_8(二进制数组)
% 输入:value(十进制整数)
value = 0;
len = length(bin2_8);
for i=1:len
    value = value + bin2_8(i)*(2^(len-i));
end

十进制转二进制:
function [bin2_8] = Decimalism_Binary(value)
% 函数说明:将十进制灰度像素值转换成8位二进制数组
% 输入:value(十进制灰度像素值)
% 输出:bin2_8(8位二进制数组)
bin2_8 = dec2bin(value)-'0';
if length(bin2_8) < 8
    len = length(bin2_8);
    B = bin2_8;
    bin2_8 = zeros(1,8);
    for i=1:len
        bin2_8(8-len+i) = B(i); %不足8位前面补充0
    end 
end

预测误差:
function [origin_PV_I] = Predictor_Value(origin_I,ref_x,ref_y)  
% 函数说明:计算origin_I的预测值
% 输入:origin_I(原始图像),ref_x,ref_y(参考像素的行列数)
% 输出:origin_PV_I(原始图像的预测值)
[row,col] = size(origin_I); %计算origin_I的行列值
origin_PV_I = origin_I;  %构建存储origin_I预测值的容器
for i=ref_x+1:row  %前ref_x行作为参考像素,不用预测
    for j=ref_y+1:col  %前ref_y列作为参考像素,不用预测
        a = origin_I(i-1,j);
        b = origin_I(i-1,j-1);
        c = origin_I(i,j-1);
        if b <= min(a,c)
            origin_PV_I(i,j) = max(a,c);
        elseif b >= max(a,c)
            origin_PV_I(i,j) = min(a,c);
        else
            origin_PV_I(i,j) = a + c - b;
        end
    end
end

图像异或加密:
function [encrypt_I] = Encrypt_Image(origin_I,Image_key)
% 函数说明:对图像origin_I进行bit级异或加密
% 输入:origin_I(原始图像),Image_key(图像加密密钥)
% 输出:encrypt_I(加密图像)
[row,col] = size(origin_I); %计算origin_I的行列值
encrypt_I = origin_I;  %构建存储加密图像的容器
%% 根据密钥生成与origin_I大小相同的随机矩阵
rand('seed',Image_key); %设置种子
E = round(rand(row,col)*255); %随机生成row*col矩阵
%% 根据E对图像origin_I进行bit级加密
for i=1:row
    for j=1:col
        encrypt_I(i,j) = bitxor(origin_I(i,j),E(i,j));
    end
end
end

信息异或加密:
function [Encrypt_D] = Encrypt_Data(D,Data_key)
% 函数说明:对原始秘密信息D进行bit级异或加密
% 输入:D(原始秘密信息),Data_key(数据加密密钥)
% 输出:Encrypt_D(加密的秘密信息)
num_D = length(D); %求嵌入数据D的长度
Encrypt_D = D;  %构建存储加密秘密信息的容器
%% 根据密钥生成与D长度相同的随机0/1序列
rand('seed',Data_key); %设置种子
E = round(rand(1,num_D)*1); %随机生成长度为num_D的0/1序列
%% 根据E对原始秘密信息D进行异或加密
for i=1:num_D  
    Encrypt_D(i) = bitxor(D(i),E(i));
end
end

位图标记:
function [Map_origin_I] = Category_Mark(origin_PV_I,origin_I,ref_x,ref_y)
% 函数说明:对每个像素值进行标记,即生成原始图像的位图
% 输入:origin_PV_I(预测值),origin_I(原始值),ref_x,ref_y(参考像素的行列数)
% 输出:Map_origin_I(像素值的标记分类,即位置图)
[row,col] = size(origin_I); %计算origin_I的行列值
Map_origin_I = origin_I;  %构建存储origin_I标记的容器
for i=1:row
    for j=1:col
        if i<=ref_x || j<=ref_y %前ref_x行、前ref_y列作为参考像素,不标记
            Map_origin_I(i,j) = -1;   %标记为-1是为了与下面产生的t=7时ca=0情况分开,两种情况都不能嵌入数据,但是参考像素不必恢复,非参考像素需要在恢复操作中被遍历
        else
            x = origin_I(i,j); %原始值
            pv = origin_PV_I(i,j); %预测值
            for t=7:-1:0  
                if floor(x/(2^t)) ~= floor(pv/(2^t))  % floor(x) 函数向下取整
                    ca = 8-t-1; %用来记录像素值的标记类别  -1是因为不同的那一位也可以嵌入数据
                    break;
                else
                    ca = 8; 
                end
            end
            Map_origin_I(i,j) = ca; %表示有ca位MSB相同,即可以嵌入ca位信息
        end        
    end
end

位图转化为二进制数组:
function [Map_Bin] = Map_Binary(Map_origin_I,Code)
% 函数说明:将位图Map_origin_I转换成二进制数组Map
% 输入:Map_origin_I(原始图像的位置图),Code(映射关系)
% 输出:Map_Bin(原始图像位置图的二进制数组)
[row,col] = size(Map_origin_I); %计算Map_origin_II的行列值
Map_Bin = zeros();
t = 0; %计数,二进制数组的长度
for i=1:row 
    for j=1:col
        if Map_origin_I(i,j) == -1 %标为-1的点是作为参考像素,不统计
            continue;
        end
        for k=1:9
            if Map_origin_I(i,j) == Code(k,1)
                value = Code(k,2);
                break;
            end
        end
        if value == 0
            Map_Bin(t+1) = 0;
            Map_Bin(t+2) = 0;
            t = t+2;
        elseif value == 1
            Map_Bin(t+1) = 0;
            Map_Bin(t+2) = 1;
            t = t+2;
        else
            add = ceil(log2(value+1)); %表示标记编码的长度
            Map_Bin(t+1:t+add) = dec2bin(value)-'0'; %将value转换成二进制数组
            t = t + add;
        end 
    end
end

哈夫曼编码:
function [Code,Code_Bin] = Huffman_Code(num_Map_origin_I)
% 函数说明:用变长编码(多位0/1编码)表示像素值的标记类别
% 输入:num_Map_origin_I(像素值标记类别的统计情况)
% 输出:Code(映射关系),Code_Bin(Code的二进制表示)
% 备注:用{00,01,100,101,1100,1101,1110,11110,11111}这9中编码来表示9种标记类别
% 规则:标记类别中像素的个数越多,则用来表示其类别的编码长度越短
% {00,01,100,101,1100,1101,1110,11110,11111}→{0,1,4,5,12,13,14,30,31}
%% 求其映射编码关系
%Code = [-1,0;-1,1;-1,4;-1,5;-1,12;-1,13;-1,14;-1,30;-1,31]; 
%此处的-1仅有一个标记作用,也可以修改为其他第二列未出现的值,最终都会被替换成映射关系
Code = [-2,0;-2,1;-2,4;-2,5;-2,12;-2,13;-2,14;-2,30;-2,31]; 
for i=1:9
    drder=1;
    for j=1:9
        if num_Map_origin_I(i,2) < num_Map_origin_I(j,2)
            drder = drder + 1; %排序寻找最小值
        end
    end
    while Code(drder) ~= -2 %防止两种标记类别中像素的个数相等
        drder = drder + 1; 
    end
    Code(drder,1) = num_Map_origin_I(i,1); %第一列从小到大排列了出现的标记顺序
end
%% 将Map映射关系用二进制比特流表示
Code_Bin = zeros();
t = 0; %计数
for i=0:8
    for j=1:9
        if Code(j,1) == i
            value = Code(j,2);
        end
    end
    if value == 0
        Code_Bin(t+1) = 0;
        Code_Bin(t+2) = 0;
        t = t+2;
    elseif value == 1
        Code_Bin(t+1) = 0;
        Code_Bin(t+2) = 1;
        t = t+2;
    else 
        add = ceil(log2(value+1)); %表示标记编码的长度
        Code_Bin(t+1:t+add) = dec2bin(value)-'0'; %将value转换成二进制数组
        t = t + add;
    end     
end
end

哈夫曼解码:
function [value,this_end] = Huffman_DeCode(Binary,last_end)
% 求二进制比特流Binary中下一个Huffman编码转换成的整数值
% 输入:Binary(二进制映射序列),last_end(上一个映射结束的位置)
% 输出:value(十进制整数值)→{0,1,4,5,12,13,14,30,31},end(本次结束的位置)
len = length(Binary);
i = last_end+1;
t = 0; %计数
if i <= len
    if i+1<=len && Binary(i)==0 %→0
        t = t + 1;
        if Binary(i+1) == 0 %→00表示0
            t = t + 1;
            value = 0;
        elseif Binary(i+1) == 1 %→01表示1
            t = t + 1;
            value = 1;
        end
    else  % Binary(i)==1
        if i+2<=len && Binary(i+1)==0 %→10
            t = t + 2;
            if Binary(i+2) == 0  %→100表示4
                t = t + 1;
                value = 4;
            elseif Binary(i+2) == 1 %→101表示5
                t = t + 1;
                value = 5;
            end
        else % Binary(i+1)==1
            if i+3<=len && Binary(i+2)==0  %→110
                t = t + 3;
                if Binary(i+3) == 0  %→1100表示12
                    t = t + 1;
                    value = 12;
                elseif Binary(i+3) == 1  %→1101表示13
                    t = t + 1;
                    value = 13;
                end
            else % Binary(i+2)==1
                if i+3 <= len
                    t = t + 3;
                    if Binary(i+3) == 0  %→1110表示14
                        t = t + 1;
                        value = 14;
                    elseif i+4<=len && Binary(i+3)==1  %→1111
                        t = t + 1;
                        if Binary(i+4) == 0  %→11110表示30
                            t = t + 1;
                            value &#
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值