暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

基于matlab和python的LSB隐写实现

做一个柔情的程序猿 2021-04-07
1773

LSB隐写技术

LSB全称为 least significant bit,是最低有效位的意思。Lsb图片隐写是基于lsb算法的一种图片隐写术。

在二进制数中意为最低有效位,一般来说,MSB(最高有效位)位于二进制数的最左侧,LSB位于二进制数的最右侧。

由于图像的每一个像素点都是由RGB(红、绿、蓝)三原色组成,而这三种颜色又可以组合成各种其它颜色,每个颜色占8位(如#FFFFFF),LSB隐写即是修改每个颜色值的最低一位,将其替换为我们想要嵌入的信息中的内容,以此来实现数据隐藏。

一个像素点包含三种颜色,每个颜色修改最后1位,这样一个像素点就可以携带3位信息。

应用LSB算法的图像格式需为位图形式,即图像不能经过压缩,如LSB算法多应用于png、bmp等格式,而jpg格式较少。

首先来讲png图片,png图片是一种无损压缩的位图片形格式,也只有在无损压缩或者无压缩的图片(BMP)上实现lsb隐写。如果图像是jpg图片的话,就没法使用lsb隐写了,原因是jpg图片对像数进行了有损压缩,我们修改的信息就可能会在压缩的过程中被破坏。而png图片虽然也有压缩,但却是无损压缩,这样我们修改的信息也就能得到正确的表达,不至于丢失。BMP的图片也是一样的,是没有经过压缩的。BMP图片一般是特别的大的,因为BMP把所有的像数都按原样储存,没有进行压缩。


png图片中的图像像数一般是由RGB三原色(红绿蓝)组成,每一种颜色占用8位,取值范围为0x00~0xFF,即有256种颜色,一共包含了256的3次方的颜色,即16777216种颜色。而人类的眼睛可以区分约1000万种不同的颜色,这就意味着人类的眼睛无法区分余下的颜色大约有6777216种。

LSB隐写就是修改RGB颜色分量的最低二进制位也就是最低有效位(LSB),而人类的眼睛不会注意到这前后的变化,每个像数可以携带3比特的信息。

上图我们可以看到,十进制的235表示的是绿色,我们修改了在二进制中的最低位,但是颜色看起来依旧没有变化。我们就可以修改最低位中的信息,实现信息的隐写。我修改最低有效位的信息的算法就叫做lsb加密算法,提取最低有效位信息的算法叫做lsb解密算法。

再放两张图加深下理解:


顺序嵌入


顺序嵌入很简单,遍历每个像素点,然后将二进制嵌入最后一位即第八位即可。

画了一个流程图,这里贴上来方便理解:

python版本


首先我用python实现了一个,利用python3的PIL库写起来还是很简单的。直接看代码吧

    # coding:utf-8
    # python 3.6.6

    from PIL import Image
    import time

    # 将字符串转为二进制
    def str_convert_bin(s):
    result = ''
    for c in s:
    b = bin(ord(c)).replace('0b', '')
    b = '0'*(7-len(b))+b
    result = result+b
    return result

    # 将二进制转化为字符串
    def bin_convert_str(b):
    str=''
    # 将二进制字符串每7位分割,成列表
    b1 = [b[i:i+7] for i in range(0, len(b), 7)]
    for i in range(len(b1)):
    b2 = chr(int(b1[i],2))
    str = str+b2
    return str

    # 将二进制字符串嵌入图片像素B通道 im:Image()、bin1:要嵌入的二进制串
    def insert(im,bin1):
    size = im.size
    length = len(bin1)
    k=0
    flag=0
    for i in range(size[0]):
    for j in range(size[1]):
    # im.getpixel((i,j))读取像素点(i,j)的像素值
    pixel_b=bin(im.getpixel((i,j))[2]).replace('0b', '')
    if pixel_b[-1:]<bin1[k]:
    # im.putpixel((i,j),(x,y,z))设置像素点(i,j)的RGB值为(x,y,z)
    im.putpixel((i,j),(im.getpixel((i,j))[0],im.getpixel((i,j))[1],im.getpixel((i,j))[2]+1))
    if pixel_b[-1:]>bin1[k]:
    im.putpixel((i,j),(im.getpixel((i,j))[0],im.getpixel((i,j))[1],im.getpixel((i,j))[2]-1))
    k=k+1
    if k==length:
    flag=1
    break
    if flag==1:
    break
    print("字符串嵌入完成\n\n")

    # 提取字符串 im:Image()、length:二进制字符串长度
    def extract(im,length):
    size = im.size
    k=0
    result=''
    flag=0
    for i in range(size[0]):
    for j in range(size[1]):
    pixel_b=bin(im.getpixel((i,j))[2]).replace('0b', '')
    result=result+pixel_b[-1:]
    k=k+1
    if k==length:
    flag=1
    break
    if flag==1:
    break
    print("提取完成,二进制字符串为:\n%s"%result)
    str = bin_convert_str(result)
    print("转换完成,结果为:\n%s"%str)

    def main():
    test_str=input("请输入字符串:\n")
    result = str_convert_bin(test_str)
    print("待嵌入字符串转化为二进制为:\n%s"%result)
    print("开始嵌入....")
    im = Image.open("2.bmp")
    insert(im, result)

    time.sleep(5)
    print("开始提取字符串:")
    extract(im, len(result))

    if __name__=='__main__':
    main()

    结果:

    matlab版本


    后续更复杂的隐写还是需要用到matlab,于是乎还是转matlab吧。

    matlab就不介绍了,代码写得粗糙,勉强贴上代码:

      % LSB隐藏(顺序隐藏)
      % 可以隐藏数字、字母、英文字符 ex: hello,world.111
      % jpg失真!用png/bmp

      clear all;clc;
      data=imread('1.png'); % 读入图片
      str=input('请输入要潜入的字符串:','s'); % 接收字符串
      str_bin_mat=dec2bin(str); % 字符串转二进制矩阵

      % 二进制矩阵转字符串
      l_str_bin_mat=size(str_bin_mat); %二进制矩阵
      str_bin='';
      for i=1:l_str_bin_mat(1)
      for j=1:l_str_bin_mat(2)
      str_bin=[str_bin,str_bin_mat(i,j)];
      end
      end
      disp('待嵌入的字符串二进制形式为');
      disp(str_bin);

      % 检测是否能够完全嵌入
      [l,w,h]=size(data);
      if length(str_bin)>=l*w*h
      error('字符长度超出!!!');
      end

      %嵌入程序
      data1=data;
      disp('开始嵌入');
      flag1=1; %输入字符二进制长度,判断嵌入是否结束
      flag2=1;
      flag3=1;
      for i=1:l
      if flag3==0
      break
      end
      for j=1:w
      if flag2==0
      flag3=0;
      break
      end
      for k=1:h
      if flag1>length(str_bin)
      disp('over');
      flag2=0;
      break
      end
      a=dec2bin(data1(i,j,k),8);%数字取二进制
      data1(i,j,k)=bin2dec([a(1:7),str_bin(flag1)]);%二进制相加,再取十进制
      flag1=flag1+1;
      end
      end
      end
      %保存图片
      imwrite(data1,'1-2.png')
      disp('嵌入完成,保存为1-2.png');

      %以下为提取程序
      disp('开始提取...')
      data2 = imread('1-2.png');
      [l,w,h]=size(data2);
      str_bin1='';%提取到的二进制字符串
      locationx=[];
      locationy=[];
      locationxy=[];
      m=length(str_bin);
      flag1=1;
      flag2=1;
      flag3=1;
      for i=1:l
      if flag3==0
      break
      end
      for j=1:w
      if flag2==0
      flag3=0;
      break
      end
      for k=1:h
      if flag1>length(str_bin)
      flag2=0;
      break
      end
      a=dec2bin(data2(i,j,k),8);%十进制转二进制
      str_bin1=[str_bin1,a(8)];% 取最后一个数
      flag1=flag1+1;
      end
      end
      end
      disp('提取完成!');
      disp('提取到的二进制字符串为:');
      disp(str_bin1);
      disp('开始转换...')

      % 二进制转字符串
      str2='';
      for q=1:length(str_bin1)/l_str_bin_mat(2)
      w=str_bin1((q-1)*l_str_bin_mat(2)+1:q*l_str_bin_mat(2));%w为每七位
      a=bin2dec(w); % 转换为十进制
      if a>9
      str2=[str2,char(a)];
      end
      if a<9
      str2=[str2,a];
      end
      end
      disp('转换完成');
      disp('最终结果为:');
      disp(str2);

      可以嵌字符,数字,字母,结果如下:

      随机LSB隐写


      其实这个和顺序差不多,无非就是在遍历像素点的时候,将(i,j)改为随机的点,我们可以写个随机函数随机生成列表X和Y,当要嵌入(i,j)时我们就将其变为(X(i+j), Y(i+j)),为什么不是(X(i), Y(j))读者可以想想(这样不随机)。

      接下来看生成随机列表的函数 randomxy.m:

        % 随机生成两个列表
        % l为长,w为宽,len_str_bin为嵌入二进制长度,key为随机种子
        function [x,y]=randxy(l,w,len_str_bin,key)
        %设置随机种子,生成一串随机数
        rand('seed',key);
        disp('hhhhhhhhh');
        x=randperm(l,len_str_bin);
        y=randperm(w,len_str_bin);
        %x = unique(x); %去重处理
        %y = unique(y) ;%去重处理
        end

        然后看主要代码:

          % By gengyanqing
          % LSB隐藏(随机隐藏)
          % 可以隐藏数字、字母、英文字符 ex: hello,world.111
          % jpg失真!用png/bmp

          clear all;clc;
          data=imread('1.png'); % 读入图片
          str=input('请输入要潜入的字符串:','s'); % 接收字符串
          str_bin_mat=dec2bin(str); % 字符串转二进制矩阵

          % 二进制矩阵转字符串
          l_str_bin_mat=size(str_bin_mat); %二进制矩阵
          str_bin='';
          for i=1:l_str_bin_mat(1)
          for j=1:l_str_bin_mat(2)
          str_bin=[str_bin,str_bin_mat(i,j)];
          end
          end
          disp('待嵌入的字符串二进制形式为');
          disp(str_bin);

          % 检测是否能够完全嵌入
          [l,w,h]=size(data);
          if length(str_bin)>=l*w*h
          error('字符长度超出!!!');
          end

          %嵌入程序
          data1=data;
          disp('开始嵌入');
          flag1=1; %输入字符二进制长度,判断嵌入是否结束
          flag2=1;
          flag3=1;
          % 调用randxy函数
          [x,y]=randxy(l,w,length(str_bin),88);
          for i=1:l
          if flag3==0
          break
          end
          for j=1:w
          if flag2==0
          flag3=0;
          break
          end
          for k=1:h
          if flag1>length(str_bin)
          disp('over');
          flag2=0;
          break
          end
          a=dec2bin(data1(x(i+j),y(i+j),k),8);%数字取二进制
          data1(x(i+j),y(i+j),k)=bin2dec([a(1:7),str_bin(flag1)]);%二进制相加,再取十进制
          flag1=flag1+1;
          end
          end
          end
          %保存图片
          imwrite(data1,'1-2.png')
          disp('嵌入完成,保存为1-2.png');

          %以下为提取程序
          %这里提供了x和y,提取二进制字符串位数的信息
          disp('开始提取...')
          data2 = imread('1-2.png');
          [l,w,h]=size(data2);
          str_bin1='';%提取到的二进制字符串
          locationx=[];
          locationy=[];
          locationxy=[];
          m=length(str_bin);
          flag1=1;
          flag2=1;
          flag3=1;
          for i=1:l
          if flag3==0
          break
          end
          for j=1:w
          if flag2==0
          flag3=0;
          break
          end
          for k=1:h
          if flag1>length(str_bin)
          flag2=0;
          break
          end
          a=dec2bin(data2(x(i+j),y(i+j),k),8);%十进制转二进制
          locationx=[locationx,x(i+j)];%随机点x坐标
          locationy=[locationy,y(i+j)];%随机点y坐标
          locationxy=[locationxy;x(i+j),y(i+j),k];
          str_bin1=[str_bin1,a(8)];% 取最后一个数
          flag1=flag1+1;
          end
          end
          end
          disp('提取完成!');
          disp('提取到的二进制字符串为:');
          disp(str_bin1);
          disp('开始转换...')

          % 二进制转字符串
          str2='';
          for q=1:length(str_bin1)/l_str_bin_mat(2)
          w=str_bin1((q-1)*l_str_bin_mat(2)+1:q*l_str_bin_mat(2));%w为每七位
          a=bin2dec(w); % 转换为十进制
          if a>9
          str2=[str2,char(a)];
          end
          if a<9
          str2=[str2,a];
          end
          end
          disp('转换完成');
          disp('最终结果为:');
          disp(str2);

          disp('随机位置分别为');
          disp(locationxy);
          plot(locationx,locationy);

          结果:

          下图为隐藏点的图(可以看出来确实是随机的)

          「❤️ 感谢大家」

          如果你觉得这篇内容对你挺有有帮助的话:

          1. 点赞支持下吧,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓 -_-)
          2. 欢迎在留言区与我分享你的想法,也欢迎你在留言区记录你的思考过程。
          3. 觉得不错的话,也可以阅读近期梳理的文章(感谢鼓励与支持🌹🌹🌹):

          老铁,三连支持一下,好吗?↓↓↓


          欢迎大家加入到知识星球这个大家庭,这里一定有与你志同道合的小伙伴,在这里大家可以一起交流,一起学习,一同吹逼,一同玩耍。。。


          长按按钮  “识别二维码” 关注我
          更多精彩内容等着你哦

          点分享

          点点赞

          点在

          文章转载自做一个柔情的程序猿,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

          评论