前言
实验环境: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原因
:图片的可用块太少,载体的选择也很重要!
提取: