数字图像隐藏图像的两种算法及实现代码

数字图像

  • 二值图像
    也叫单色图像,是将每个像素点存放在一个bit空间(值为0或者1)的图像,也就是说每个像素“非黑即白”,主要用于图像形态学的研究。
  • 8位灰度图像
    每个像素存放在一个byte空间(8位,0-255:0表示最暗色黑色,255表示最亮色白色)。8位灰度图像可以看成是一系列1位“位平面”的叠加。
  • 彩色图像
    最常用的是24位全彩色,图像中的每个像素占据3个byte的空间,分别表示RGB3个通道,总共可以显示16,777,216(256的3次方)种颜色,色彩显示区域相当广泛,但相应的存储空间要求很高。

八位位平面

这里8位灰度图像提到的位平面概念如图:

这里写图片描述

像素值的取值范围是【0,255】,刚好可以用八位二进制来表示,每一位代表一张位平面图。

比如一幅图像在坐标【15,33】像素值是233,那么其二进制表示为11101001,因此
其第七位平面在坐标【15,33】的值为1,
其第六位平面在坐标【15,33】的值为1,
其第五位平面在坐标【15,33】的值为1,
其第四位平面在坐标【15,33】的值为0,
其第三位平面在坐标【15,33】的值为1,
其第二位平面在坐标【15,33】的值为0,
其第一位平面在坐标【15,33】的值为0,
其第零位平面在坐标【15,33】的值为1,

叠加八个位平面的算法也不难想,应该是:

1*2^7 + 1*2^6 + 1*2^5 + 0*2^4 + 1*2^3 + 0*2^2 + 0*2^1 + 1*2^0 = 233

从这也可以看出,第七位平面的权值最大的,为128。

权值

权值越大,该位平面的数值能影响到原像素值的因素也越大。因此,第七位平面所能保存的图像信息也是最大的,依次递减,第零位平面能保存的图像信息最小,其保存的信息只对应于原图像的像素值为奇数或者偶数,而奇数或者偶数只是原像素值加一或者减一这一微不足道的区别罢了。

记住这个结论,因为马上我们就要用到。

像素高低差

当然了,这是从权值的角度理解,其实还有另一种理解方式:

在第七位平面,原像素值中,所有灰度值最高位是0的还是0,最高位是1的还是1,即,所有大于等于128的像素值在第七位平面的值为1,而所有小于128的像素值在第七位平面的值为0。因此图像大范围的像素值高低差还保存着,图像的信息就不会被破坏太多,所以图像轮廓也能得到保持。

而在第一位平面中,比如像素值为233,为奇数,在第一位平面的值为1,而240,为偶数,在第一位平面的值为0,因此明明240比233高,结果在第一位平面中却颠倒过来了,大大破坏了图像信息。

图像隐藏的两种算法

末位置0法

原理

设原图为图A,要隐藏的图为图B。图A的尺寸一定要大于等于图B的尺寸。

刚才也提到了,第零位平面能保存的图像信息最小。如果把图A的第零位平面清空,然后把图B转为二值图像,全部放到图A的第零位平面,那么对现在的图A来说,只是原像素值减一或者加一,人的肉眼是完全看不出变化的。

算法

①、先把图A的灰度值全部初始化为偶数(这样就清空第零位平面了)

②、把图B改为二值图像(其实就是提取图B的第七位平面,将所有大于等于128的像素值置为1,所有小于128的像素值置为0即可)

③、挨个遍历图A的像素值,每个像素值再把图B的像素值加上。

这样就实现在图A中隐藏图B了,然后从图A中取出图B也很简单,直接取图A的第零位图即可。

代码

合并图像:

% main_merge1.m
% 读取原图
Img = imread('原图.png');
[M, N, Z] = size(Img);
Img = double(Img);
ImgR2 = Img(:,:,1);
ImgG2 = Img(:,:,2);
ImgB2 = Img(:,:,3);

% 读取待隐藏的图
Imgmark = imread('待隐藏的图.png');
% 转为灰度图
Imgmark = rgb2gray(Imgmark);
% 转为二值图
Imgmark = im2bw(Imgmark);
figure;imshow(Imgmark,[]);title('待隐藏的图')

