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
实验思路
关于这个实验我的设计思路是。
-
先编写用于读取要隐藏的文件的函数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 -
编写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) -
然后编写解密函数,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() -
然后编写计算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))
实验测试
-
测试隐藏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
-
测试隐藏图片文件,隐藏的文件名为
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)










网友评论