多媒体安全实验

2022-04-24

多媒体安全实验,总共有4个,分别是像素域的LSB替换算法,JSTEG算法,F4算法,F5算法

实验环境

matlab2022Ra

像素域LSB替换

实现算法

原理特别简单,就是在像素点的最低bit位嵌入信息,这个实验的目的主要还是给学生练手的(熟练使用matlab)

选用的图像是灰度图像(即一个像素占8bits)

picture=imread("your picture");
double_picture=picture;
double_picture=double(double_picture);

wen.txt_id=fopen("secret_message","r");
[msg,len]=fread(wen.txt_id,'ubit1');

[m,n]=size(double_picture);
p=1;

for f2=1:n

    for f1=1:m
        double_picture(f1,f2)=double_picture(f1,f2)-mod(double_picture(f1,f2),2)+msg(p,1);
        if p==len
            break;
        end
        p=p+1
    end
    if p==len
        break;
    end
end


double_picture=uint8(double_picture);
imwrite(double_picture,"save_changed_picture");
subplot(121);imshow(picture);title("未嵌入信息的图像");
subplot(122);imshow(double_picture);title("嵌入信息的图像");
fclose(wen.txt_id);

检验

一开始是说要直接看值对现象的,但是说实话,值对现象这个条件还是太严苛了(之前说值对时, 嵌入的信息是加密后的随机的01序列,并且每个像素值,高7位一致时 最低为 0,1的概率相等,这样值对现象才明显),然而现实是残酷的,我的实验的值对现象非常不明显(甚至没有。

最后换了个办法检验。

我取所有高7位相同的像素点出来,算他们最低位为1和0的差值。

最后累加,这样子效果会比较明显

Picture_old=imread('original_picture');
Picture_new=imread('changed_picture');
%len=80;
Double_Picture_old=Picture_old;
Double_Picture_old=double(Double_Picture_old);
Double_Picture_new=Picture_new;
Double_Picture_new=double(Double_Picture_new);

%count1=zeros(1,256);
%count2=zeros(1,256);
[m,n]=size(Double_Picture_old);

%subplot(121);imhist(Picture_old);
%subplot(122);imhist(Picture_new);

[H1,D1]=imhist(Picture_old);
%subplot(121);imhist(Picture_old);title("test");
a=0;
b=0;
for f1=0:2:255
    bit0=H1(D1 == f1);
    bit1=H1(D1 == f1+1);
    a=abs(bit0-bit1);
    b=b+a;
end

%disp(a);
disp(b);

[H2,D2]=imhist(Picture_new);
b=0;
for f1=0:2:255
    bit0=H2(D2 == f1);
    bit1=H2(D2 == f1+1);
    a=abs(bit0-bit1);
    b=b+a;
end

disp(b);

JSTEG嵌入

说白了就是DCT域的LSB嵌入

算法实现

函数功能

function [AC]=JSTEG_simulation(COVER,STEGO,message)

try
    jobj=jpeg_read(COVER);	%读取cover图片
    PrimeDCT=jobj.coef_arrays{1};%读取DCT系数
    DCT=PrimeDCT;
catch
    error('ERROR (problem with the cover image)');
end

AC=numel(DCT)-numel(DCT(1:8:end,1:8:end));	%计算AC系数个数
messageLen=length(message); %隐秘信息长度

%信息过长
if(messageLen>AC)
    error('ERROR (too long message)');
end

changeable=true(size(DCT));		%生成一个布尔矩阵
changeable(1:8:end,1:8:end)=false;	%DC系数的位置置为false
changeable=find(changeable);	%找出布尔矩阵中为true的位置

idD=1;

for id=1:messageLen
    while(abs(DCT(changeable(idD)))<=1) %嵌入ac系数绝对值大于1的位置
        idD=idD+1;
        if(idD>=AC)
            break;
        end
    end
    DCTInfo=DCT(changeable(idD));  
    if(message(id)~=mod(DCTInfo,2)) %LSB与隐藏信息不同
        fprintf("DCT %x lowest bit %x input message %x\n",DCTInfo,mod(DCTInfo,2),message(id));
        if(DCTInfo<0)
            if(mod(DCTInfo,2))   % 负数 偶加奇减
                DCT(changeable(idD))=DCTInfo+1;
            else
                DCT(changeable(idD))=DCTInfo-1; 
            end
        else
            if(mod(DCTInfo,2))  %正数 偶减奇加
                DCT(changeable(idD))=DCTInfo-1;
            else
                DCT(changeable(idD))=DCTInfo+1;
            end
        end
        fprintf("last DCT:%x\n",DCT(changeable(idD)));
    end
    
    idD=idD+1;
end

%histogram(DCT) %直方图

%%% save the resulting stego image
try
    jobj.coef_arrays{1} = DCT; %写入隐藏信息后的图像
    jobj.optimize_coding = 1;
    jpeg_write(jobj,STEGO);
catch
    error('ERROR (problem with saving the stego image)');
end

%显示未嵌入信息的图像
subplot(2,3,1);
imshow(COVER);
title('未嵌入信息的图像');

%显示未嵌入信息的图像的DCT系数直方图
subplot(2,3,2);
histogram(PrimeDCT);
axis([-10,10,0,2*1e4]);
title('嵌入前的图像DCT系数直方图');

% 灰度直方图
I=imread(COVER);
subplot(2,3,3);
imhist(I);
title('未嵌入信息的灰度直方图');

%显示嵌入信息的图像
subplot(2,3,4);
imshow(STEGO);
title('嵌入信息的图像');

%显示嵌入信息的图像的DCT系数直方图
subplot(2,3,5);
histogram(DCT);
axis([-10,10,0,2*1e4]);
title('嵌入后的图像DCT系数直方图');

% 灰度直方图
I=imread(STEGO);
subplot(2,3,6);
imhist(I);
title('嵌入信息的灰度直方图');

主程序

COVER='cover.jpg';
STEGO='stego.jpg';

message=randi([0 1],1,20000); %生成随机数,作为隐藏信息
save('message','message','-ascii'); %保存秘密信息

tic;
[nzAC]=JSTEG_simulation(COVER,STEGO,message);
T=toc;

fprintf('-----------------------------------\n');
fprintf('JSTEG simulation finished\n');
fprintf('cover image: %s\n',COVER);
fprintf('stego image: %s\n',STEGO);
fprintf('number of nzACs in cover: %i\n',nzAC);
fprintf('elapsed time: %.4f seconds\n',T);
fprintf('-----------------------------------\n');

check

还是值对现象,但是这个实验我的值对现象效果挺明显的(乐

函数

function message=JSTEG_extract(STEGO,messageLen)

try
    jobj=jpeg_read(STEGO);	%读取stego图片
    DCT=jobj.coef_arrays{1};%读取DCT系数
catch
    error('ERROR (problem with the cover image)');
end

changeable=true(size(DCT));		%生成一个布尔矩阵
changeable(1:8:end,1:8:end)=false;	%DC系数的位置置为false
changeable=find(changeable);	%找出布尔矩阵中为true的位置

idD=1;

for id=1:messageLen
    while(abs(DCT(changeable(idD)))<=1)
        idD=idD+1;
    end
    message(1,id)=mod(DCT(changeable(idD)),2); %不管怎么样 1表示数据为1,0表示数据为0
    idD=idD+1;
end

主程序

STEGO='stego.jpg';

messageLen=20000;

tic;
messageHiden=JSTEG_extract(STEGO,messageLen);
T=toc;

save('messageHiden','messageHiden','-ascii'); %保存提取出来的秘密信息

fprintf('-----------------------------------\n');
fprintf('JSTEG extract finished\n');
fprintf('elapsed time: %.4f seconds\n',T);
fprintf('-----------------------------------\n');

F4

算法

函数

function [AC]=F4_simulation(COVER,STEGO,message)

try
    jobj=jpeg_read(COVER);	%读取cover图片
    PrimeDCT=jobj.coef_arrays{1};%读取DCT系数
    DCT=PrimeDCT;
catch
    error('ERROR (problem with the cover image)');
end

AC=numel(DCT)-numel(DCT(1:8:end,1:8:end));	%计算AC系数个数
messageLen=length(message);

%信息过长
if(messageLen>AC)
    error('ERROR (too long message)');
end

changeable=true(size(DCT));		%生成一个布尔矩阵
changeable(1:8:end,1:8:end)=false;	%DC系数的位置置为false
changeable=find(changeable);	%找出布尔矩阵中为true的位置

id=1;
idD=1;

while(id<=messageLen)
    while(DCT(changeable(idD))==0)
        idD=idD+1;
        if(idD>=AC)
            break;
        end
    end      %F4算法中,用正奇数和负偶数代表秘密消息1、负奇 数和正偶数代表秘密消息0
    if(message(id)~=mod(DCT(changeable(idD)),2))	%LSB与隐藏信息不同 -1
        if(DCT(changeable(idD))>0)
            DCT(changeable(idD))=DCT(changeable(idD))-1;
        end
    else	%LSB与隐藏信息相同 +1
        if(DCT(changeable(idD))<0)
            DCT(changeable(idD))=DCT(changeable(idD))+1;
        end
    end
    if(DCT(changeable(idD))==0)
        id=id-1;
    end
    id=id+1;
    idD=idD+1;
end

%%% save the resulting stego image
try
    jobj.coef_arrays{1} = DCT;
    jobj.optimize_coding = 1;
    jpeg_write(jobj,STEGO);
catch
    error('ERROR (problem with saving the stego image)');
end

%显示未嵌入信息的图像
subplot(2,3,1);
imshow(COVER);
title('未嵌入信息的图像');

%显示未嵌入信息的图像的DCT系数直方图
subplot(2,3,2);
histogram(PrimeDCT);
axis([-10,10,0,2*1e4]);
title('嵌入前的图像DCT系数直方图');

% 灰度直方图
I=imread(COVER);
subplot(2,3,3);
imhist(I);
title('未嵌入信息的灰度直方图');

%显示嵌入信息的图像
subplot(2,3,4);
imshow(STEGO);
title('嵌入信息的图像');

%显示嵌入信息的图像的DCT系数直方图
subplot(2,3,5);
histogram(DCT);
axis([-10,10,0,2*1e4]);
title('嵌入后的图像DCT系数直方图');

% 灰度直方图
I=imread(STEGO);
subplot(2,3,6);
imhist(I);
title('嵌入信息的灰度直方图');

主程序

COVER='cover.jpg';
STEGO='stego.jpg';

message=randi([0 1],1,30000); %生成随机数,作为隐藏信息
save('message','message','-ascii'); %保存秘密信息

tic;
[nzAC]=F4_simulation(COVER,STEGO,message);
T=toc;

fprintf('-----------------------------------\n');
fprintf('F4 simulation finished\n');
fprintf('cover image: %s\n',COVER);
fprintf('stego image: %s\n',STEGO);
fprintf('number of nzACs in cover: %i\n',nzAC);
fprintf('elapsed time: %.4f seconds\n',T);
fprintf('-----------------------------------\n');

check

这个算法会导致DCT系数是-1和1的频率大幅下降

函数

function message=F4_extract(STEGO,messageLen)

try
    jobj=jpeg_read(STEGO);	%读取stego图片
    DCT=jobj.coef_arrays{1};%读取DCT系数
catch
    error('ERROR (problem with the cover image)');
end

AC=numel(DCT)-numel(DCT(1:8:end,1:8:end));  %计算AC系数个数

changeable=true(size(DCT));		%生成一个布尔矩阵
changeable(1:8:end,1:8:end)=false;	%DC系数的位置置为false
changeable=find(changeable);	%找出布尔矩阵中为true的位置

idD=1;

for id=1:messageLen
    while(DCT(changeable(idD))==0)
        idD=idD+1;
    end
    if(DCT(changeable(idD))>0)
        message(1,id)=mod(DCT(changeable(idD)),2);
    else
        if(mod(DCT(changeable(idD)),2)==1) % 1表示数据为0  0表示实际数据为1
            message(1,id)=0;
        else
            message(1,id)=1;
        end
    end
    idD=idD+1;
end

主程序

STEGO='stego.jpg';

messageLen=30000;

tic;
messageHiden=F4_extract(STEGO,messageLen);
T=toc;

save('messageHiden','messageHiden','-ascii'); %保存提取出来的秘密信息

fprintf('-----------------------------------\n');
fprintf('F4 extract finished\n');
fprintf('elapsed time: %.4f seconds\n',T);
fprintf('-----------------------------------\n');

F5

算法

COVER = 'cover.jpg';
STEGO='stego.jpg';
SEED=99;

message=randi([0 1],1,10000);
save('message','message','-ascii');
try
    jobj=jpeg_read(COVER);
    PrimeDCT=jobj.coef_arrays{1};
    DCT=PrimeDCT;
catch
    error('Error (problem with the cover image)');
end


AC = numel(DCT)-numel(DCT(1:8:end,1:8:end)); % 得到ac系数的个数

if(length(message)>AC)
    error('ERROR (too ling message)');
end

changeable=true(size(DCT)); %生成一个布尔矩阵
changeable(1:8:end,1:8:end)=false;%DC系数的位置置为false
changeable=find(changeable);%找出布尔矩阵中为true的位置

rand('state',SEED);
changeable=changeable(randperm(AC));

idD=1;
len=length(message);
id=1;
while id<len
    id1=message(id);
    id2=message(id+1);
    %fprintf("%d %d \n",id1,id2);
    count=1;
    choosed_DCT=[1,2,3];
    choosed_count=[1,2,3];
    while count<=3
        while (abs(DCT(changeable(idD)))<=1)
            DCT(changeable(idD))=0; %判断修改后的DCT系数是否变为0,若没有则继续嵌入下一组数 据;若系数变为0,则本次隐藏无效,返回继续嵌入本组数据
            idD=idD+1;              %这里采取取到1就变0的策略,简化流程
            if(idD>=AC)
                break;
            end
        end
        choosed_DCT(count)=DCT(changeable(idD));
        choosed_count(count)=idD;
        count=count+1;
        idD=idD+1;
    end
    %fprintf("%d %d %d\n",choosed_DCT(1),choosed_DCT(2),choosed_DCT(3));
    % id1=choosed_DCT(1)^choosed_DCT(2),id2=choosed_DCT(2)^choosed_DCT(3)
    if (id1~=mod(choosed_DCT(1)^choosed_DCT(2),2)) && (id2~=mod(choosed_DCT(2)^choosed_DCT(2),3))
        choosed_DCT(2)=choosed_DCT(2)-sign(choosed_DCT(2));
    elseif  (id1~=mod(choosed_DCT(1)^choosed_DCT(2),2))
        choosed_DCT(1)=choosed_DCT(1)-sign(choosed_DCT(1));
    else
        choosed_DCT(3)=choosed_DCT(3)-sign(choosed_DCT(3));
    end
    for i=1:(count-1)
        DCT(changeable(choosed_count(i)))=choosed_DCT(i);
    end
    id=id+2;
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

%显示未嵌入信息的图像
subplot(2,3,1);
imshow(COVER);
title('未嵌入信息的图像');

%显示未嵌入信息的图像的DCT系数直方图
subplot(2,3,2);
histogram(PrimeDCT);
axis([-10,10,0,2*1e4]);
title('嵌入前的图像DCT系数直方图');

% 灰度直方图
I=imread(COVER);
subplot(2,3,3);
imhist(I);
title('未嵌入信息的灰度直方图');

%显示嵌入信息的图像
subplot(2,3,4);
imshow(STEGO);
title('嵌入信息的图像');

%显示嵌入信息的图像的DCT系数直方图
subplot(2,3,5);
histogram(DCT);
axis([-10,10,0,2*1e4]);
title('嵌入后的图像DCT系数直方图');

% 灰度直方图
I=imread(STEGO);
subplot(2,3,6);
imhist(I);
title('嵌入信息的灰度直方图');

check

STEGO='stego.jpg';
SEED=99;
mlen=10000;

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));
changeable=true(size(DCT));
changeable(1:8:end,1:8:end)=false;
changeable=find(changeable);
rand('state',SEED);
changeable=changeable(randperm(AC));

idD=1;
while id<mlen
    id1 = id;
    id2 = id+1;
    count=1;
    choosed_DCT=[1,2,3];
    choosed_count=[1,2,3];
    while count<=3   
        while(DCT(changeable(idD))==0)
            idD=idD+1;
        end
        choosed_DCT(count)=DCT(changeable(idD));
        choosed_count(count)=idD;
        idD=idD+1;
        count=count+1;
    end
    % id1=choosed_DCT(1)^choosed_DCT(2),id2=choosed_DCT(2)^choosed_DCT(3)
    message(1,id1)=choosed_DCT(1)^choosed_DCT(2);
    message(1,id2)=choosed_DCT(2)^choosed_DCT(3);
    id=id+2;
end

save('messageHiden','message','-ascii');

致谢

感谢学长的blog!

https://blog.csdn.net/WZZ_2363173126?type=blog