for i = 1 : M
    for j = 1 : N
	    % 清空图A第零位平面
        if mod(ImgR2(i,j), 2) == 1
            ImgR2(i,j) = ImgR2(i,j) - 1;
        end
        if mod(ImgG2(i,j), 2) == 1
            ImgG2(i,j) = ImgG2(i,j) - 1;
        end
        if mod(ImgB2(i,j), 2) == 1
            ImgB2(i,j) = ImgB2(i,j) - 1;
        end
        % 加上图B的像素值
        ImgR2(i,j) = ImgR2(i,j) + Imgmark(i, j);
        ImgG2(i,j) = ImgG2(i,j) + Imgmark(i, j);
        ImgB2(i,j) = ImgB2(i,j) + Imgmark(i, j);
    end
end

% 三通道合并
ImgNew = zeros(M, N, Z);
ImgNew(:,:,1) = ImgR2;
ImgNew(:,:,2) = ImgG2;
ImgNew(:,:,3) = ImgB2;

figure;imshow(uint8(ImgNew),[]);title('合并后的RGB图');
imwrite(uint8(ImgNew), '合并后的RGB图.png'); % 保存图片

提取被隐藏的图像:

% main_extract1.m
% 读取合并后的RGB图
Img = imread('合并后的RGB图.png');
[M, N, Z] = size(Img);
Img = double(Img);
ImgR2 = Img(:,:,1);
ImgG2 = Img(:,:,2);
ImgB2 = Img(:,:,3);

Imgmarkextract = zeros(M, N, Z);
ImgmarkextractR = Imgmarkextract(:,:,1);
ImgmarkextractG = Imgmarkextract(:,:,2);
ImgmarkextractB = Imgmarkextract(:,:,3);
for i = 1 : M
    for j = 1 : N
	    % 读取第零位平面
        if mod(ImgR2(i,j), 2) == 1
            ImgmarkextractR(i,j) = 1;
        end
        if mod(ImgG2(i,j), 2) == 1
            ImgmarkextractG(i,j) = 1;
        end
        if mod(ImgB2(i,j), 2) == 1
            ImgmarkextractB(i,j) = 1;
        end
    end
end
Imgmarkextract(:,:,1) = ImgmarkextractR;
Imgmarkextract(:,:,2) = ImgmarkextractG;
Imgmarkextract(:,:,3) = ImgmarkextractB;
figure;imshow(Imgmarkextract,[]);title('提取出的隐藏图');
imwrite(Imgmarkextract, '提取出的隐藏图.png'); % 保存图片

示例

原图:
这里写图片描述

待隐藏的图:
这里写图片描述

合并后的图:
这里写图片描述

提取出的图:
这里写图片描述

可以用StegSolve 软件打开合并后的图像验证(第零位平面整个平面图为隐藏的图像)。

在这里插入图片描述

这是合并后图像的第七位平面图,可以看到还是保存了原图中足够多的信息的。

在这里插入图片描述

这是合并后图像的第零位平面图,可以看到整个位平面图都被替换成了隐藏图像的第七位平面图,因此这种算法的缺点就是隐藏的图像容易被识破。

末位置0的缺陷就是只能隐藏二值图像,下面还有一种可以隐藏灰度图像,也是基于位运算的。

大容量隐藏法

原理

我们刚才提到的末位置0法之所以动第零位平面的刀是因为第零位平面影响原图像很小,肉眼难以察觉,那么现在我们把其他位图也利用进来隐藏图像信息。

只需要的找到一个临界条件,在这个条件下,可以尽最大可能从第零位平面往上多用几个位图来保存要隐藏的图像而又不破坏原图的信息,被人眼看出差别。

注意这本身就是个矛盾的过程,越往上位图对原图的像素影响越大,而要用更多位图隐藏图像势必会破坏原图信息,因此能动用来隐藏的位图一定要与像素值的最高非零位有“一段距离”。

现在“这段距离”我们已经找到了。
我们规定,对于红色通道图,能动用的位平面要与最高非零位有4位的距离;
对于绿色通道图,能动用的位平面要与最高非零位有5位的距离;
对于蓝色通道图,能动用的位平面要与最高非零位有3位的距离;

