二值图像隐写

前言

实验环境:Windows下,MATLAB,C++/C编译器
Zhao 和 Koch 提出了一个信息隐藏方案, 他们使用一个特定图像区域中黑像素的个数来编码秘密信息。把一个二值图像分成矩形图像区域 Bi, 分别令P0 ( Bi ) ,P1 ( Bi ) 为黑白像素在图像块 Bi 中所占的百分比。

算法思想

算法思想为: 若某块 P1 ( Bi) > 50% , 则嵌入一个 1 , 若 P0 ( Bi) > 50% , 则嵌入一个 0。在嵌入过程中, 为达到希望的像素关系, 需要对一些像素的颜色进行调整。调整的区域是那些与邻近像素有相反颜色的像素。在具有鲜明对比性的二值图像中, 应该对黑白像素的边界进行修改, 所有的这些规则都是为了确保不被引起察觉。为了使整个系统对传输错误和图像修改具有鲁棒性, 必须调整嵌入处理过程。如果在传输过程中一些像素改变了颜色, 例如 P1 ( Bi) 由 50. 6 % 下降到 49. 5% 时, 这种情况就会发生, 从而破坏了嵌入 信息。因此, 要引 入两个阈值 R1 > 50% 和R0 < 50% 以及一个健壮参数 λ, λ是传输过程中能改变颜色的像素百分比。发送者在嵌入处理中确保 P1 ( Bi ) ∈[ R1, R1 + λ] 或 P0 (Bi) ∈[ R0 - λ, R0 ] 。如果为达到目标必须修改太多的像素, 就把这块标识成无效, 其修正方法为:P1 ( Bi) < R0 - 3λ或P1 ( Bi) > R1 + 3λ然后以比特 i 伪随机地选择另一个图像块。在提取过程中, 无效的块被跳过, 有效的块根据 P1 ( Bi) 进行。

信息隐藏函数

主函数

binaryhide.m

%文件名:binaryhide.m
%函数功能:本函数将完成二值图像下的信息隐秘.
%输入格式举例:[result,count]=binaryhide('c:\blenna.jpg','c:\secret.txt','c:\test.jpg',2020,45,55,3)
%参数说明:
%cover为二值载体图像
%msg为发送的秘密消息
%goalfile为隐藏有秘密消息的结果图片的保存路径
%key为隐藏密钥
%R0,R1和lumda为分析参数
%result为隐藏结果
%count为隐藏的信息数
%availabler,availablec存放隐藏块首地址的行,列标
function [result,count,availabler,availablec]=binaryhide(cover,msg,goalfile,key,R0,R1,lumda)
%按位读取秘密信息
frr=fopen(msg,'r');%定义文件指针
[msg,count]=fread(frr,'ubit1');%msg为消息的位表示形式,count为消息的bit数
fclose(frr);
%读取载体图像信息
images=imread(cover);
image=round(double(images)/255);
%确定图像块的首地址
[m,n]=size(image);
m=floor(m/10);
n=floor(n/10);
temp=zeros([m,n]);
[row,col]=hashreplacement(temp,m*n,m,key,n);%将m,n也作为密钥简化输入
for i=1:m*n
    if row(i)~=1
        row(i)=(row(i)-1)*10+1;
    end
    if col(i)~=1
        col(i)=(col(i)-1)*10+1;
    end
end
%随机置乱8*8个点
temp=zeros(8);
[randr,randc]=hashreplacement(temp,64,key,m,n);%将m,n也作为密钥简化输入
%分析可用的图像块
[availabler,availablec,image]=available(msg,count,row,col,m,n,image,R1,R0,lumda,randr,randc);
%信息嵌入
for i=1:count
    p1bi=computep1bi(availabler(i),availablec(i),image);
    if msg(i,1)==1
       if p1bi<R1
           image=editp1bi(availabler(i),availablec(i),image,0,R1-p1bi+1,randr,randc);%使p1(Bi)>R1
       elseif p1bi>R1+lumda
           image=editp1bi(availabler(i),availablec(i),image,1,p1bi-R1-lumda+1,randr,randc);%使p1(Bi)<R1+λ
       else
       end
   end
   if msg(i,1)==0
       if p1bi>R0
           image=editp1bi(availabler(i),availablec(i),image,1,p1bi-R0+1,randr,randc);%使p1(Bi)<R0
       elseif p1bi<R0-lumda
           image=editp1bi(availabler(i),availablec(i),image,0,R0-lumda-p1bi+1,randr,randc);%使p1(Bi)<R1+λ
       else
       end
   end
end
%信息写回保存
image=round(image);%防止边界扩散后的取整复原
result=image;
imwrite(result,goalfile);
subplot(121),imshow(images),title('原始图像为:');
subplot(122),imshow(result),title(['取阈值R0,R1为',int2str(R0),',',int2str(R1),'以及健壮参数λ为',int2str(lumda),'下的信息',int2str(count),'bits隐秘效果']);

