目录

一、问题描述

二、程序设计

1. 程序输入

2. 程序输出

3. 程序功能

三、程序实现原理

四、程序具体实现

1.模块设计

​编辑

2.程序测试与分析

3.代码附录

五、分析总结

一、问题描述

style=”margin-left:0;text-align:justify;”>使用Jsteg和F5隐写算法对图像进行隐写与隐写分析

二、程序设计

1. 程序输入

task.m:标准图像Lena.tiff,供Jsteg算法隐藏的随机数据data1和供F5算法隐藏的随机数据data2;

Jsteg_in.m:隐藏数据data和载体图像矩阵cover;

Jsteg_out.m:载密图像矩阵stego和嵌入率ER;

F5_in.m:隐藏数据data和载体图像矩阵cover;

F5_out.m:载密图像矩阵stego和嵌入率ER;

2. 程序输出

task.m:由标准图像Lena.tiff生成的压缩图像,由Jsteg算法生成的伪装图像setgo1和由F5算法生成的伪装图像stego2;

Jsteg_in.m:载密图像矩阵stego;

Jsteg_out.m:提取出的秘密数据data;

F5_in.m:载密图像矩阵stego;

F5_out.m:提取出的秘密数据data;

3. 程序功能

task.m:生成不同质量因子qf下的压缩图像,调用Jsteg_in.m、Jsteg_out.m、F5_in.m、F5_out.m对标准图像Lena.tiff使用Jsteg算法和F5算法进行秘密信息嵌入和秘密信息提取;

Jsteg_in.m:使用Jsteg算法将秘密数据data嵌入载体图像cover中;

Jsteg_out.m:使用Jsteg算法从载密图像矩阵stego中提取出秘密数据data;

F5_in.m:使用F5算法将秘密数据data嵌入载体图像cover中;

F5_out.m:使用F5算法从载密图像矩阵stego中提取出秘密数据data。

三、程序实现原理

一、Jsteg算法

1. Jsteg是一种DCT系数的LSB嵌入方法;

2. Jsteg隐写方法将秘密信息嵌入在量化后的DCT系数的LSB上,原始值为−1、0、+1的DCT系数例外;

3. 提取时,也只是将含密图象中不等于−1、0、+1的量化DCT系数的LSB取出

优点:嵌入容量大、简单易实现

缺点:可以被卡方分析方法分析出来


如上图,可利用位置为(1,1)和(2,1)

二、F5算法

1. JPEG图象的DCT系数有下面两个特性:

(1)DCT系数的绝对值越大,其对应的直方图中的值就越小,也就是说出现的频率越低;

(2)随着系数绝对值的升高,其出现次数下降的幅度减小;

(3)不希望秘密信息的嵌入改动这些特性。

2. F5算法的步骤:

(1)进行JPEG压缩,量化DCT系数;

(2)伪随机置乱DCT系数,置乱方法作为密钥;

(3)确定k,并计算n=2k -1;

(4)嵌入数据:实施矩阵编码,修改DCT系数;

(5)逆混洗,产生隐写后的图象。

关键技术:矩阵编码(Hamming LSB)

3. 矩阵编码

(1)LSB隐写方案中嵌入1比特秘密信息有可能修改原数据,也有可能不修改原数据,且概率各为1/2,也就是说每个LSB的修改可以平均嵌入2比特秘密信息;

(2)矩阵编码的目的就是提高嵌入效率,使每个LSB修改可以嵌入更多的秘密比特;

(3)F5的矩阵编码:在2 k−1个原始数据的LSB中,嵌入k比特秘密信息,最多改动1比特;

(4)这种编码也称为Hamming隐写编码,因为嵌入和提取过程中使用了Hamming码的一致校验阵。


4. 矩阵编码在F5中的实现步骤:

(1)取出n个非0的DCT系数,用正奇数和负偶数代表1、负奇数和正偶数代表0,组成一 个向量a;

(2)取出待嵌入的k比特秘密数据,计算是否需要修改a,如果无需修改(r=0),则返回第 一步,继续下一组嵌入;

(3)如果需要修改(r不为0),将要改动的DCT系数绝对值减1,符号不变;需要检验修改 后的DCT系数为0:

如不为0,则返回第一步,继续下一组嵌入

如为0,则以上操作无效,要重新取出n个非0的DCT系数(原来的n个DCT系数中的一个变 为0,所以新取出的DCT系数包含原来的n−1个系数和一个真正新的DCT系数),再次嵌入这k比特秘密数据。

