python图像隐写_【快速跟水】图像隐写的python实现

昨天摸鱼的时候,看到 @刘冬煜 的图像隐写的介绍文章

什么是图像隐写

首先,简单介绍一下这个技术。图像隐写听起来很高大上,其实说起来非常简单,一般的,图像都可以用RGB三通道表示(可能也有CMYL或其他的方式),对于每个通道的微小扰动,事实上人眼是无法很好捕捉的。因此,稍微对图像的RGB值进行调整,得出来的图像和原图从肉眼上看不会有什么区别。基于这个现象,我们就可以把信息放在对RGB值的微小修改上,从而实现图像隐写。一个非常简单的例子是EOJ3343

当然,EOJ3343实现的隐写是利用图像的合成。如果我们把要隐藏的信息转化为二进制,和图像进行某种运算得到新的图像,可以储存更多的信息。从文章低四位比特基本没有区别,所以我们可以把信息与图像的低四位做异或,借此实现图像的隐写。异或和简单的加减法相比,有着不会溢出的优点,同时,反向得到信息也很容易,只需要用原图重新进行异或即可。

实现结果

以下是具体实现的结果,同样选取了JOJO的名台词进行隐写。私の名前は吉良吉影です。33歳です。杜王町東北部の別荘地一帯に住んでいます。私は亀友チェーン店でサービスしています。毎日残業して夜8時まで家に帰ります。タバコは吸わないで、お酒はただ試食だけにします。夜11時に寝ます。毎日8時間ぐらい寝ます。寝る前に、必ず温かい牛乳を一杯飲んで、20分間の柔軟体操をして、ベッドに入って、すぐ寝ます。一眠りして夜が明けるまで、

加上原图

得到处理后

可以看到,两者基本没有区别。

具体实现

代码主体分成两个部分,一个是对信息的编码和解码,一个是图像的编码解码。

信息编码解码

为了能同时适配多种语言,实现时选择采用UTF-8读入,先转换为16进制,这样就能分割出4比特的小块,然后再逐步转换为十进制列表。解码是同样读入十进制列表,反向得到原文。

def encode(filename):

with open(filename,encoding="utf-8") as doc:

file = doc.readlines()

text = ''.join(file)

byte = text.encode()

hs = ''.join(['%02X' %x for x in byte])

ls = list(hs)

ls = [int(i, 16) for i in ls]

return ls

def decode(ls):

ls = [hex(i)[2:] for i in ls]

ans = ''.join(ls)

bs = bytes.fromhex(ans)

fin = bs.decode()

return fin

图像编码解码

同样是两个互逆的过程,硬跑就完事了。

事实上,由于是显式的循环遍历,稍微修改一下就可以调整开始的位置。甚至还可以先实现一个随机的映射表,对应修改的位置,而不需要连续修改某一块,这样就更像是保存时的图像损失了。

解码时采用顺序读取,直到读取到信息为零。由于UTF-8中0同样是合法的(尤其是进行4比特切割后,0就更多了),预处理信息列表全部加一,解码时再减一。

def en_image(imgname,ls):

im = Image.open(imgname)

data = np.array(im)

l = len(ls)

num = 0

col,row,channel = data.shape

for i in range(0,col):

for j in range(0,row):

for k in range(0,channel):

data[i][j][k] = data[i][j][k] ^ (ls[num] + 1)

num = num + 1

if(l==num):return data

return data

def de_image(ori_img, diff_img):

ori = Image.open(ori_img)

diff = Image.open(diff_img)

ori_data = np.asarray(ori)

diff_data = np.asarray(diff)

ans = []

col,row,channel = ori_data.shape

for i in range(0,col):

for j in range(0,row):

for k in range(0,channel):

temp = ori_data[i][j][k] ^ diff_data[i][j][k]

if(temp == 0):return ans

ans.append(temp - 1)

return ans

输出和保存

整个过程非常简单,最容易翻车的其实是最后的保存环节。在这个实现中,我采用的是PIL的Image模块。然而,这个模块保存JPG格式时必压缩(毕竟JPG本来就是有损压缩格式),所以,有效的保存方式必须是PNG或BMP。如果简单地保存为JPG/JPEG,最后是无法得到有效的结果的。

ls = encode("introduction.txt")

data = en_image("killerqueen.png", ls)

new_im = Image.fromarray(data)

new_im.save("killerqueen-1.png")

ans = de_image("killerqueen.png", "killerqueen-1.bmp")

print(decode(ans))

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值