python学习之 图片隐写术

原文来自https://www.shiyanlou.com/courses/651/labs/2121/documentclass 

bytearray([source[encoding[errors]]])

返回一个新的字节数组。bytearray 类是一个关于整数的 mutable(可变)序列,范围为0 < = x < 256。它包含了可变序列大部分的常用方法,参见 Mutable Sequence Types,同时也包含了bytes类型的大部分方法,参见Bytes and Bytearray Operations

可选参数source可用于初始化数组,有多种实现方式:

  • 如果是个字符串 string,应该直接在参数中指定编码 encoding (and optionally, errors) ;之后bytearray() 将使用 str.encode()按照编码转化字符串为字节序列。
  • 如果是个integer,数组将会获取大小并将其初始化为空字节。
  • 如果是个符合buffer接口的对象,该对象的一个只读缓冲区将用于初始化字节数组。
  • 如果它是可迭代类型iterable,其整数元素的取值范围是0 <= x < 256,一般用作数组的初始内容。

如果没有设置任何参数,数组大小为0.

bin ( x )

Convert an integer number to a binary string. The result is a valid Python expression. If x is not a Python int object, it has to define an __index__()method that returns an intege

( 注意,bin在前面会加上'0b',并且这个二进制串的长度是不确定的(是十进制BCD编码的长度)),我们想要实现信息编码,最好让它达到定长,而达到定长的函数实现是很简单的

对于作用域:

  • python能够改变变量作用域的代码段是def、class、lamda.
  • if/elif/else、try/except/finally、for/while 并不能涉及变量作用域的更改,也就是说他们的代码块中的变量,在外部也是可以访问的
  • 变量搜索路径是:本地变量->全局变量



关于UTF-8的实现方式

UTF-8是Unicode的一种实现方式,也就是它的字节结构有特殊要求,所以我们说一个汉字的范围是0X4E00到0x9FA5,是指unicode值,至于放在utf-8的编码里去就是由三个字节来组织,所以可以看出unicode是给出一个字符的范围,定义了这个字是码值是多少,至于具体的实现方式可以有多种多样来实现。



UTF-8是一种变长字节编码方式。对于某一个字符的UTF-8编码,如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的位数,其余各字节均以10开头。UTF-8最多可用到6个字节。 
如表: 
1字节 0xxxxxxx 
2字节 110xxxxx 10xxxxxx 
3字节 1110xxxx 10xxxxxx 10xxxxxx 
4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 
5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
因此UTF-8中可以用来表示字符编码的实际位数最多有31位,即上表中x所表示的位。除去那些控制位(每字节开头的10等),这些x表示的位与UNICODE编码是一一对应的,位高低顺序也相同。 
实际将UNICODE转换为UTF-8编码时应先去除高位0,然后根据所剩编码的位数决定所需最小的UTF-8编码位数。 
因此那些基本ASCII字符集中的字符(UNICODE兼容ASCII)只需要一个字节的UTF-8编码(7个二进制位)便可以表示。 

对于上面的问题,代码中给出的两个字节是 
十六进制:C0 B1 
二进制:11000000 10110001 
对比两个字节编码的表示方式: 
110xxxxx 10xxxxxx 
提取出对应的UNICODE编码: 
00000 110001 
可以看出此编码并非“标准”的UTF-8编码,因为其第一个字节的“有效编码”全为0,去除高位0后的编码仅有6位。由前面所述,此字符仅用一个字节的UTF-8编码表示就够了。 



自己的一点体会:

1、

关于这个代码,有一个地方需要解释一下:为什么原文中index*4<len(binary)能够保证既不越界又能表示完所有的binary呢?这是因为binary一定是4的倍数,我们的constLen函数使得binary其实一定是8的倍数,因为一个中文字符需要3个整数来编码,一个整数需要8个binary bit来表示,所以binary数组一定是8的倍数,所以这样做是没有问题的

2、

还有一个是decode的时候为什么直接使用join就可以连成一串了呢?这是因为如果list的内部全部都是字符串,那么使用join方法就能连成一个完整的字符串,并且前面不用加上str方法来转换,否则需要加上str方法转变,并且转换出来的字符串会带上'[',']',','等list的符号

3、

这个lamda函数其实很简单。。不需要看刘大牛的博客也可以理解。自己用c写一个递归马上就能发现它是怎么写的了

4、关于全文编码的一个过程:中文/英文 -》utf-8(bytearray其实等价于str.encode('utf-8'))-》utf-8转化成的int(并且具有前导0,其实这个前导0只有在符号是ascill码的时候需要添加,因为查看utf-8编码表发现凡是需要字节数大于1的字符它的utf-8编码的第一位一定是1) -》加密到图片 -》从图片中获得尾数 ->用两个函数(func和rec)解码为unicode的int(因为unicode删除utf-8的前导数字以后就是unicode编码) -》用chr从unicode的int解码为unicode


</pre><pre name="code" class="python">#coding=utf-8
from PIL import Image

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]
	evenIMG = Image.new(image.mode,image.size)
	evenIMG.putdata(evenPixels)
	return evenIMG


def encodeDataInImage(image,data):
	evenIMG = makeImageEven(image)
	binary = ''.join(map(constLen,bytearray(data,'utf-8')))
	evenPixels = list(evenIMG.getdata())
	if len(binary) > len(evenPixels)*4:
		raise Exception("Error:cannot conver data coded larger than"+len(evenPixels)*4+4)
	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(evenPixels)]
	encodedIMG = Image.new(image.mode,image.size)
	encodedIMG.putdata(encodedPixels)
	return encodedIMG

def constLen(int):
	binary = '0'*(8-(len(bin(int))-2))+bin(int).replace('0b','') # 8 bit lenth with front 0s
	return binary # the zero can be deleted later, which won't effect the result


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 ''
    # rec = lambda x, i: x and (x[2:8] + (i > 1 and rec(x[8:], i-1) or '')) or ''
    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(r%2)+str(g%2)+str(int(b%2))+str(int(t%2)) for (r,g,b,t) in pixels])
	location_END = binary.find('0000000000000000')
	if location_END % 8 == 0 :
		End_Index = location_END
	else:
		End_Index = location_END + (8 - location_END % 8)
	data = BinaryToString(binary[:End_Index])
	return data




if __name__ == '__main__':	
	# IMGname = raw_input("IMG name:")
	IMGname='4.png'
	IMG = Image.open(IMGname)
	# data = raw_input("the data:")
	data='asdasdasd'
	encodedIMG = encodeDataInImage(IMG,data)
	encodedIMG.save('encoded'+IMGname)
	print decodeImage(encodedIMG)


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值