0°

LSB信息隐藏实验报告


title: LSB信息隐藏实验报告
date: 2019-05-08 21:12:36
tags:
– 实验报告
– LSB
– 隐写
categories:
– Study
– 信息隐藏


这学期上的信息隐藏课的实验,这次做的是LSB算法。因为写实验报告是用markdown写然后转pdf提交的,所以就记录下。


前言

感觉这个LSB写的有点辣鸡,凑合看吧(233)

本来还想把实验报告改的通顺一点再放上来,但是因为懒,所以直接粘贴过来了。。


实验要求

实现LSB信息隐藏算法,使其能够隐藏信息,并且能够还原,然后计算原图和隐藏了信息的图的MSE、PSNR


实验环境

采用python进行编程
其中python版本是: 3.6
用到的库及说明如下:

from PIL import Image   # 为了对载体图片进行像素操作
import base64           # 实验思路是对文字和图片进行不同方式处理
import qrcode           # 如果要隐藏的是图片的话,把图片base64编码后再隐藏
                        # 如果要隐藏的是文字的话,先用qrcode生成一张二维码,
                        # 把文字隐藏在二维码里,然后再把二维码图片base64后隐藏
import numpy as np      #为了计算PSNR、MSE
import math
import cv2

实验思路

关于这个实验我的设计思路是。

  1. 先编写用于读取要隐藏的文件的函数get_data(),相关代码如下。简单解释下,先是根据传入的文件名判断文件的后缀,如果是txt文件则使用generate_qrcode()函数生成包含txt内容的二维码,然后再把二维码图片进行base64转换隐藏。如果是png、jpg、gif文件的话,先调用img_to_b64()函数转为base64编码后,然后再进行隐藏。进行完base64转换后,在要隐藏的数据后面加上\r\n\r\n + 后缀,以此作为结束符,也方便还原,然后把内容从字节流转为8位比特流。

    # 获取需要隐藏的文件信息
    def get_data(path):
        data = ''
        # 获取文件类型,根据类型不同操作
        ext = path[path.find('.') + 1:]
        if ext.lower() == 'txt':
            img_path = generate_qrcode(path)
            # 生成二维码后的文件扩展为png
            ext = 'png'.upper()
            ext = '\r\n\r\n' + ext
            # 藏的时候要把图片扩展名藏进去,方便还原
            b64data = img_to_b64(img_path) + ext
        if ext.lower() in ['png','jpg','gif']:
            ext = '\r\n\r\n' + ext.upper()
            b64data = img_to_b64(path) + ext
        # 把信息转为比特流
        for i in range(len(b64data)):
            data +=  bin(ord(b64data[i])).replace('0b','').zfill(8) #zfill返回指    定长度字符串,不足填0
        return data
    # 把图片转为base64,返回base64字串
    def img_to_b64(path):
        f = open(path,'rb')
        s = f.read()
        f.close()
        res = base64.b64encode(s).decode()
        return res
    # 把txt内容转为二维码,返回生成的二维码路径
    def generate_qrcode(path):
        f = open(path,'rb')
        text = f.read()
        f.close()
        img = qrcode.make(text)
        save_path = path[:path.find('.')] + '.png'
        img.save(save_path)
        return save_path
    
  2. 编写lsb加密函数,这里用PIL库里的Image包文件,先判断要隐藏的信息是否超过载体最低一位的容量,如果超过即输出提示信息载体装不下,换个大点的吧,然后退出。若没有超过容量的话,就遍历载体图片的像素点,利用模2取出最后一位,然后替换成我们要隐藏的信息。重要操作的话,下面代码都附上了注释,这里就不多叙述了。

    # old 载体,path 隐藏信息,new 新生成的图片
    def lsb(old,path,new):
        im = Image.open(old)
        width,height = im.size[0],im.size[1]
        count = 0
        data = get_data(path)
        if len(data) > width * height *3:
            print("载体装不下,换个大点的吧")
            exit()
        data_len = len(data)
        for h in range(height):
            for w in range(0,width):
                pixel = im.getpixel((w,h)) #获取像素
                a = pixel[0]
                b = pixel[1]
                c = pixel[2]
                # 每次循环前判断是否藏完
                if count == data_len:
                    break
                a = a - mod(a,2) + int(data[count]) # 先把最低一位减了再藏
                count += 1
                if count == data_len:
                    im.putpixel((w,h),(a,b,c))
                    break
                b = b - mod(b,2) + int(data[count])
                count += 1
                if count == data_len:
                    im.putpixel((w,h),(a,b,c))
                    break
                c = c - mod(c,2) + int(data[count])
                count += 1
                if count == data_len:
                    im.putpixel((w,h),(a,b,c))
                    break
                if count % 3 == 0: #一个像素藏完,putpixel一下
                    im.putpixel((w,h),(a,b,c))
        im.save(new)
    
  3. 然后编写解密函数,lsb_decode(),代码如下,这里我是直接把图像的最后一位先全部提出来,然后根据上面加密函数里最后放入的\r\n\r\n标记判断是否结束,然后找出扩展名,将提取出的数据base64解码后存到文件里,这里生成的文件名用lsb_decode.+找出的扩展名。

    def lsb_decode(path):
        im = Image.open(path)
        width,height = im.size[0],im.size[1]
        data = ''
        for h in range(height):
            for w in range(width):
                pixel = im.getpixel((w,h)) #获取像素
                for p in pixel[:3]:         
                    data += str(mod(p,2))
        temp_data = ''
        for i in range(0,len(data),8):
            temp = int(data[i:i+8],2)  #转成十进制
            temp_data += chr(temp)
        # 藏的信息结束位置
        end_pos = temp_data.find('\r\n\r\n')
        msg_b64 = temp_data[:end_pos]
        msg_ext = temp_data[end_pos+4:end_pos+7]
        msg = base64.b64decode(msg_b64)
        msg_name = "lsb_decode." + msg_ext.lower()
        f = open(msg_name,'wb')
        f.write(msg)
        f.close()   
    
  4. 然后编写计算MSE、PSNR的函数,这里代码就是利用PSNR和MSE的公式计算,不多讲。

    def PSNR(old,new):
        import numpy as np
        import math
        import cv2
        img1,img2 = cv2.imread(old),cv2.imread(new)
        mse = np.mean((img1 - img2) ** 2 )
        if mse < 1.0e-10:
          return 100
        psnr = 10 * math.log10(255.0**2/mse)
        print("MSE为:{0}\nPSNR为:{1}".format(mse,psnr))
    

