原理:对于无损压缩的图片来说,最小值是像素(pixel),每个像素的颜色都是有RGB,三原色组成,颜色的范围就是0 ~ 255,转换成二进制,就是00000000 ~ 11111111。密文可以被拆分成一个个char,并且每个char可以变成二进制。这样利用最低显著位LSB把信息藏到图片中。
我之前写过一个文章,介绍了这类隐写术的原理
隐写术主要分为四步:
- 第一步:读取图片,把每个像素中的RGB值转变成二进制,并保存
- 第二步:读取密文信息,把信息拆分成char,转变成二进制,并保存
- 第三步:把第二步得到的数据,利用LSB算法隐藏在第一步的数据中,并保存
- 第四步:利用第三步得到的数据绘制新的隐写图片
需要import的库有:
import math
import imageio
import matplotlib.pyplot as plt
第一步:读取图片,把每个像素中的RGB值转变成二进制,并保存
path = "test.png"
message = "I am alex" * 15
bits = 3
picture = load_img(path)
# plt.imshow(pic)
# 第一步:读取图片每一个像素的 rgb 并转成二进制,补足八位
img_binary = img_in_binary(picture)
首先,声明load_img() function, 然后利用imageio把图片读进来。
def load_img(path):
img = imageio.imread(path)
# plt.figure(figsize = (50,50))
return img
然后,声明img_in_binary() function,把图片每个像素点的RGB保存在list中。 比如[h,w,0] 代表图片中height为h,width为w这点像素的Red数值,1是Green,2是Blue。
def img_in_binary(pic):
height = int(pic.shape[0])
width = int(pic.shape[1])
img_rgb_decimal = []
for h in range(height):
for w in range(width):
Red = int(pic[h, w, 0])
img_rgb_decimal.append(Red)
Green = int(pic[h, w, 1])
img_rgb_decimal.append(Green)
Blue = int(pic[h, w, 2])
img_rgb_decimal.append(Blue)
img_list = decimal_to_binary(img_rgb_decimal)
return img_list
由于,读取的RGB值是十进制整数,所以声明了一个decimal_to_binary() function, 把十进制变成二进制。Python中,用bin()这个方法可以转变成二进制,但是结果前面会带有0b两个char,所以我从第三位开始取,就是[2:]。最后声明 fill_up_binary() function把每一个二进制补足八位,以防止后期用多位数的LSB算法藏数据的时候,位数不够的问题。
def fill_up_binary(message):
if len(message) != 8:
gap = 8 - len(message)
for i in range(gap):
message = "0"+message
return message
def decimal_to_binary(data):
result = []
for i in data:
temp_bin = bin(i)[2:]
full_bin = fill_up_binary(temp_bin)
result.append(full_bin)
return result
第二步:读取密文信息,把信息拆分成char,转变成二进制,并保存
声明 message_to_binary() function, 把密文信息转变成二进制,然后补足八位。同样会调用fill_up_binary() function. 每个char补足8位是为了提取文字的时候可以提取出来,不然位数不统一,最后无法提取密文。
def message_to_binary(message):
result=""
for i in message:
temp_asc = ord(i)
temp_bin = bin(temp_asc)[2:]
full_bin = fill_up_binary(temp_bin)
result = result + full_bin
return result
# 第二步:把信息变成二进制补足八位
message_binary = message_to_binary(message)
第三步:把第二步得到的数据,利用LSB算法存在第一步的数据中,并保存
LSB核心的数据隐藏就在这步。声明了exchange() function,把密文藏进图片,计算出stego图片的新的RGB值,存到list中,以便于之后的stego图片的生成。
其中,考虑到多位数LSB算法的问题,比如LSB3的话,每个颜色信道会交换最后3位的数据,但是这样的话,最后一个颜色只有最后2位的数据可以交换,因为一个char,8为,3位在R,3位在G, 最后只有2位在B。所以要对于这种 信息长度%算法bits != 0 的情况,单独的做最后一个颜色信道的考虑。
exchange_count 是为了计算一共要多个颜色信道,要用来隐藏数据。
list储存exchange_count位的颜色隐藏信息后,并把原来图片中剩余的颜色RGB值补充进来,生成一个完整的stego图片RGB值。
def exchange(bits,data,img):
list = []
exchange_count = math.ceil(len(data)/bits)
for i in range(exchange_count):
index = i *bits
rest = len(data) % bits
if len(data) == rest:
temp_str = img[i][0:-rest] + data[0:]
else:
temp_str = img[i][0:-bits]+ data[0:bits]
data= data[bits:]
list.append(temp_str)
if len(list) != len(img):
for i in range(len(list),len(img)):
list.append(img[i])
return list
第四步:利用第三步得到的数据生成新的隐写图片
最后一步,声明generate_stego_img() function, 把第三步保存的RGB值,分别覆写原图的每个像素的每个RGB中,最终可以生成新的stego图片。 注意的是,由于第三步储存的结果是二进制,所以这里声明了binary_to_decimal() function把二进制变成十进制。
def binary_to_decimal(stego_binary):
result = []
for i in stego_binary:
result.append(int(i,2))
return result
def generate_stego_img(stego_list,pic):
height = int(pic.shape[0])
width = int(pic.shape[1])
for h in range(height):
for w in range(width):
pic[h, w, 0] = stego_list[0]
pic[h, w, 1] = stego_list[1]
pic[h, w, 2] = stego_list[2]
stego_list = stego_list[3:]
return pic
# 第四步:把新的 rgb 值放到图片里,生成新的图片
stego_img = generate_stego_img(stego_decimal,picture)
plt.imshow(stego_img)