四、程序具体实现

1.模块设计




2.程序测试与分析

(1)局部性测试,只以标准图像Lena.tiff为载体图像

1.调用getJPEG.m函数对标准图像Lena.tiff在不同质量因子QF上进行压缩生成压缩图像。

2.对不同质量因子QF的压缩图像在不同嵌入率ER下分别使用Jsteg算法和F5算法对数据进行嵌入。

3.通过Jsteg_out函数和F5_out函数将由Jsteg算法和F5算法生成的载密图像进行数据提取。

4.对比提取数据和嵌入数据的一致性




  1. 对比不同质量因子QF和不同嵌入率ER和不同嵌入算法产生的载密图像与载体压缩图像的PSNR值



(2)一般性测试,以整个标准图像库里的图像作为载体图像【2.tiff~10.tiff】

由于测试范围变大,此处测试过程中存在一部分提取数据与嵌入数据不一致的情况,但其不一致的数目较少,猜测是由于在量化过程中取整操作带来的误差。




由于使用了整个标准库图像作为载体图像,因此生成的PSNR值过多不方便使用图像的方式进行展示,因此在全局性测试中未展示出来。

3.代码附录

task.m

%% 初始环境
clc
clear
close all
p=zeros(9,15,2);            %存储psnr值
k1=1;
cover=imread('Lena.tiff');
[h,w]=size(cover);
max_len=(h/8)*(w/8);
ER_max=max_len/(h*w);       %只能保证每8*8的方块中最少存在一个非零值,现在最大嵌入率
 
%% 生成Lena图像的压缩图像,质量因子qf取10:10:90
filename='Lena';
for QF=10:5:100
    ImgName=getJPEG(filename,QF);  %返回生成jpg文件名称
    %% 读取生成的压缩图像的数据
    cover=imread(ImgName);
    [h,w]=size(cover);
    k2=1;
    disp("质量因子QF=");QF
    for ER=0.001:0.001:ER_max
        disp("嵌入率ER=");ER
        data_len=floor(ER*(h*w));                       %总嵌入长度为data_len比特
        data1=round(rand(data_len,1));                  %随机数为2进制数,即一个数占1bit,总嵌入长度为data_len比特
        data2=round(rand(floor(data_len/3),1)*7);       %随机数为8进制数,即一个数占3bit,总嵌入长度为data_len比特
        %     figure();
        %     subplot(1,3,1);
        %     title("原始图像")
        %     imshow(cover);
        %% Jsteg隐写
        stego1 = JPEG_in(cover,data1);
        %     subplot(1,3,2);
        %     imshow(stego1);
        
        %% Jsteg提取并将嵌入数据与提取数据进行对比
        extract1=JPEG_out(stego1,ER);
        if isequal(data1,extract1)
            disp("jpeg嵌入的数据与提取的数据完全一致");
        end
 
        %% F5隐写
        stego2 = F5_in(cover,data2);
        %     subplot(1,3,3);
        %     imshow(stego2);
 
        %% F5提取并将嵌入数据与提取数据进行对比
        extract2=F5_out(stego2,ER);
        if isequal(data2,extract2)
            disp("F5嵌入的数据与提取的数据完全一致");
        end
 
        %% 对比两种算法嵌入后图像的视觉效果(PSNR值)
        p(k1,k2,1)=psnr(cover,stego1);      %Jsteg算法
        p(k1,k2,2)=psnr(cover,stego2);      %F5算法
        k2=k2+1;
    end
    k1=k1+1;
end
%% 画图展示
color=["r-","g-","b-","c-","m-","y-","k-","r+","g+"];
figure();
plot(0.001:0.001:ER_max,p(1,:,1),color(1));
for i=2:9
    hold on;
    plot(0.001:0.001:ER_max,p(i,:,1),color(i));
end
legend(["QF=10","QF=20","QF=30","QF=40","QF=50","QF=60","QF=70","QF=80","QF=90"]);
xlabel("ER");
ylabel("PSNR");
title("Jsteg算法");
 
figure();
plot(0.001:0.001:ER_max,p(1,:,2),color(1));
for i=2:9
    hold on;
    plot(0.001:0.001:ER_max,p(i,:,2),color(i));
end
legend(["QF=10","QF=20","QF=30","QF=40","QF=50","QF=60","QF=70","QF=80","QF=90"]);
xlabel("ER");
ylabel("PSNR");
title("F5算法");


task_su.m

%% 初始环境
clc
clear
close all
 