实验测试

  1. 测试隐藏txt文件,要隐藏的1.txt文件内容为易涛LSB实验测试,使用下面代码测试。预期结果应该是,产生1.png(1.txt的二维码文件),隐藏有1.pngbase64编码的lsb_encode_txt.bmp文件,和解码还原后的lsb_decode.png二维码

    if __name__ == "__main__":
         data_path = r"1.txt"
         image_path = r"mitu.bmp"
         new_path = r"lsb_encode_txt.bmp"
         lsb(image_path,data_path,new_path)
         lsb_decode("lsb_encode_txt.bmp")
         PSNR(image_path,new_path)
    

    下面截图是未执行代码前的截图


    image

    执行一下代码,代码执行结果如下,可以看到计算出了MSE和PSNR

    image

    而我们的目录也多出来了系列文件,可以扫描下面截图的两个二维码从而获取隐藏的信息易涛LSB实验测试

    image

  2. 测试隐藏图片文件,隐藏的文件名为shell.gif,用下面代码测试。预期结果应该是,生成隐藏有shell.gifbase64编码的lsb_encode_img.bmp文件,和解码还原后的lsb_decode.gif文件

    if __name__ == "__main__":
         data_path = r"shell.gif"
         image_path = r"mitu.bmp"
         new_path = r"lsb_encode_img.bmp"
         lsb(image_path,data_path,new_path)
         lsb_decode("lsb_encode_img.bmp")
         PSNR(image_path,new_path)
    

    下面是未执行代码前的文件夹目录


    image

    代码执行结果截图如下,可以看到计算的MSE和PSNR值,PSNR值比上面小了一些,可能是隐藏的shell.gif的大小比二维码大导致。

    image

    执行完后的目录截图如下。可以看到,和预期结果一致。

    image


实验源码

整个实验的源码lsb.py如下,实验的代码和测试用到的图片也会以压缩包附件形式上传。