计算可用图像块子函数

available,m

%函数功能:分析可用的图像块与秘密信息对应
%msg,count为秘密消息及其数量
%row,col存放的是随机选块后的块首地址的行,列地址值
%m*n为总块数量
%image为载体图像
%R1,R0,lumda为参数
%randr,randc是在8*8范围内随机置乱的行,列标
function [availabler,availablec,image]=available(msg,count,row,col,m,n,image,R1,R0,lumda,randr,randc)
msgquan=1;
unable=0;
difficult=0;
for blockquan=1:m*n
    %计算这一块的p1(Bi)
    p1bi=computep1bi(row(blockquan),col(blockquan),image);
    if p1bi>=R1+3*lumda ||  p1bi<=R0-3*lumda  %情况(1) 
       row(blockquan)=-1;%标记为无用
       col(blockquan)=-1;
       unable=unable+1;
       msgquan=msgquan-1;%该消息还未找到可以隐藏的块 
    %情况(2)
    elseif msg(msgquan,1)==1 && p1bi<=R0
        %调整p1(Bi)变得更小
        %disp([num2str(row(blockquan)),'a', num2str(col(blockquan)),'a', num2str(msgquan)]);
        image=editp1bi(row(blockquan),col(blockquan),image,1,3*lumda,randr,randc);
        row(blockquan)=-1;
        col(blockquan)=-1;
        difficult=difficult+1;
        msgquan=msgquan-1;%该消息还未找到可以隐藏的块    
    elseif msg(msgquan,1)==0 && p1bi>=R1 
       %调整p1(Bi)变得更大
       %disp([num2str(row(blockquan)),'b', num2str(col(blockquan)),'b', num2str(msgquan)]);
       image=editp1bi(row(blockquan),col(blockquan),image,0,3*lumda,randr,randc);
       row(blockquan)=-1;
       col(blockquan)=-1;
       difficult=difficult+1;
       msgquan=msgquan-1;%该消息还未找到可以隐藏的块 
    else    
       row(blockquan)=row(blockquan);
       row(blockquan)=row(blockquan);    
    end           
   msgquan=msgquan+1;
   if msgquan==count+1%消息已经读取完成
       for i=(blockquan+1):m*n
          row(i)=-1;
          col(i)=-1;
      end
  disp(['消息长度:',num2str(msgquan-1),'bits;用到的块数:',num2str(blockquan),';其中不可用块有:',num2str(unable),';另有',num2str(difficult),'块难以调整块已修改为不可用块'])
  break;    
  end
end
%载体分析完但消息还没有读完
if msgquan<=count  
    disp(['消息长度:',num2str(msgquan-1),'bits;分析过的块数:',num2str(blockquan),';其中不可用块有:',num2str(unable),';另有',num2str(difficult),'块难以调整块已修改为不可用块'])
    disp('请根据以上数据更换载体!');
    error('载体太小!!');
end
%计算可用块的数量
%disp(row)
quan=0; 
for i=1:m*n
    if row(i)~=-1
        quan=quan+1;
    end
end
if quan<count
    error('可用块数量太小!请根据以上数据更换载体!');
end
disp(['可用图像块为:',num2str(quan)]);
%生成可用的块的行标列标并与消息对应
image=round(image);%防止边界扩散后的取整复原
availabler=zeros([1,quan]);
availablec=zeros([1,quan]);
j=1;
for i=1:m*n
   if row(i)~=-1
      availabler(j)=row(i);
      availablec(j)=col(i);
      j=j+1;
  end  
end

计算每一块p1( Bi )子函数

computep1bi.m

%计算P1(Bi)的子函数
%headr为块首行地址
%headc为块首列地址
function p1bi=computep1bi(headr,headc,image)
p1bi=0;
for i=1:10
    for j=1:10
        if  image(headr+i-1,headc+j-1)==1
            p1bi=p1bi+1;
        end
    end
end

修改像素子函数

editp1bi.m

%修改像素的函数
%headr为块首行地址
%headc为块首列地址
%image为图像
%pixel为要修改的像素
%count为修改的数量
%randr,randc是随机置乱后的结果
function image=editp1bi(headr,headc,image,pixel,count,randr,randc)
c=0;
for i=1:64
    if image(headr+randr(i),headc+randc(i))==pixel
       %八连接检测
       if  image(headr+randr(i)-1,headc+randc(i))==~pixel || image(headr+randr(i)+1,headc+randc(i))==~pixel || image(headr+randr(i),headc+randc(i)-1)==~pixel || image(headr+randr(i),headc+randc(i)+1)==~pixel ||  image(headr+randr(i)-1,headc+randc(i)-1)==~pixel || image(headr+randr(i)-1,headc+randc(i)+1)==~pixel|| image(headr+randr(i)+1,headc+randc(i)-1)==~pixel ||  image(headr+randr(i)+1,headc+randc(i)+1)==~pixel 
           image(headr+randr(i),headc+randc(i))=~pixel+0.01;
           c=c+1;
       end
   end
   if c==count
       return
   end