举个栗子:
比如现在有个像素值是233,也就是11101001,其最高非零位为第八位。
对于红色通道图要有四位的距离,八减四得四,那么我们认为可以在其红色通道图中使用第四位第三位第二位和第一位,也就是可以使用第三、第二、第一、第零位平面来保存图像且不会被肉眼看出明显差别。
以此类推,对于绿色通道图,距离为5,只可以用第零、第一、第二位平面;
对于蓝色通道图,距离为3,可以用第四、第三、第二、第一、第零位平面。

这样就能完美利用到所有像素值的位平面。

由于是按位隐藏的,所以要把待隐藏的图像的全部像素值的所有位数全部分散出,一位一位地保存到原图中去。

而在提取隐藏图像的时候,如果这个像素值最高非零位是第八位,我们就提取其红色通道图的后四位二进制数,绿色通道图的后三位,蓝色通道图的后五位。

每提取8位就合成一个灰度值,这个提取过程要持续到把隐藏的图像的所有像素灰度值全部提取回为止。最后再重新排列成隐藏的图像。

代码

合并图像:

% main_merge2.m
% 各通道肉眼可接受位差
yr = 4;
yg = 5;
yb = 3;

% 读取原图
Img = imread('原图.png');
figure;imshow(Img,[]);title('原图');

% 读取待隐藏的图
Imgmark = imread('待隐藏的图.png');
figure;imshow(Imgmark,[]);title('待隐藏的图');
% 转为灰度图
Imgmark = rgb2gray(Imgmark);
Imgmark = double(Imgmark);
[markm, markn] = size(Imgmark);
% 将灰度图的二维数组转成一列
Imgmarkline = Imgmark(:);

% 这一列再转化为更长的一列,二进制八位表示
Imgmarklinebin = zeros(markm*markn*8,1);
for ii = 1 : markm*markn
    [Imgmarklinebin(8*ii-7), Imgmarklinebin(8*ii-6), Imgmarklinebin(8*ii-5), Imgmarklinebin(8*ii-4), Imgmarklinebin(8*ii-3),...
        Imgmarklinebin(8*ii-2), Imgmarklinebin(8*ii-1), Imgmarklinebin(8*ii)] = Find8bits(Imgmarkline(ii));   
end

%%
% 获得RGB各通道分量图
Img = double(Img);
ImgR = Img(:,:,1);
ImgG = Img(:,:,2);
ImgB = Img(:,:,3);
% 嵌入
% 对于红色通道
embedNumsed = 0; % 已嵌入个数
[M, N, Z] = size(Img);
y = zeros(8, 1);
flag = 0; % 辅助跳出的标志

ImgRline = ImgR(:); % 转换为一列
ImgRlineNew = ImgRline; % 嵌入后
for ii = 1 : M*N
    if flag == 1; % 跳出外层循环
       break;
    end
    
    [y(8), y(7), y(6), y(5), y(4), y(3), y(2), y(1)] = Find8bits(ImgRline(ii));   
    posNzreo = FindNotZero(y(8), y(7), y(6), y(5), y(4), y(3), y(2), y(1));
    embedNums = posNzreo - yr; % 能嵌入的个数
    if  embedNums > 0 %符合嵌入条件
        for jj = 1 : embedNums
            embedNumsed = embedNumsed + 1; % 已嵌入个数
            if embedNumsed > markm*markn*8 % 嵌入完成
               flag = 1; % 设置标识,使外层循环也跳出
               break;
            end
            
            y(jj) = Imgmarklinebin(embedNumsed);% 嵌入
        end
    end
    ImgRlineNew(ii) = bin2dec_trans(y(8), y(7), y(6), y(5), y(4), y(3), y(2), y(1));% 嵌入后的  
end
ImgR2 = reshape(ImgRlineNew, [M, N]);


% 对于G通道
ImgGline = ImgG(:); % 转换为一列
ImgGlineNew = ImgGline; % 嵌入后
for ii = 1 : M*N
    if flag == 1; % 跳出外层循环
       break;
    end
    
    [y(8), y(7), y(6), y(5), y(4), y(3), y(2), y(1)] = Find8bits(ImgGline(ii));   
    posNzreo = FindNotZero(y(8), y(7), y(6), y(5), y(4), y(3), y(2), y(1));
    embedNums = posNzreo-yg; % 能嵌入的个数
    if  embedNums > 0 % 符合嵌入条件
        for jj = 1 : embedNums
            embedNumsed = embedNumsed + 1; % 已嵌入个数
            if embedNumsed > markm*markn*8 % 嵌入完成
               flag = 1; % 设置标识,使外层循环也跳出
               break;
            end
            
            y(jj) = Imgmarklinebin(embedNumsed); % 嵌入 
        end
    end
    ImgGlineNew(ii) = bin2dec_trans(y(8), y(7), y(6), y(5), y(4), y(3), y(2), y(1)); % 嵌入后的  
