[HUST]多媒体安全实验:图像隐写JSTEG,F4,F5的实现

引言:在变换域进行隐写相比于直接隐写在图像的像素值信息中的方法,具有更好的鲁棒性,也更难从肉眼上被察觉。

本次实验使用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

你可能感兴趣的:(matlab)