end
if c~=count
   disp('warning!参数选择不当,未能完全按要求修改本块像素,信息可能无法提取,建议重做');
end

应用MD5的随机置换子函数

hashreplacement.m

%函数功能:本函数将利用MD5函数产生随机的无碰撞的像素选择策略.
%输入格式举例:[row,col,j]=hashreplacement(test,60,2020,421,1121)
%参数说明:
%matrix为载体矩阵
%quantity为要嵌入的信息的数量(要选择的像素数量)
%key1,key2,key3为三个密钥
%row为伪随机输出的像素行标
%col为伪随机输出的像素列标
function [row,col,j]=hashreplacement(matrix,quantity,key1,key2,key3)
%记录载体矩阵大小
[X,Y]=size(matrix);
%初始化row和col
row=zeros([1,quantity]);
col=zeros([1,quantity]);
j=zeros([1,quantity]);
for i=1:quantity
    v=round(i/X);
    u=mod(i,X);
    v=mod(v+md52num(md5(u+key1)),Y);
    u=mod(u+md52num(md5(v+key2)),X);
    v=mod(v+md52num(md5(u+key3)),Y);
    j(i)=v*X+u+1;
    col(i)=mod(j(i),Y);
    row(i)=j(i)/Y;
    row(i)=floor(row(i))+1;
    if col(i)==0
       col(i)=Y;
       row(i)=row(i)-1;
    end
end

hashreplacement的子函数:将MD5码转成数字

md52num.m

%将MD5码转成数字
function result=md52num(md5code)
result=0;
for i=1:32
    result=result+table_01(md5code(i))*i;
end

hashreplacement的子函数:将十六进制符转成数字

table_01.m

%查表转换16进制字符为数字
function a=table_01(character)
switch character
    case '0'
        a=0;
    case '1'
        a=1;
    case '2'
        a=2;
    case '3'
        a=3;
    case '4'
        a=4;
    case '5'
        a=5;
    case '6'
        a=6;  
    case '7'
        a=7;
    case '8'
        a=8; 
    case '9'
        a=9;
    case 'a'
        a=10;
    case 'b'
        a=11;
    case 'c'
        a=12;
    case 'd'
        a=13;
    case 'e'
        a=14;
    otherwise
        a=15;
end

hashreplacement的子函数:md5函数

md5.m

function y = md5(M)
    y = GetMD5(M);

GetMD5函数的安装

可从MATLAB的附加功能中下载GetMD5函数使用,需要安装过c++/c编译器(使用命令mex -setup安装)
在这里插入图片描述

信息提取函数

binaryextract.m

%文件名:binaryextract.m
%函数功能:本函数二值空间下的隐秘信息的提取.
%输入格式举例:result=binaryextract('c:\test.jpg','c:\extract.txt',2020,45,55,3,23333)
%参数说明:
%stegocover为隐藏有信息的秘密消息
%goalfile为信息提取后保存的地址
%key为提取密钥
%R0,R1和lumda为分析参数
%count为要提取的信息数
%result为提取的信息
function result=binaryextract(stegocover,goalfile,key,R0,R1,lumda,count)
%读取隐秘载体图像信息,并提取亮度分量.该载体应为16位存储方式的图像,建议使用png格式
stegoimage= imread(stegocover);
stegoimage=round(double(stegoimage)/255);
%确定图像块的首地址
[m,n]=size(stegoimage);
m=floor(m/10);
n=floor(n/10);
temp=zeros([m,n]);
[row,col]=hashreplacement(temp,m*n,m,key,n);%将m,n也作为密钥简化输入
for i=1:m*n
    if row(i)~=1
        row(i)=(row(i)-1)*10+1;
    end
    if col(i)~=1
        col(i)=(col(i)-1)*10+1;
    end
end
%准备提取并回写信息
frr=fopen(goalfile,'a');%定义文件指针
%按隐藏顺序分析图像块
quan=1;
result=zeros([count 1]);
for i=1:m*n
    %计算这一块的p1(Bi)
   p1bi=computep1bi(row(i),col(i),stegoimage);
   if p1bi<R1+3*lumda &&  p1bi>50
       fwrite(frr,1,'ubit1');%回写1
       result(quan,1)=1;     
       quan=quan+1;
   elseif  p1bi>R0-3*lumda &&  p1bi<50   
      fwrite(frr,0,'ubit1');%回写0
       result(quan,1)=0;
       quan=quan+1;
   else
       quan=quan;
   end
   if quan==count+1
      break;
   end
end
fclose(frr);
disp(['已经正确处理',num2str(quan-1),'bits的消息']);

实验结果

隐藏:
在这里插入图片描述
warning原因:图片的可用块太少,载体的选择也很重要!
提取:
在这里插入图片描述
new.jpg

猜你喜欢

转载自blog.csdn.net/Bob_ganxin/article/details/108873575