%% 以整个标准库图像作为载体图像
for k=2:10
    filename=[num2str(k),'/',num2str(k)];
    cover=imread([filename,'.tiff']);
    [h,w]=size(cover);
    max_len=(h/8)*(w/8);
    ER_max=max_len/(h*w);       %只能保证每8*8的方块中最少存在一个非零值,现在最大嵌入率
    for QF=10:5:100
        ImgName=getJPEG(filename,QF);  %返回生成jpg文件名称
        %% 读取生成的压缩图像的数据
        cover=imread(ImgName);
        [h,w]=size(cover);
        k2=1;
        disp("质量因子QF=");QF
        for ER=0.001:0.001:ER_max
            disp("嵌入率ER=");ER
            data_len=floor(ER*(h*w));                       %总嵌入长度为data_len比特
            data1=round(rand(data_len,1));                  %随机数为2进制数,即一个数占1bit,总嵌入长度为data_len比特
            data2=round(rand(floor(data_len/3),1)*7);       %随机数为8进制数,即一个数占3bit,总嵌入长度为data_len比特
            %     figure();
            %     subplot(1,3,1);
            %     title("原始图像")
            %     imshow(cover);
            %% Jsteg隐写
            stego1 = JPEG_in(cover,data1);
            %     subplot(1,3,2);
            %     imshow(stego1);
 
            %% Jsteg提取并将嵌入数据与提取数据进行对比
            extract1=JPEG_out(stego1,ER);
            extract1=uint8(extract1);
            data1=uint8(data1);
            if isequal(data1,extract1)
                disp("jpeg嵌入的数据与提取的数据完全一致");
            else
                disp("jpeg嵌入的数据与提取的数据不完全一致");
                [sit,~]=find((data1==extract1)==0)
            end
 
            %% F5隐写
            stego2 = F5_in(cover,data2);
            %     subplot(1,3,3);
            %     imshow(stego2);
 
            %% F5提取并将嵌入数据与提取数据进行对比
            extract2=F5_out(stego2,ER);
            extract2=uint8(extract2);
            data2=uint8(data2);
            if isequal(data2,extract2)
                disp("F5嵌入的数据与提取的数据完全一致");
            else
                disp("F5嵌入的数据与提取的数据不完全一致");
                [sit,~]=find((data2==extract2)==0)
            end
        end
    end
end

psnr.m

function psnrvalue=psnr(origin,test)
%得到图像origin和test 的PSNR
I1=double(origin);
I2=double(test);
E=I1-I2;
MSE=mean2(E.*E);
if MSE==0
    psnrvalue=-1;
else
    psnrvalue=10*log10(255*255/MSE);
end


Jsteg_in.m

function stego = Jsteg_in(cover,data)
% 使用jpeg进行信息藏入
%% 标准量化表
Q=[16 11 10 16 24 40 51 61
    12 12 14 19 26 58 60 55
    14 13 16 24 40 57 69 56
    14 17 22 29 51 87 80 62
    18 22 37 56 68 109 103 77
    24 35 55 64 81 104 113 92
    49 64 78 87 103 121 120 101
    72 92 95 98 112 100 103 99];
%% 初始化,DCT转换和量化
[h,w]=size(cover);
data_len=numel(data);
D=zeros(h,w);       %零时存储矩阵
for i=1:h/8
    for j=1:w/8
        D(8*(i-1)+1:8*i,8*(j-1)+1:8*j)=dct2(cover(8*(i-1)+1:8*i,8*(j-1)+1:8*j));
        D(8*(i-1)+1:8*i,8*(j-1)+1:8*j)=round(D(8*(i-1)+1:8*i,8*(j-1)+1:8*j)./Q);
    end
end
 
%% LSB嵌入
stego=D;
num=1;      %表示data的嵌入进度
for i=1:h
    for j=1:w
        if(abs(D(i,j))>1)
            if(D(i,j)>1)
                stego(i,j)=bitset(D(i,j),1,data(num));
            else
                stego(i,j)=bitset(-D(i,j),1,data(num));
                stego(i,j)=-stego(i,j);
            end
            num=num+1;
        end
        if(num>data_len)
            break;
        end
    end
    if(num>data_len)
        break;
    end
end
%% DCT转换,转换成伪装图像
for i=1:h/8
    for j=1:w/8
        stego(8*(i-1)+1:8*i,8*(j-1)+1:8*j)=stego(8*(i-1)+1:8*i,8*(j-1)+1:8*j).*Q;
        stego(8*(i-1)+1:8*i,8*(j-1)+1:8*j)=idct2(stego(8*(i-1)+1:8*i,8*(j-1)+1:8*j));
    end
