两句闲话
老师在课上讲了许多图片隐写和隐写分析的方法,在这里我整合一下,并对部分进行代码实现。
LSB隐写
LSB隐写是最基础、最简单的隐写方法,具有容量大、嵌入速度快、对载体图像质量影响小的特点。
LSB的大意就是最低比特位隐写。我们将深度为8的BMP图像,分为8个二值平面(位平面),我们将待嵌入的信息(info)直接写到最低的位平面上。换句话说,如果秘密信息与最低比特位相同,则不改动;如果秘密信息与最低比特位不同,则使用秘密信息值代替最低比特位。
具体实现如下
from PIL import Image
import math
class LSB:
def __init__(self):
self.im=None
def load_bmp(self,bmp_file):
self.im=Image.open(bmp_file)
self.w,self.h=self.im.size
self.available_info_len=self.w*self.h # 不是绝对可靠的
print ("Load>> 可嵌入",self.available_info_len,"bits的信息")
def write(self,info):
"""先嵌入信息的长度,然后嵌入信息"""
info=self._set_info_len(info)
info_len=len(info)
info_index=0
im_index=0
while True:
if info_index>=info_len:
break
data=info[info_index]
x,y=self._get_xy(im_index)
self._write(x,y,data)
info_index+=1
im_index+=1
def save(self,filename):
self.im.save(filename)
def read(self):
"""先读出信息的长度,然后读出信息"""
_len,im_index=self._get_info_len()
info=[]
for i in range(im_index,im_index+_len):
x,y=self._get_xy(i)
data=self._read(x,y)
info.append(data)
return info
#===============================================================#
def _get_xy(self,l):
return l%self.w,int(l/self.w)
def _set_info_len(self,info):
l=int(math.log(self.available_info_len,2))+1
info_len=[0]*l
_len=len(info)
info_len[-len(bin(_len))+2:]=[int(i) for i in bin(_len)[2:]]
return info_len+info
def _get_info_len(self):
l=int(math.log(self.w*self.h,2))+1
len_list=[]
for i in range(l):
x,y=self._get_xy(i)
_d=self._read(x,y)
len_list.append(str(_d))
_len=''.join(len_list)
_len=int(_len,2)
return _len,l
def _write(self,x,y,data):
origin=self.im.getpixel((x,y))
lower_bit=origin%2
if lower_bit==data:
pass
elif (lower_bit,data) == (0,1):
self.im.putpixel((x,y),origin+1)
elif (lower_bit,data) == (1,0):
self.im.putpixel((x,y),origin-1)
def _read(self,x,y):
data=self.im.getpixel((x,y))
return data%2
if __name__=="__main__":
lsb=LSB()
# 写
lsb.load_bmp('test.bmp')
info1=[0,1,0,1,1,0,1,0]
lsb.write(info1)
lsb.save('lsb.bmp')
# 读
lsb.load_bmp('lsb.bmp')
info2=lsb.read()
print (info2)
在这里,我们定义几个指标来评价隐写算法。
假设,某灰度图像的大小为$M\times N$,深度为8。
嵌入容量 $M\times N$bit。
嵌入率 $\frac{嵌入容量}{总容量}$,LSB的嵌入率为$\frac{1}{8}=12.5\%$。
MSE mean square error,平均方根误差,$MSE=\frac{\sum^{M}_{m=1}\sum^{N}_{n-1}d(m,n)^2}{M \times N}$,这里的$d(m,n)$指的是,原图像和修改后的图像在$(m,n)$位置上的像素点之差。
PSNR peak signal-to-noise ratio,峰值信噪比,$PSNR=-10log\{\frac{MSE}{255^2MN}\}$。
更多的评价方法还有,VQM(vedio quality measurement), SSIM(structural similarity index)等。
面向JPEG的图像隐写(1):Jsteg隐写
关于JPEG格式,可以看看这篇博客 JPEG图像压缩算法流程详解。JPEG压缩中,最主要的就是DCT变换。
Jsteg隐写是将秘密信息嵌入在量化后的DCT系数的LSB上,但原始值为-1,0,+1的DCT系数除外。此外,由于量化后的DCT系数中有负数,编程的时候需要格外注意以下。
具体实现如下
import math
class Jsteg:
def __init__(self):
self.sequence_after_dct=None
def set_sequence_after_dct(self,sequence_after_dct):
self.sequence_after_dct=sequence_after_dct
self.available_info_len=len([i for i in self.sequence_after_dct if i not in (-1,1,0)]) # 不是绝对可靠的
print ("Load>> 可嵌入",self.available_info_len,'bits')
def get_sequence_after_dct(self):
return self.sequence_after_dct
def write(self,info):
"""先嵌入信息的长度,然后嵌入信息"""
info=self._set_info_len(info)
info_len=len(info)
info_index=0
im_index=0
while True: