Raye's Journey

二值图像的隐写

算法思想

把一个二值图像分成矩形图像区域Bi,若某块p1bi > 50% 则嵌入一个1,若p0bi > 50% 则嵌入一个0

但是在嵌入的过程中需要对一些像素的颜色进行调整。

准备1——计算可以利用的块

由上图可以看出,只有当一个块中0或者1的比例落在某个区间并且结合消息是0or1才可以决定这一块究竟是藏0还是藏1

为了简化我们将一幅图片分为10*10的大小,同时我们采用8*8检测,于是可以严格8*8检测的就只剩下中间的64个块了

我们假设有:

  1. msg 要隐藏的消息
  2. count 消息的长度
  3. image 二值图像
  4. R1,R0,lumda 是我们设置的参数
  5. computep1bi函数可以计算出某个块的p1bi
  6. row,col是为了随机隐藏我们随机选择的块首地址

我们需要确定的就是随机选出来的row和col是否可以用,于是很自然地想到需要对8*8的块进行遍历,同时标记col和row是否可用,不可用的直接将其设为-1

% 最后计算哪些是可以用的存起来
j = 1;
for i = 1:m*n
if row(i) ~= -1
  availabler(j) = row(i);
  availablec(j) = col(i);
  j = j+1;

完整代码

%分析可用的图像块与秘密信息对应
% 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
        % 计算这一块的p1bi
		p1bi = compute1bi(row(blockquan), col(blockquan), image);  %计算p1(bi)
		if p1bi >= R1+3*lumda || p1bi <= R0 -3*lumda
			row(blockquan) = -1; % 标记为无用
			col(blockquan) = -1;
			unable = unable + 1;
			msgquan = msgquan - 1; % 该消息还未找到可以隐藏的块
		elseif msg(msgquan, 1) == 1 && p1bi <= R0
            % 调整p1bi变的更小
			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
			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);
			col(blockquan) = col(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

准备2——修改像素点

%修改像素的函数
% 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

这里其实就是针对中间的64个块进行8连接检测。关于8连接检测边界的做法,我的理解就是只要一个像素点周围的8个点有一个不一样就说明这个点是可以修改的,并且为了防止边界扩散,我们并不直接取反,而是取反后+0.01

准备3——计算p1bi的函数

function p1bi = compute1bi(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

>  cd ..