end
stego=uint8(stego);

Jsteg_out.m 

function stego = Jsteg_in(cover,data)
% 使用jpeg进行信息藏入
%% 标准量化表
Q=[16 11 10 16 24 40 51 61
    12 12 14 19 26 58 60 55
    14 13 16 24 40 57 69 56
    14 17 22 29 51 87 80 62
    18 22 37 56 68 109 103 77
    24 35 55 64 81 104 113 92
    49 64 78 87 103 121 120 101
    72 92 95 98 112 100 103 99];
%% 初始化,DCT转换和量化
[h,w]=size(cover);
data_len=numel(data);
D=zeros(h,w);       %零时存储矩阵
for i=1:h/8
    for j=1:w/8
        D(8*(i-1)+1:8*i,8*(j-1)+1:8*j)=dct2(cover(8*(i-1)+1:8*i,8*(j-1)+1:8*j));
        D(8*(i-1)+1:8*i,8*(j-1)+1:8*j)=round(D(8*(i-1)+1:8*i,8*(j-1)+1:8*j)./Q);
    end
end
 
%% LSB嵌入
stego=D;
num=1;      %表示data的嵌入进度
for i=1:h
    for j=1:w
        if(abs(D(i,j))>1)
            if(D(i,j)>1)
                stego(i,j)=bitset(D(i,j),1,data(num));
            else
                stego(i,j)=bitset(-D(i,j),1,data(num));
                stego(i,j)=-stego(i,j);
            end
            num=num+1;
        end
        if(num>data_len)
            break;
        end
    end
    if(num>data_len)
        break;
    end
end
%% DCT转换,转换成伪装图像
for i=1:h/8
    for j=1:w/8
        stego(8*(i-1)+1:8*i,8*(j-1)+1:8*j)=stego(8*(i-1)+1:8*i,8*(j-1)+1:8*j).*Q;
        stego(8*(i-1)+1:8*i,8*(j-1)+1:8*j)=idct2(stego(8*(i-1)+1:8*i,8*(j-1)+1:8*j));
    end
end
stego=uint8(stego);

 F5_in.m

function stego = F5_in(cover,data)
% 使用F5进行信息藏入
%% 标准量化表
Q=[16 11 10 16 24 40 51 61
    12 12 14 19 26 58 60 55
    14 13 16 24 40 57 69 56
    14 17 22 29 51 87 80 62
    18 22 37 56 68 109 103 77
    24 35 55 64 81 104 113 92
    49 64 78 87 103 121 120 101
    72 92 95 98 112 100 103 99];
M=[0 0 0 1 1 1 1
    0 1 1 0 0 1 1
    1 0 1 0 1 0 1];   %校验矩阵
data_len=numel(data);
%% 初始化,DCT转换和量化
[h,w]=size(cover);
D=zeros(h,w);           %零时存储矩阵
for i=1:h/8
    for j=1:w/8
        D(8*(i-1)+1:8*i,8*(j-1)+1:8*j)=dct2(cover(8*(i-1)+1:8*i,8*(j-1)+1:8*j));
        D(8*(i-1)+1:8*i,8*(j-1)+1:8*j)=round(D(8*(i-1)+1:8*i,8*(j-1)+1:8*j)./Q);
    end
end
%% 嵌入data(k)
stego=D;
num=1;          %记录目前已经嵌入的data数量
a=zeros(7,1);
k=1;            %记录当前7个数值中已取数量
sit=zeros(7,2); %记录当前7个数在stego中的位置
for i=1:h
    for j=1:w
        if (D(i,j)>0 && mod(D(i,j),2)==1)||(D(i,j)<0 && mod(D(i,j),2)==0)        %正奇数或负偶数为1
            a(k)=1;
            sit(k,1)=i;
            sit(k,2)=j;
            k=k+1;
        elseif (D(i,j)<0 && mod(D(i,j),2)==1)||(D(i,j)>0 && mod(D(i,j),2)==0)    %负奇数或正偶数为0
            a(k)=0;
            sit(k,1)=i;
            sit(k,2)=j;
            k=k+1;
        end
        if(k>7)
            data_bit=[bitget(data(num),3),bitget(data(num),2),bitget(data(num),1)]';  %8进制转换为2进制
            temp=M*a;
            temp=mod(temp,2);
            n=bitxor(data_bit,temp);      %异或
            n=n(1)*4+n(2)*2+n(3);
            %% 修改第n位的DCT值
            if n>0      %需要修改,否则不需要修改
                if D(sit(n,1),sit(n,2))<0
                    stego(sit(n,1),sit(n,2))=D(sit(n,1),sit(n,2))+1;
                elseif D(sit(n,1),sit(n,2))>0
                    stego(sit(n,1),sit(n,2))=D(sit(n,1),sit(n,2))-1;
                end
                %% 检查修改过后的DCT值是否为0,若为0则重新选择1位数据作为载体信号
                if stego(sit(n,1),sit(n,2))==0
                    k=k-1;
                    sit(n:k-1,:)=sit(n+1:k,:);
                    a(n:k-1)=a(n+1:k);
                    continue;
                end
            end
            num=num+1;
            k=1;
        end
        if(num>data_len)
            break;
        end
    end
    if(num>data_len)
        break;
    end