end
ImgG2 = reshape(ImgGlineNew, [M, N]);

% 对于B通道
ImgBline = ImgB(:); % 转换为一列
ImgBlineNew = ImgBline; % 嵌入后
for ii = 1 : M*N
    if flag == 1; % 跳出外层循环
       break;
    end
    
    [y(8), y(7), y(6), y(5), y(4), y(3), y(2), y(1)] = Find8bits(ImgBline(ii));   
    posNzreo = FindNotZero(y(8), y(7), y(6), y(5), y(4), y(3), y(2), y(1));
    embedNums = posNzreo - yb; % 能嵌入的个数
    if  embedNums > 0 % 符合嵌入条件
        for jj = 1 : embedNums
            embedNumsed = embedNumsed + 1; % 已嵌入个数
            if embedNumsed > markm*markn*8 % 嵌入完成
               flag = 1; % 设置标识,使外层循环也跳出
               break;
            end
            
            y(jj) = Imgmarklinebin(embedNumsed); % 嵌入 
        end
    end
    ImgBlineNew(ii) = bin2dec_trans(y(8), y(7), y(6), y(5), y(4), y(3), y(2), y(1)); % 嵌入后的  
end
ImgB2 = reshape(ImgBlineNew, [M, N]);

ImgNew = zeros(M, N, Z);
ImgNew(:,:,1) = ImgR2;
ImgNew(:,:,2) = ImgG2;
ImgNew(:,:,3) = ImgB2;

figure;imshow(uint8(ImgNew),[]);title('合并后的RGB图');
imwrite(uint8(ImgNew), '合并后的RGB图.png'); % 保存图片

提取被隐藏的图像:

% main_extract2.m
% 各通道肉眼可接受位差
yr = 4;
yg = 5;
yb = 3;

% 读取合并后的RGB图
Img = imread('合并后的RGB图.png');
[M, N, Z] = size(Img);
Img = double(Img);
ImgR2 = Img(:,:,1);
ImgG2 = Img(:,:,2);
ImgB2 = Img(:,:,3);

% 提取嵌入图像
flag = 0;
Imgmark_extractlinebin = zeros(M*N*8, 1);
extractNumsed = 0; % 已提取个数

% R通道
ImgRline2 = ImgR2(:); % 转换为一列
for ii = 1 : M*N
    if flag == 1; % 跳出外层循环
       break;
    end
    
    [y(8), y(7), y(6), y(5), y(4), y(3), y(2), y(1)] = Find8bits(ImgRline2(ii));   
    posNzreo = FindNotZero(y(8), y(7), y(6), y(5), y(4), y(3), y(2), y(1));
    embedNums = posNzreo - yr; % 已嵌入的个数
    if  embedNums > 0 % 符合嵌入条件
        for jj = 1 : embedNums
            
            extractNumsed = extractNumsed + 1; % 已提取个数
            if extractNumsed > M*N*8 % 提取完成
               flag = 1; % 设置标识,使外层循环也跳出
               break;
            end 
            
           Imgmark_extractlinebin(extractNumsed) = y(jj); % 提取
        end  
    end  
end

% G通道
ImgGline2 = ImgG2(:); % 转换为一列
for ii = 1 : M*N
    if flag == 1; % 跳出外层循环
       break;
    end
    
    [y(8), y(7), y(6), y(5), y(4), y(3), y(2), y(1)] = Find8bits(ImgGline2(ii));   
    posNzreo = FindNotZero(y(8), y(7), y(6), y(5), y(4), y(3), y(2), y(1));
    embedNums = posNzreo - yg; % 已嵌入的个数
    if embedNums > 0 % 符合嵌入条件
        for jj = 1:embedNums
            
            extractNumsed = extractNumsed + 1; % 已提取个数
            if extractNumsed > M*N*8 % 提取完成
               flag = 1; % 设置标识,使外层循环也跳出
               break;
            end
            
           Imgmark_extractlinebin(extractNumsed) = y(jj);% 提取
        end
    end
end

%  G通道
ImgBline2 = ImgB2(:); % 转换为一列
for ii = 1:M*N
    if flag == 1; % 跳出外层循环
       break;
    end
    
    [y(8), y(7), y(6), y(5), y(4), y(3), y(2), y(1)] = Find8bits(ImgBline2(ii));   
    posNzreo = FindNotZero(y(8), y(7), y(6), y(5), y(4), y(3), y(2), y(1));
    embedNums = posNzreo - yb; % 已嵌入的个数
    if embedNums > 0 % 符合嵌入条件
        for jj = 1 : embedNums
            
            extractNumsed = extractNumsed + 1; % 已提取个数
            if extractNumsed > M*N*8 % 提取完成
               flag = 1; % 设置标识,使外层循环也跳出
               break;
            end
            
           Imgmark_extractlinebin(extractNumsed) = y(jj); % 提取
        end
    end
end

% 二进制转十进制
Imgmarklinedec = zeros(M*N, 1); % 转化为十进制
for ii = 1 : M*N
    Imgmarklinedec(ii) = bin2dec_trans(Imgmark_extractlinebin(8*ii-7), Imgmark_extractlinebin(8*ii-6), Imgmark_extractlinebin(8*ii-5), Imgmark_extractlinebin(8*ii-4),...
                                     Imgmark_extractlinebin(8*ii-3), Imgmark_extractlinebin(8*ii-2), Imgmark_extractlinebin(8*ii-1), Imgmark_extractlinebin(8*ii));
end
Imgmarkextract = reshape(Imgmarklinedec, [M, N]);
figure;imshow(Imgmarkextract,[]);title('提取出的隐藏图');
imwrite(uint8(Imgmarkextract), '提取出的隐藏图.png'); % 保存图片

三个工具函数:

% bin2dec_trans.m
% 二进制转十进制
function Data=bin2dec_trans(y7,y6,y5,y4,y3,y2,y1,y0)
   Data=y7*128+y6*64+y5*32+y4*16+y3*8+y2*4+y1*2+y0;
end

% Find8bits.m
function [y7,y6,y5,y4,y3,y2,y1,y0]=Find8bits(Data)
y0=mod(Data,2);
y7=fix(Data/128);Data=Data-y7*128;
y6=fix(Data/64); Data=Data-y6*64;
y5=fix(Data/32); Data=Data-y5*32;
y4=fix(Data/16); Data=Data-y4*16;
y3=fix(Data/8);  Data=Data-y3*8;
y2=fix(Data/4);  Data=Data-y2*4;
y1=fix(Data/2);  Data=Data-y1*2;
end

% FindNotZero.m
%找出第一个不为零的数位 从最高位(第八位)开始
function posNzreo=FindNotZero(y7,y6,y5,y4,y3,y2,y1,y0)
if y7~=0      posNzreo=8;
elseif y6~=0  posNzreo=7;
elseif y5~=0  posNzreo=6;
elseif y4~=0  posNzreo=5;
elseif y3~=0  posNzreo=4;
elseif y2~=0  posNzreo=3;
elseif y1~=0  posNzreo=2;
else          posNzreo=1;
end
end

示例

原图:
这里写图片描述

待隐藏的图:
这里写图片描述

合并后的图:
这里写图片描述

提取出的图:
这里写图片描述

可以看到合并后图跟原图相比有细微差距,树懒的鼻子到额头部分有点发紫,因为跟末位置0法相比,我们破坏了更多的图像信息。

而值得欣慰的就是提取出的图是完整的灰度图像,而不再是存储信息量很少的二值图像。

至于能不能隐藏彩色图像然后提取出彩色图像,我认为是可以的,只要你的原图尺寸足够大,有更多的位平面空间可以保存待隐藏图像的三个通道图即可。(三个通道图相当于三个灰度图)

可以用StegSolve 软件打开合并后的图像,可以看到不管哪个位平面都不会完整显示出待隐藏的图像,从保密性来说,肯定是完爆末位置0法的。

  • 16
    点赞
  • 112
    收藏
    觉得还不错? 一键收藏
  • 15
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值