#!/usr/bin/env python
# coding=UTF-8
'''
@Author: 易涛
@LastEditors: 易涛
@Description: LSB隐写算法
@Date: 2019-04-30 13:41:37
@LastEditTime: 2019-05-08 16:43:23
'''
from PIL import Image
import base64
import qrcode
# 获取需要隐藏的文件信息
def get_data(path):
    data = ''
    # 获取文件类型,根据类型不同操作
    ext = path[path.find('.') + 1:]
    if ext.lower() == 'txt':
        img_path = generate_qrcode(path)
        # 生成二维码后的文件扩展为png
        ext = 'png'.upper()
        ext = '\r\n\r\n' + ext
        # 藏的时候要把图片扩展名藏进去,方便还原
        b64data = img_to_b64(img_path) + ext
    if ext.lower() in ['png','jpg','gif']:
        ext = '\r\n\r\n' + ext.upper()
        b64data = img_to_b64(path) + ext
    # 把信息转为比特流
    for i in range(len(b64data)):
        data +=  bin(ord(b64data[i])).replace('0b','').zfill(8) #zfill返回指定长度字符串,不足填0
    return data
# 把图片转为base64,返回base64字串
def img_to_b64(path):
    f = open(path,'rb')
    s = f.read()
    f.close()
    res = base64.b64encode(s).decode()
    return res
# 把txt内容转为二维码,返回生成的二维码路径
def generate_qrcode(path):
    f = open(path,'rb')
    text = f.read()
    f.close()
    img = qrcode.make(text)
    save_path = path[:path.find('.')] + '.png'
    img.save(save_path)
    return save_path
def mod(x,y):
    return x%y
# old 载体,path 隐藏信息,new 新生成的图片
def lsb(old,path,new):
    im = Image.open(old)
    width,height = im.size[0],im.size[1]
    count = 0
    data = get_data(path)
    if len(data) > width * height *3:
        print("载体装不下,换个大点的吧")
        exit()
    data_len = len(data)
    for h in range(height):
        for w in range(0,width):
            pixel = im.getpixel((w,h)) #获取像素
            a = pixel[0]
            b = pixel[1]
            c = pixel[2]
            # 每次循环前判断是否藏完
            if count == data_len:
                break
            a = a - mod(a,2) + int(data[count]) # 先把最低一位减了再藏
            count += 1
            if count == data_len:
                im.putpixel((w,h),(a,b,c))
                break
            b = b - mod(b,2) + int(data[count])
            count += 1
            if count == data_len:
                im.putpixel((w,h),(a,b,c))
                break
            
            c = c - mod(c,2) + int(data[count])
            count += 1
            if count == data_len:
                im.putpixel((w,h),(a,b,c))
                break
            if count % 3 == 0: #一个像素藏完,putpixel一下
                im.putpixel((w,h),(a,b,c))
    im.save(new)
def lsb_decode(path):
    im = Image.open(path)
    width,height = im.size[0],im.size[1]
    data = ''
    for h in range(height):
        for w in range(width):
            pixel = im.getpixel((w,h)) #获取像素
            for p in pixel[:3]:         
                data += str(mod(p,2))
    
    temp_data = ''
    for i in range(0,len(data),8):
        temp = int(data[i:i+8],2)  #转成十进制
        temp_data += chr(temp)
    # 藏的信息结束位置
    end_pos = temp_data.find('\r\n\r\n')
    msg_b64 = temp_data[:end_pos]
    msg_ext = temp_data[end_pos+4:end_pos+7]
    msg = base64.b64decode(msg_b64)
    msg_name = "lsb_decode." + msg_ext.lower()
    f = open(msg_name,'wb')
    f.write(msg)
    f.close()
def PSNR(old,new):
    # target:目标图像  ref:参考图像  scale:尺寸大小
    # assume RGB image
     
    import numpy as np
    import math
    import cv2
    img1,img2 = cv2.imread(old),cv2.imread(new)
    mse = np.mean((img1 - img2) ** 2 )
    if mse < 1.0e-10:
      return 100
    psnr = 10 * math.log10(255.0**2/mse)
    print("MSE为:{0}\nPSNR为:{1}".format(mse,psnr))
if __name__ == "__main__":
    data_path = r"shell.gif"        # 自己选择
    image_path = r"mitu.bmp"
    new_path = r"lsb_encode_img.bmp"
    lsb(image_path,data_path,new_path)
    lsb_decode("lsb_encode_img.bmp")
    PSNR(image_path,new_path)

「点点赞赏,手留余香」

    还没有人赞赏,快来当第一个赞赏的人吧!