end
%% DCT转换,转换成伪装图像
for i=1:h/8
    for j=1:w/8
        stego(8*(i-1)+1:8*i,8*(j-1)+1:8*j)=stego(8*(i-1)+1:8*i,8*(j-1)+1:8*j).*Q;
        stego(8*(i-1)+1:8*i,8*(j-1)+1:8*j)=idct2(stego(8*(i-1)+1:8*i,8*(j-1)+1:8*j));
    end
end
stego=uint8(stego);

  F5_out.m

function extract = F5_out(stego,ER)
%% 初始化
Q=[16 11 10 16 24 40 51 61
    12 12 14 19 26 58 60 55
    14 13 16 24 40 57 69 56
    14 17 22 29 51 87 80 62
    18 22 37 56 68 109 103 77
    24 35 55 64 81 104 113 92
    49 64 78 87 103 121 120 101
    72 92 95 98 112 100 103 99]; %标准量化表
[h,w]=size(stego);
M=[0 0 0 1 1 1 1
    0 1 1 0 0 1 1
    1 0 1 0 1 0 1];     %校验矩阵
data_len=floor(h*w*ER/3);
D=zeros(h,w);           %零时存储矩阵
%% DCT转换和量化
for i=1:h/8
    for j=1:w/8
        D(8*(i-1)+1:8*i,8*(j-1)+1:8*j)=dct2(stego(8*(i-1)+1:8*i,8*(j-1)+1:8*j));
        D(8*(i-1)+1:8*i,8*(j-1)+1:8*j)=round(D(8*(i-1)+1:8*i,8*(j-1)+1:8*j)./Q);
    end
end
%% 数据提取
num=1;          %记录目前已经嵌入的data数量
a=zeros(7,1);
k=1;            %记录当前7个数值中已取数量
for i=1:h
    for j=1:w
        if (D(i,j)>0 && mod(D(i,j),2)==1)||(D(i,j)<0 && mod(D(i,j),2)==0)        %正奇数或负偶数为1
            a(k)=1;
            k=k+1;
        elseif (D(i,j)<0 && mod(D(i,j),2)==1)||(D(i,j)>0 && mod(D(i,j),2)==0)    %负奇数或正偶数为0
            a(k)=0;
            k=k+1;
        end
        if k>7  %表示集满7个数据
            temp=M*a;
            temp=mod(temp,2);
            extract(num,1)=temp(1)*4+temp(2)*2+temp(3);
            num=num+1;
            k=1;
        end
        if num>data_len
            break;
        end
    end
    if num>data_len
        break;
    end
end

 getJPEG.m

function [ImgName] = getJPEG(filename,qf)
%输入空域图像对应的文件名和质量因子,输出对应质量因子的JPEG图像,返回生成jpg文件名称
ImgStr=filename;
ImgName=[ImgStr,'_',num2str(qf),'.jpg'];
ImgData=imread([ImgStr,'.tiff']);
imwrite(ImgData,ImgName,'JPEG','Quality',qf);
end


五、分析总结

  1. 通过task.m主函数(局部性测试)对比不同质量因子QF和不同嵌入率ER和不同嵌入算法产生的载密图像与载体压缩图像的PSNR值可以发现,质量因子QF为10/40/90时进行数据嵌入使得原图像与载密图像的视觉差距最小;

  2. 通过task_su.m主函数(全局性测试)发现,不论是使用Jsteg算法还是F5算法都存在嵌入和提取过程中的细微误差,猜测此项误差是由DCT系数在量化中取整所造成的,而具体不一致的情况随图像的不同也有所不同。