一、问题描述
二、程序设计
1. 程序输入
2. 程序输出
3. 程序功能
三、程序实现原理
四、程序具体实现
1.模块设计
编辑
2.程序测试与分析
3.代码附录
五、分析总结
使用Jsteg和F5隐写算法对图像进行隐写与隐写分析
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;
task.m:由标准图像Lena.tiff生成的压缩图像,由Jsteg算法生成的伪装图像setgo1和由F5算法生成的伪装图像stego2;
Jsteg_in.m:载密图像矩阵stego;
Jsteg_out.m:提取出的秘密数据data;
F5_in.m:载密图像矩阵stego;
F5_out.m:提取出的秘密数据data;
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)局部性测试,只以标准图像Lena.tiff为载体图像
1.调用getJPEG.m函数对标准图像Lena.tiff在不同质量因子QF上进行压缩生成压缩图像。
2.对不同质量因子QF的压缩图像在不同嵌入率ER下分别使用Jsteg算法和F5算法对数据进行嵌入。
3.通过Jsteg_out函数和F5_out函数将由Jsteg算法和F5算法生成的载密图像进行数据提取。
4.对比提取数据和嵌入数据的一致性
5. 对比不同质量因子QF和不同嵌入率ER和不同嵌入算法产生的载密图像与载体压缩图像的PSNR值
图1
图2
(2)一般性测试,以整个标准图像库里的图像作为载体图像【2.tiff~10.tiff】
由于测试范围变大,此处测试过程中存在一部分提取数据与嵌入数据不一致的情况,但其不一致的数目较少,猜测是由于在量化过程中取整操作带来的误差。
由于使用了整个标准库图像作为载体图像,因此生成的PSNR值过多不方便使用图像的方式进行展示,因此在全局性测试中未展示出来。
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 extract = Jsteg_out(stego,ER)
%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];
[h,w]=size(stego);
data_len=floor(ER*h*w);
extract=zeros(data_len,1);
%% 数据提取,DCT转换和量化
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(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的提取进度
for i=1:h
for j=1:w
if(abs(D(i,j))>1) %即不为-1,0,1
extract(num,1)=bitget(abs(D(i,j)),1);
num=num+1;
end
if(num>data_len)
break;
end
end
if(num>data_len)
break;
end
end
extract=logical(extract);
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系数在量化中取整所造成的,而具体不一致的情况随图像的不同也有所不同。