python实现简单的图片隐写术
来源:互联网 发布:融资租赁软件市场 编辑:程序博客网 时间:2024/06/11 09:52
本文转载自Python3图片隐写术
载体文件相对隐秘文件的大小越大,隐藏后者就越加容易。因为这个原因,数字图像在因特网和其他传媒上被广泛用于隐藏消息。例如一个24位的位图中的每个像素的三个颜色分量(红,绿和蓝)各使用8个比特来表示,如果我们只考虑蓝色的话,就有2^8种不同的数值来表示深浅不同的蓝色。而像11111111和11111110这两个值所表示的蓝色,人眼几乎无法区分。因此,这个最低有效位就可以用来存储颜色之外的信息,而且在某种程度上几乎是检测不到的。如果对红色和绿色进行同样的操作,就可以在差不多三个像素中存储一个字节的信息。本实验便是利用RGBA(RGB+Alpha透明度)的最低有效位(Least Significant Bit)来隐藏文字。
1.开发环境
Python3+Pillow库
2.实验过程
开始编写我们的代码。
def encodeDataInImage(image, data): evenImage = makeImageEven(image) binary = ''.join(map(constLenBin,bytearray(data, 'utf-8'))) if len(binary) > len(image.getdata()) * 4: raise Exception("Error: Can't encode more than " + len(evenImage.getdata()) * 4 + " bits in this image. ") encodedPixels = [(r+int(binary[index*4+0]),g+int(binary[index*4+1]),b+int(binary[index*4+2]),t+int(binary[index*4+3])) if index*4 < len(binary) else (r,g,b,t) for index,(r,g,b,t) in enumerate(list(evenImage.getdata()))] encodedImage = Image.new(evenImage.mode, evenImage.size) encodedImage.putdata(encodedPixels) return encodedImage
将隐藏信息编码到图片中的encodeDataInImage函数有两个参数,分别是用作载体的图片对象和需要被隐藏的字符串。首先调用makeImageEven函数获得最低有效位为0的图片副本,然后调用constLenBin函数将需要被隐藏的字符串转换成二进制字符串。如果不能编码全部数据,则抛出异常。接下来将binary中的二进制字符串信息编码进像素里,创建新图片以存放编码后的像素,最后在新图片中添加编码后的数据。
bytearray函数将字符串转换为整数值序列。
makeImageEven函数通过一个移位操作把所有值改成偶数。
def makeImageEven(image): pixels = list(image.getdata()) evenPixels = [(r>>1<<1,g>>1<<1,b>>1<<1,t>>1<<1) for [r,g,b,t] in pixels] evenImage = Image.new(image.mode, image.size) evenImage.putdata(evenPixels) return evenImage
map(constLenBin,bytearray(data,'utf-8'))
对数值序列中的每一个值应用constLenBin函数,去掉bin返回的二进制字符串中的0b,并在左边补足0直到字符串长度为8,从而将十进制数值序列转换为二进制字符串序列。
def constLenBin(int): binary = "0"*(8-(len(bin(int))-2))+bin(int).replace('0b','') return binary
decodeImage函数返回图片解码后的隐藏文字,其接受一个图片对象参数。找到数据截止处所用的字符串0000000000000000很有意思,它的长度为16,而不是直觉上的8,因为两个包含数据的字节的接触部分可能有8个0。
def decodeImage(image): pixels = list(image.getdata()) binary = ''.join([str(int(r>>1<<1!=r))+str(int(g>>1<<1!=g))+str(int(b>>1<<1!=b))+str(int(t>>1<<1!=t)) for (r,g,b,t) in pixels]) locationDoubleNull = binary.find('0000000000000000') endIndex = locationDoubleNull+(8-(locationDoubleNull % 8)) if locationDoubleNull%8 != 0 else locationDoubleNull data = binaryToString(binary[0:endIndex]) return data
binaryToString函数将提取出来的二进制字符串转换为隐藏的文本。
def binaryToString(binary): index = 0 string = [] rec = lambda x, i: x[2:8] + (rec(x[8:], i-1) if i>1 else '') if x else '' fun = lambda x, i: x[i+1:8] + rec(x[8:], i-1) while index + 1 < len(binary): chartype = binary[index:].index('0') length = chartype*8 if chartype else 8 string.append(chr(int(fun(binary[index:index+length],chartype),2))) index += length return ''.join(string)
要看明白这个,必须要先搞懂UTF-8编码的方式。UTF-8是unicode的一种变长度的编码表达方式,也就是说一个字符串中,不同的字符所占的字节数不一定相同。如果我们要支持中文的话,这就给我们的工作带来了一点复杂度。
在上图中,只有x所在的位置(也即是字节中第一个0之后的数据)存储的是真正的字符数据,因此我们使用下面两个匿名函数来提取出这些数据。
rec = lambda x, i: x[2:8] + (rec(x[8:], i-1) if i>1 else '') if x else '' fun = lambda x, i: x[i+1:8] + rec(x[8:], i-1)
fun接收两个参数,第一个参数为一个字符的二进制字符串,这个二进制字符串可能有不同的长度(8、16、24……48);第二个参数为这个字符占多少个字节。lambda x, i: x[i+1:8] + rec(x[8:], i-1)
中x[i+1:8]获得第一个字节的数据,然后调用rec,以递归的方式提取后面字节中的数据。字符的字节数据中,第一个字节开头1的数目便是字符所占的字节数。 string.append(chr(int(fun(binary[index:index+length],chartype),2)))
这一行中用到的函数int和chr的作用如下。
chr:接收一个参数,参数为int值,返回值这个int值的字符。
int:接收两个参数,第一个参数为数字字符串,第二个参数为这个数字字符串代表的数字的进制。
完整的代码如下。
from PIL import Imagedef makeImageEven(image): pixels = list(image.getdata()) evenPixels = [(r>>1<<1,g>>1<<1,b>>1<<1,t>>1<<1) for [r,g,b,t] in pixels] evenImage = Image.new(image.mode, image.size) evenImage.putdata(evenPixels) return evenImagedef constLenBin(int): binary = "0"*(8-(len(bin(int))-2))+bin(int).replace('0b','') return binarydef encodeDataInImage(image, data): evenImage = makeImageEven(image) binary = ''.join(map(constLenBin,bytearray(data,'utf-8'))) if len(binary) > len(image.getdata()) * 4: raise Exception("Error: Can't encode more than " + len(evenImage.getdata()) * 4 + " bits in this image. ") encodedPixels = [(r+int(binary[index*4+0]),g+int(binary[index*4+1]),b+int(binary[index*4+2]),t+int(binary[index*4+3])) if index*4 < len(binary) else (r,g,b,t) for index,(r,g,b,t) in enumerate(list(evenImage.getdata()))] encodedImage = Image.new(evenImage.mode, evenImage.size) encodedImage.putdata(encodedPixels) return encodedImagedef binaryToString(binary): index = 0 string = [] rec = lambda x, i: x[2:8] + (rec(x[8:], i-1) if i>1 else '') if x else '' fun = lambda x, i: x[i+1:8] + rec(x[8:], i-1) while index + 1 < len(binary): chartype = binary[index:].index('0') length = chartype*8 if chartype else 8 string.append(chr(int(fun(binary[index:index+length],chartype),2))) index += length return ''.join(string)def decodeImage(image): pixels = list(image.getdata()) binary = ''.join([str(int(r>>1<<1!=r))+str(int(g>>1<<1!=g))+str(int(b>>1<<1!=b))+str(int(t>>1<<1!=t)) for (r,g,b,t) in pixels]) locationDoubleNull = binary.find('0000000000000000') endIndex = locationDoubleNull+(8-(locationDoubleNull % 8)) if locationDoubleNull%8 != 0 else locationDoubleNull data = binaryToString(binary[0:endIndex]) return dataencodeDataInImage(Image.open("coffee.png"), 'hello word你好!').save('encodeImage.png')print(decodeImage(Image.open("encodeImage.png")))
- python实现简单的图片隐写术
- python图片文本识别的简单实现
- python图片文本识别的简单实现
- Python 实现简单的网络抓取图片
- Python实现一个简单的图片爬虫
- 【python】简单的图片隐写术
- 利用Python实现简单的相似图片搜索的教程
- Python 实现简单的爬虫功能: 图片的抓取
- python+opencv实现简单的图片搜索功能
- 利用Python实现简单的相似图片搜索
- 用Python实现几个简单的图片特效
- python 简单实现 图片百叶窗效果
- python实现简单爬虫抓取图片
- 【Python】Python简单的图片识别
- 简单图片按钮的实现
- 实现简单的图片滚动
- 简单图片浏览器的实现
- 简单图片轮播器的实现
- NYOJ-12 喷水装置2(贪心 区间覆盖)
- LeetCode 454 4Sum II 题解
- C提高(4)/const
- PHP-memcahce概念
- 272. Closest Binary Search Tree Value II
- python实现简单的图片隐写术
- redis数据对象底层结构图
- Git——Day2(git初始化及仓库创建)
- Java 读书笔记 21.1并发
- 浏览器的渲染原理
- Freecodecamp学习笔记(2)
- 跟踪分析Linux内核的启动过程
- 端口扫描—TCP Connect()
- HDU 4858 项目管理 (简单图+暴力)