数字水印(文字嵌入)

项目起源:​

  微信公众号 Crossin的编程教室 中有这么一篇文章【每周一坑】图像的指纹:数字水印 + 【解答】鸡兔同笼

​  学习python之余发现有这么一个数字水印的技术,觉得挺好玩的于是便实现出来。

​  文章中的思路已经说明的很是清楚了,主要思路是我们知道图像是由一个一个的像素点构成的,而每个像素点是由rgb三原色组成,也就是说每个点可以表示为(0-255,0-255,0-255)的一个tuple。由于人眼识别的能力有限,修改最后一位是无法用肉眼发现差异的,如果我们能够将这些数据的最后一位提取出来作为信息位存储信息,那么便能够向图片中嵌入信息啦。

代码解释:

1.我们使用到的python库:

from PIL import Image
import numpy as np
import functools

2.信息嵌入函数:

def embedding_info(picname, savename, text):
    text += '#%#' #作为结束标记
    im = np.array(Image.open(picname))        
    rows, columns, colors = im.shape
    embed = []
    for c in text:
        bin_sign = (bin(ord(c))[2:]).zfill(16)
        for i in range(16):
            embed.append(int(bin_sign[i]))
    
    count = 0
    for row in range(rows):
        for col in range(columns):
            for color in range(colors):
                if count < len(embed):
                    im[row][col][color] = im[row][col][color] // 2 * 2 + embed[count]
                    count += 1

    Image.fromarray(im).save(savename)

  首先我们将需要嵌入的信息添加一个结束标志'#%#',用以在提取信息的时候确定何时停止。之后是使用numpy库将图片转换成矩阵的形式,留待之后方便我们修改信息位。对需要嵌入的信息逐字符进行处理,由于我们需要支持中文信息的嵌入,我们这里使用16的bit位来存储信息,因此我们使用 '(bin(ord(c))[2:]).zfill(16)' 来将字符转换成二进制编码并在高位补0至16位。最后将处理后的比特流逐位替换掉原图像中的RGB值的最后一位,调用PIL库的函数对图片进行保存。

3.信息提取函数:

def extract_info(picname):
    im = np.array(Image.open(picname))
    rows, columns, colors = im.shape
    text = ""
    extract = np.array([], dtype = int)

    count = 0
    for row in range(rows):
        for col in range(columns):
            for color in range(colors):
                extract = np.append(extract, im[row][col][color] % 2)
                count += 1
                if count % 16 == 0:
                    bcode = functools.reduce(lambda x, y: str(x) + str(y), extract)
                    cur_char = chr(int(bcode, 2))
                    text += cur_char
                    if cur_char == '#' and text[-3:] == '#%#':
                        return text[:-3]
                    extract = np.array([], dtype=int)

  提取信息则与上面那个函数的思路相反,将各个信息位提取出来,按每16位转换成字符,并且检查是否到达结束标志。这里说明一下'bcode = functools.reduce(lambda x, y: str(x) + str(y), extract)' 这里使用到了 functools.reduce()函数,它的原型是functools.reduce(function, sequence[, initial]) -> value, 意思就是对sequence连续使用function, 如果不给出initial, 则第一次调用传递sequence的两个元素, 以后把前一次调用的结果和sequence的下一个元素传递给function. 如果给出initial, 则第一次传递initial和sequence的第一个元素给function. 我们使用匿名函数来将各个比特位拼接起来之后在转换成字符。

4.使用示例:

text = "Hello, world!"
embedding_info('Before.png', 'After.png', text)
extract_info('After.png')

  在我们这个项目中,图片的格式最好为PNG格式,其他形如jpg,jpeg格式的图像文件在处理的过程中由于会对原图进行压缩,可能导致信息位被破坏而失去嵌入其中的信息,现在已经有基于机器学习方面的算法可以实现更好的抗干扰性能。

  当然,数字水印是个很大的范畴,有很多实现方式,具有不同的实现难度、信息容量、抗攻击性、对原图的干扰等。我们在这里实现的是最简单的一种方法,其他更加高深的方法还是有待探索的。

 

其他: 

 代码文件以及在线版本: 

Github仓库:Digital Watermarking

在线版本:Schnee_Lab_Text embed

 

文件说明:

1. build 文件夹下是使用cxfreeze打包好的可执行文件(Windows)

2. text_embed.py 是可执行文件的源代码,增加了用户交互

3. text_embed_origin.py 是原始代码,仅提供原始思路

你可能感兴趣的:(数字水印(文字嵌入))