引言:在变换域进行隐写相比于直接隐写在图像的像素值信息中的方法,具有更好的鲁棒性,也更难从肉眼上被察觉。
本次实验使用MATLAB进行编写,由于在此之前我基本没有使用过MATLAB进行变成,因而可能会有很多不雅观的地方,希望各位能多多包容。而且因为本人水平有限,所以还望大家包容指正。
1.JSTEG
相对于直接嵌入灰度而言,选择使用DCT隐写的一大原因有是因为DCT系数表示的是各种图像变化的程度,因而,从理论上而言,一张自然图像的DCT图像应该呈现一种山峰形的变化趋势。以0对称,左右逐渐下降的形状。
JSTEG的隐写方法是直接更改DCT绝对值大于1的系数的最低位,将信息通过这种方式嵌入图像之中。在解析时跳过绝对值小于等于1的DCT系数,便可以直接读出隐写的信息。
这种方法显然足够简单,而缺点是JSTEG由于直接更改了最低位,因而会产生值对现象,打个比方,从理论上来说,1出现的频次应该会比2高上很多,但是如果选择了JSTEG方法的话,就会发现,1和2的出现频次会随着嵌入的信息变多,而越来越接近,因而如果分析者发现了这种值对现象,便不难发现了这些图像被处理过。
原图像DCT系数直方图:
而嵌入之后的图像可以看到明显的值对现象:
不难看出之间的差距。
F4嵌入:
对于F4而言,他将用正奇数和负偶数代表秘密消息1、负奇数和正偶数代表秘密消息0。一旦不一致则将绝对值-1.
这种做法的优点是相较于JSTEG而言,不会发生值对现象(毕竟他相当于全方位的平移)。
F5嵌入:
F5是F4的一种直接改进,对于F5而言,他可以有很多有很多形式,具体而言,他可以用2^(n+1)-1位来表示n个比特信息,对于n=1而言,第一个比特信息是b1=a1⊕a2,b2=a2⊕a3,那么,无论何种情况,你只需要在3个DCT系数中修改一个,即可以表示两个字符,这样子的好处是大大增加了DCT嵌入效率,坏处是对于同样要嵌入2个比特信息,正常只需要2个DCT系数,而在这里则需要3个,这就是所谓的有得必有失吧。
JSTEG代码实现:
首先去这个网站下载:http://dde.binghamton.edu/download/nsf5simulator/
将该文件拖入你的工作目录中即可正常运行(也就是当前脚本的文件夹)
你需要一个原始的图像,并且将它命名为cover.jpg(当然你也可以跟随代码进行改动)
你修改后的图像是stego.jpg
代码实现:
%%% setup
COVER = 'cover.jpg'; % cover image (grayscale JPEG image)
STEGO = 'stego.jpg'; % resulting stego image that will be created
SEED=99;
myInformation='Hello,Matlab'
msgBin = de2bi(int8(myInformation),8,'left-msb');
len = size(msgBin,1).*size(msgBin,2);
myInfo = reshape(double(msgBin).',len,1).';
tic;
[nzAC] = nsf5_simulation(COVER,STEGO,myInfo);
T = toc;
fprintf('-----\n');
fprintf('nsF5 simulation finished\n');
fprintf('cover image: %s\n',COVER);
fprintf('stego image: %s\n',STEGO);
fprintf('PRNG seed: %i\n',SEED);
%fprintf('relative payload: %.4f bpac\n',ALPHA);
fprintf('number of nzACs in cover: %i\n',nzAC);
fprintf('elapsed time: %.4f seconds\n',T);
function [AC]=nsf5_simulation(COVER,STEGO,message)
try
jobj=jpeg_read(COVER);
DCT=jobj.coef_arrays{1}
catch
error('ERROR (problem with the cover image)');
end
AC=numel(DCT)-numel(DCT(1:8:end,1:8:end));%计算AC系数数量,每个DCT分块是8*8的
if(length(message)>AC)
error('ERROR (too long message)');
end
%打乱Changeable的排列顺序
idD=1;
len=length(message);
for id=1:len
while(abs(DCT(idD))<=1)
message(id);
DCT(idD)=0;
idD=idD+1;
if(idD>=AC)
break;
end
end
if(DCT(idD)<0)
message(id)
DCT(idD)
if(message(id)==mod(DCT(idD),2))
if(mod(DCT(idD),2)==1)
DCT(idD)=DCT(idD)-sign(DCT(idD));
else
DCT(idD)=DCT(idD)+sign(DCT(idD));
end
end
else
message(id)
DCT(idD)
if(message(id)~=mod(DCT(idD),2))
if(mod(DCT(idD),2)==1)
DCT(idD)=DCT(idD)-sign(DCT(idD));
else
DCT(idD)=DCT(idD)+sign(DCT(idD));
end
end
end
idD=idD+1;
end
try
jobj.coef_arrays{1}=DCT;
jobj.optimize_coding=1;
jpeg_write(jobj,STEGO);
catch
error('ERROR (problem with saving the stego image)')
end
end
function res=invH(y)
to_minimize=@(x) (H(x)-y)^2;
res=fminbnd(to_minimize,eps,0.5-eps);
end
function res=H(x)
res=-x*log2(x)-(1-x)*log2(1-x);
end
F4代码实现:
%%% setup
COVER = 'cover.jpg'; % cover image (grayscale JPEG image)
STEGO = 'stego.jpg'; % resulting stego image that will be created
SEED=99;
output=fopen("test.txt","w");
myInformation='SEEYOUINHE'
msgBin = de2bi(int8(myInformation),8,'left-msb');
len = size(msgBin,1).*size(msgBin,2);
myInfo = reshape(double(msgBin).',len,1).';
tic;
[nzAC] = nsf5_simulationF4(COVER,STEGO,myInfo);
T = toc;
fprintf('-----\n');
fprintf('nsF5 simulation finished\n');
fprintf('cover image: %s\n',COVER);
fprintf('stego image: %s\n',STEGO);
fprintf('PRNG seed: %i\n',SEED);
%fprintf('relative payload: %.4f bpac\n',ALPHA);
fprintf('number of nzACs in cover: %i\n',nzAC);
fprintf('elapsed time: %.4f seconds\n',T);
function [AC]=nsf5_simulationF4(COVER,STEGO,message)
output=fopen("test.txt","w");
try
jobj=jpeg_read(COVER);
DCT=jobj.coef_arrays{1}
catch
error('ERROR (problem with the cover image)');
end
AC=numel(DCT)-numel(DCT(1:8:end,1:8:end));%计算AC系数数量,每个DCT分块是8*8的
if(length(message)>AC)
error('ERROR (too long message)');
end
%打乱Changeable的排列顺序
idD=1;
len=length(message)
for id=1:len
while(abs(DCT(idD))<=1)
if(DCT(idD)<0 && message(id)==1)
DCT(idD)=0
elseif(DCT(idD)>0 && message(id)==0)
DCT(idD)=0
elseif(DCT(idD)~=0)
message(id)
DCT(idD)
break
else
end
idD=idD+1;
if(idD>=AC)
break;
end
end
if(DCT(idD)<=-1)
if(message(id)==mod(DCT(idD),2))
fprintf("%d",id);
message(id)
sign(DCT(idD));
DCT(idD)=DCT(idD)+1;
DCT(idD)
idD=idD+1;
continue
end
message(id)
DCT(idD)
fprintf(output,"%d\n",DCT(idD));
idD=idD+1;
continue
end
if(DCT(idD)>=1)
if(message(id)~=mod(DCT(idD),2))
message(id)
sign(DCT(idD));
DCT(idD)=DCT(idD)-1;
DCT(idD)
idD=idD+1;
continue;
end
message(id)
DCT(idD)
fprintf(output,"%d\n",DCT(idD));
idD=idD+1;
continue
end
end
try
jobj.coef_arrays{1}=DCT;
jobj.optimize_coding=1;
jpeg_write(jobj,STEGO);
catch
error('ERROR (problem with saving the stego image)')
end
end
function res=invH(y)
to_minimize=@(x) (H(x)-y)^2;
res=fminbnd(to_minimize,eps,0.5-eps);
end
function res=H(x)
res=-x*log2(x)-(1-x)*log2(1-x);
end
F5代码实现:
%%% setup
COVER = 'cover.jpg'; % cover image (grayscale JPEG image)
STEGO = 'stego.jpg'; % resulting stego image that will be created
SEED=99;
output=fopen("test.txt","w");
myInformation='HELLOWOLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824HELLOWORLDU201911824';
msgBin = de2bi(int8(myInformation),8,'left-msb');
len = size(msgBin,1).*size(msgBin,2);
myInfo = reshape(double(msgBin).',len,1).';
Extend=2-mod(len,2)
for extend=1:Extend
myInfo(len+extend)=0
end
[nzAC] = nsf5_simulationF5(COVER,STEGO,myInfo);
T = toc;
fprintf('-----\n');
fprintf('nsF5 simulation finished\n');
fprintf('cover image: %s\n',COVER);
fprintf('stego image: %s\n',STEGO);
fprintf('PRNG seed: %i\n',SEED);
%fprintf('relative payload: %.4f bpac\n',ALPHA);
fprintf('number of nzACs in cover: %i\n',nzAC);
fprintf('elapsed time: %.4f seconds\n',T);
function [mba1,mba2,mba3]=my_AND(a1,a2,a3,b1,b2)
if(a1>0)
m1=mod(a1,2);
else
ma1=a1*-1+1;
m1=mod(ma1,2);
end
if(a2>0)
m2=mod(a2,2);
else
ma2=a2*-1+1;
m2=mod(ma2,2);
end
if(a3>0)
m3=mod(a3,2);
else
ma3=a3*-1+1;
m3=mod(ma3,2);
end
mb1=mod(m1+m2,2);
mb2=mod(m2+m3,2);
if(mb1~=b1&&mb2==b2)
mba2=a2;
mba3=a3;
mba1=a1-sign(a1);
elseif(mb1==b1&&mb2~=b2)
mba3=a3-sign(a3);
mba1=a1;
mba2=a2;
elseif(mb1~=b1&&mb2~=b2)
mba2=a2-sign(a2);
mba1=a1;
mba3=a3;
else
mba1=a1;
mba2=a2;
mba3=a3;
end
end
function [AC]=nsf5_simulationF5(COVER,STEGO,message)
output=fopen("test.txt","w");
try
jobj=jpeg_read(COVER);
DCT=jobj.coef_arrays{1}
catch
error('ERROR (problem with the cover image)');
end
AC=numel(DCT)-numel(DCT(1:8:end,1:8:end));%计算AC系数数量,每个DCT分块是8*8的
AC=AC/(3/2);
if(length(message)>AC)
error('ERROR (too long message)');
end
idD=1;
len=length(message)
for id=1:2:len
place1=0;
place2=0;
place3=0;
value1=0;
value2=0;
value3=0;
Value1=0;
Value2=0;
Value3=0;
a1=message(id);
a2=message(id+1);
while(abs(DCT(idD))<=0)
DCT(idD)=0;
idD=idD+1;
if(idD>=AC)
break;
end
end
place1=idD;
value1=DCT(idD);
idD=idD+1;
while(abs(DCT(idD))<=0)
DCT(idD)=0;
idD=idD+1;
if(idD>=AC)
break;
end
end
place2=idD;
value2=DCT(idD);
idD=idD+1;
while(abs(DCT(idD))<=0)
DCT(idD)=0;
idD=idD+1;
if(idD>=AC)
break;
end
end
place3=idD;
value3=DCT(idD);
idD=idD+1;
[Value1,Value2,Value3]=my_AND(value1,value2,value3,a1,a2);
while(Value1==0||Value2==0||Value3==0)
if(Value1==0&&Value2~=0&&Value3~=0)
DCT(place1)=0;
value1=value2;
place1=place2;
place2=place3;
value2=value3;
while(abs(DCT(idD))<=0)
DCT(idD)=0;
idD=idD+1;
if(idD>=AC)
break;
end
end
value3=DCT(idD)
place3=idD
idD=idD+1;
[Value1,Value2,Value3]=my_AND(value1,value2,value3,a1,a2);
elseif(Value1~=0&&Value2==0&&Value3~=0)
DCT(place2)=0;
place2=place3;
value2=value3;
while(abs(DCT(idD))<=0)
DCT(idD)=0;
idD=idD+1;
if(idD>=AC)
break;
end
end
value3=DCT(idD);
place3=idD;
idD=idD+1;
[Value1,Value2,Value3]=my_AND(value1,value2,value3,a1,a2);
elseif(Value1~=0&&Value2~=0&&Value3==0)
DCT(place3)=0;
while(abs(DCT(idD))<=0)
DCT(idD)=0;
idD=idD+1;
if(idD>=AC)
break;
end
end
value3=DCT(idD)
place3=idD
idD=idD+1;
[Value1,Value2,Value3]=my_AND(value1,value2,value3,a1,a2);
elseif(Value1==0&&Value2==0&&Value3~=0)
DCT(place1)=0;
DCT(place2)=0;
value1=value3
place1=place3
while(abs(DCT(idD))<=0)
DCT(idD)=0;
idD=idD+1;
if(idD>=AC)
break;
end
end
value2=DCT(idD)
place2=idD
idD=idD+1;
while(abs(DCT(idD))<=0)
DCT(idD)=0;
idD=idD+1;
if(idD>=AC)
break;
end
end
value3=DCT(idD);
place3=idD;
idD=idD+1;
[Value1,Value2,Value3]=my_AND(value1,value2,value3,a1,a2);
elseif(Value1==0&&Value2~=0&&Value3==0)
DCT(place1)=0;
DCT(place3)=0;
value1=value2
place1=place2
while(abs(DCT(idD))<=0)
DCT(idD)=0;
idD=idD+1;
if(idD>=AC)
break;
end
end
value2=DCT(idD)
place2=idD
idD=idD+1;
while(abs(DCT(idD))<=0)
DCT(idD)=0;
idD=idD+1;
if(idD>=AC)
break;
end
end
value3=DCT(idD)
place3=idD
idD=idD+1;
[Value1,Value2,Value3]=my_AND(value1,value2,value3,a1,a2);
elseif(Value1~=0&&Value2==0&&Value3==0)
DCT(place2)=0;
DCT(place3)=0;
while(abs(DCT(idD))<=0)
DCT(idD)=0;
idD=idD+1;
if(idD>=AC)
break;
end
end
value2=DCT(idD)
place2=idD
idD=idD+1;
while(abs(DCT(idD))<=0)
DCT(idD)=0;
idD=idD+1;
if(idD>=AC)
break;
end
end
value3=DCT(idD)
place3=idD
idD=idD+1;
[Value1,Value2,Value3]=my_AND(value1,value2,value3,a1,a2);
else
DCT(place1)=0;
DCT(place2)=0;
DCT(place3)=0;
while(abs(DCT(idD))<=0)
DCT(idD)=0;
idD=idD+1;
if(idD>=AC)
break;
end
end
value1=DCT(idD)
place1=idD
idD=idD+1;
while(abs(DCT(idD))<=0)
DCT(idD)=0;
idD=idD+1;
if(idD>=AC)
break;
end
end
value2=DCT(idD)
place2=idD
idD=idD+1;
while(abs(DCT(idD))<=0)
DCT(idD)=0;
idD=idD+1;
if(idD>=AC)
break;
end
end
value3=DCT(idD)
place3=idD
idD=idD+1;
[Value1,Value2,Value3]=my_AND(value1,value2,value3,a1,a2);
end
end
temp1=message(id);
temp2=message(id+1);
DCT(place1)=Value1;
DCT(place2)=Value2;
DCT(place3)=Value3;
end
try
jobj.coef_arrays{1}=DCT;
jobj.optimize_coding=1;
jpeg_write(jobj,STEGO);
catch
error('ERROR (problem with saving the stego image)')
end
end
function res=invH(y)
to_minimize=@(x) (H(x)-y)^2;
res=fminbnd(to_minimize,eps,0.5-eps);
end
function res=H(x)
res=-x*log2(x)-(1-x)*log2(1-x);
end
JSTEG内容提取:
STEGO='stego.jpg';
output=fopen("secret.txt","w");
SEED=99;
mlen=80;
tic;
messageste=nsf5_extract(STEGO,mlen);
length(messageste)
for j=1:length(messageste)
if(rem(j,8)==0)
fprintf(output,"%d",messageste(j));
%fprintf(output,'\r\n');
continue
end
fprintf(output,"%d",messageste(j));
end
%fscanf(output,'%s',messageste);
T=toc;
fprintf('-------\n');
fprintf('nsF5 extract finished\n');
fprintf('elapsed time:%.4f seconds\n',T);
fclose(output);
function message=nsf5_extract(STEGO,mlen)
try
jobj=jpeg_read(STEGO);
DCT=jobj.coef_arrays{1};
catch
error('ERROR (problem with the STEGO image)');
end
AC=numel(DCT)-numel(DCT(1:8:end,1:8:end));
idD=1;
for id=1:mlen
while(abs(DCT(idD))<=1)
idD=idD+1;
end
if(DCT(idD)>0)
if(mod(DCT(idD),2)==1)
message(1,id)=1;
else
message(1,id)=0;
end
else
if(mod(DCT(idD),2)~=1)
message(1,id)=1;
else
message(1,id)=0;
end
end
idD=idD+1;
end
end
F4内容提取
STEGO='stego.jpg';
output=fopen("secret.txt","w");
SEED=99;
mlen=80;
tic;
messageste=nsf5_extractF4(STEGO,mlen);
length(messageste)
for j=1:length(messageste)
if(rem(j,8)==0)
fprintf(output,"%d",messageste(j));
%fprintf(output,'\r\n');
continue
end
fprintf(output,"%d",messageste(j));
end
%fscanf(output,'%s',messageste);
T=toc;
fprintf('-------\n');
fprintf('nsF5 extract finished\n');
fprintf('elapsed time:%.4f seconds\n',T);
fclose(output);
function message=nsf5_extractF4(STEGO,mlen)
try
jobj=jpeg_read(STEGO);
DCT=jobj.coef_arrays{1};
catch
error('ERROR (problem with the STEGO image)');
end
AC=numel(DCT)-numel(DCT(1:8:end,1:8:end));
idD=1;
for id=1:mlen
while(DCT(idD)==0)
idD=idD+1;
end
DCT(idD)
if(DCT(idD)<0)
if(mod(DCT(idD),2)==1)
message(1,id)=0;
end
if(mod(DCT(idD),2)==0)
message(1,id)=1;
end
end
if(DCT(idD)>0)
if(mod(DCT(idD),2)==1)
message(1,id)=1;
end
if(mod(DCT(idD),2)==0)
message(1,id)=0;
end
end
idD=idD+1;
end
end
F5内容提取
STEGO='stego.jpg';
output=fopen("secret.txt","w");
SEED=99;
mlen=80;
tic;
messageste=nsf5_extractF5(STEGO,mlen);
length(messageste)
for j=1:length(messageste)
if(rem(j,8)==0)
fprintf(output,"%d",messageste(j));
%fprintf(output,'\r\n');
continue
end
fprintf(output,"%d",messageste(j));
end
%fscanf(output,'%s',messageste);
T=toc;
fprintf('-------\n');
fprintf('nsF5 extract finished\n');
fprintf('elapsed time:%.4f seconds\n',T);
fclose(output);
function message=nsf5_extractF5(STEGO,mlen)
mlen=mlen+3-mod(mlen,3);
try
jobj=jpeg_read(STEGO);
DCT=jobj.coef_arrays{1};
catch
error('ERROR (problem with the STEGO image)');
end
AC=numel(DCT)-numel(DCT(1:8:end,1:8:end));
idD=1;
for id=1:2:mlen
while(DCT(idD)==0)
idD=idD+1;
end
value1=DCT(idD)
idD=idD+1;
while(DCT(idD)==0)
idD=idD+1;
end
value2=DCT(idD)
idD=idD+1;
while(DCT(idD)==0)
idD=idD+1;
end
value3=DCT(idD)
idD=idD+1;
if(value1>0)
m1=mod(value1,2);
else
m1=mod(value1+1,2);
end
if(value2>0)
m2=mod(value2,2);
else
m2=mod(value2+1,2);
end
if(value3>0)
m3=mod(value3,2);
else
m3=mod(value3+1,2);
end
message(id)=mod((m1+m2),2);
message(id+1)=mod((m2+m3),2);
end
end