一、数字水印定义
数字水印是将一些标识信息直接嵌入数字载体当中(包括多媒体、文档、软件等),通过这些隐藏在载体中的信息,既不影响载体的使用价值,也不易检测或修改。可以达到确认内容创建者、购买者、传送隐秘信息或者判断载体是否被篡改等目的。
数字水印是指在载体中嵌入一些信息, 载体数据可以是文档、图片、音频或者视频, 嵌入的水印可以是与载体数据有关的, 如作者身份、时间戳、产品属性等, 水印的存在形式可以是文字、图形、数列等, 当遇到疑似侵权等问题时可以通过算法把水印信息提取出来, 从而证明数字产品是否被篡改或者伪造。数字水印具有以下特点:
嵌入成功率。又称嵌入有效性, 主要指的是当有需要将水印信息提取出来时, 水印被检测到的几率, 一般来说这种几率小于100%。
保真度。也称相似度, 是指将水印嵌入原始载体作品后, 载体作品在人的视觉效果中没有发生变化。
鲁棒性。又称抗攻击性, 载体数据在网络传输过程中经常会受到一些常规的信息处理, 如滤波噪声、图像压缩、几何失真或者恶意攻击等, 之后要仍然能在载体中检测到水印的存在。
安全性。当载体受到非法攻击时, 水印没有发生变化或者变化很小, 说明水印的安全性较高。
二、数字水印应用
数字水印技术主要的应用领域是版权保护, 因为水印和载体是密不可分的, 因此, 它更适用于检测鉴别。一般用于版权保护的水印都是由有意义的信息组成, 如所有者的信息、产品的属性等, 这样可以防止版权被侵犯。
数字水印技术通过在数字作品中的水印嵌入证明作品的版权归属信息,从而防止在未经原创作者许可的情况下,其他人擅自使用网络作品的侵权问题出现,其主要的方式如下:第一,数字媒体将水印信息添加到作品中;第二,将网络作品通过数字媒体发行;第三,通过检测网络作品数字媒体的数字水印信息来识别媒体版权信息的真假。
三、图像数字水印
在这里我通过使用PIL这个库文件中的图像处理模块,PIL中,使用Image模块的open()函数打开后,返回的图像对象的模式都是“RGB”。而对于灰度图像,不管其图像格式是PNG,还是BMP,或者JPG,打开后,其模式为“L”。
我们利用该图像处理模块设计了数字水印信息的嵌入和提取,我们用过将水印信息嵌入到图像中的像素点中的方式来嵌入水印,具体的图像数字水印嵌入代码如下:
嵌入:
def watermark_embed(filePath, watermarkInfo, outPath):
img = Image.open(filePath)
imgSize = img.size
pixel = img.load() # 获取图片所有的像素点
len1 = floor((imgSize[0] * imgSize[1]) / (2 * (imgSize[0] + imgSize[1]))) # 数值的下舍整数
flag = 0
if imgSize[0] > imgSize[1]:
len2 = floor((imgSize[0] * imgSize[1]) / (len1 * (imgSize[0] - imgSize[1])))
flag = 1
elif imgSize[0] < imgSize[1]:
len2 = floor((imgSize[0] * imgSize[1]) / (len1 * (imgSize[1] - imgSize[0])))
flag = 2
else:
len2 = floor((imgSize[0] * imgSize[1]) / (len1 * (imgSize[0] + imgSize[1])))
if flag == 0 or flag == 1:
row = len1
col = len2
elif flag == 2:
row = len2
col = len1
new_array1 = []
new_array2 = []
x2 = ""
for i in range(0, len(watermarkInfo)):
new_array1.append(255 - ord(watermarkInfo[i])) # ord()函数以字符(长度为1的字符串)作为参数,返回对应的ASCII或Unicode
for i in range(0, len(x2)):
new_array2.append(255 - ord(x2[i]))
new_pixel1 = pixel[row, col]
new_pixel2 = pixel[(new_pixel1[0] + new_pixel1[1]), (new_pixel1[2] + new_pixel1[0])]
pix2 = int(new_pixel1[2])
pix1 = int(new_pixel1[1])
pix0 = int(new_pixel1[0])
col = imgSize[1]
row = imgSize[0]
for i in range(0, len(watermarkInfo)):
watermark_pix = new_array1.pop(0)
pixel[(pix0 + pix1), (pix2 + pix1)] = (new_pixel2[0], watermark_pix, new_pixel2[2])
pix2 = col - (new_pixel2[2] / 16) //分母"16"数值越大,则可嵌入的水印信息越多
col = pix2
pix1 = i
pix0 = row - (new_pixel2[0] / 16)
row = pix0
if (new_pixel1[2] + i) < 0:
print('Encryption intercepted')
break
if (new_pixel1[1] + i) < 0:
print('Encryption intercepted')
break
new_pixel2 = pixel[(new_pixel1[0] + i + 1), (new_pixel1[2] + 1 + i)]
pixel[imgSize[0] - 3, imgSize[1] - 3] = (0, len(watermarkInfo), len(x2))
for i in range(0, len(x2)):
watermark_pix = new_array2.pop(0)
pixel[(new_pixel1[0] + new_pixel1[1]), (new_pixel1[2] + new_pixel1[1])] = (
new_pixel2[0], watermark_pix, new_pixel2[2])
pix2 = (col) - new_pixel2[2]
col = pix2
pix1 = i
pix0 = (row) - new_pix1[0]
row = pix0
if (new_pixel1[2] + i) < 0:
print('Encryption intercepted')
if (new_pixel1[1] + i) < 0:
print('Encryption intercepted')
new_pix1 = pixel[(new_pixel1[0] + i), new_pixel1[2] + i]
img.save(outPath)
嵌入的数字水印图像通过img.save存储到新的图像中。当需要提取图像中的数字水印时,可通过提取函数来提取。如下
提取:
def watermark_extract(filePath):
img = Image.open(filePath)
imgSize = img.size
pix = img.load()
len1 = floor((imgSize[0] * imgSize[1]) / (2 * (imgSize[0] + imgSize[1])))
flag = 0
if imgSize[0] > imgSize[1]:
len2 = floor((imgSize[0] * imgSize[1]) / (len1 * (imgSize[0] - imgSize[1])))
flag = 1
elif imgSize[0] < imgSize[1]:
len2 = floor((imgSize[0] * imgSize[1]) / (len1 * (imgSize[1] - imgSize[0])))
flag = 2
else:
len2 = floor((imgSize[0] * imgSize[1]) / (len1 * (imgSize[0] + imgSize[1])))
if flag == 0 or flag == 1:
row = len1
col = len2
elif flag == 2:
row = len2
col = len1
new_pix1 = pix[row, col]
new_pix2 = pix[(new_pix1[0] + new_pix1[1]), (new_pix1[2] + new_pix1[0])]
pix2 = int(new_pix1[2])
pix1 = int(new_pix1[1])
pix0 = int(new_pix1[0])
col = imgSize[1]
row = imgSize[0]
var = pix[imgSize[0] - 3, imgSize[1] - 3]
watermarkInfo_1 = ""
watermarkInfo_2 = ""
for i in range(0, int(var[1])):
new_var = pix[(pix0 + pix1), (pix2 + pix1)]
watermarkInfo_1 += (chr(255 - (new_var[1]))) # chr() ASCII -> 对应的字符
pix2 = col - (new_pix2[2] / 16)
col = pix2
pix1 = i
pix0 = row - (new_pix2[0] / 16)
row = pix0
new_pix2 = pix[(new_pix1[0] + i + 1), (new_pix1[2] + 1 + i)]
for i in range(0, var[2]):
new_var = pix[(pix0 + pix1), (pix2 + pix1)]
watermarkInfo_2 += chr(255 - (new_var[2]))
pix2 = col - new_pix2[2]
col = pix2
pix1 = i
pix0 = row - new_pix2[0]
row = pix0
new_pix2 = pix[(new_pix1[0] + i + 1), (new_pix1[2] + 1 + i)]
if var[2] != 0:
watermarkInfo = watermarkInfo_2
print(watermarkInfo)
else:
watermarkInfo = watermarkInfo_1
print(watermarkInfo)
测试效果:
嵌入水印
python embed.py input.png python-digital-watermark out.png
提取水印
python extract.py out.png
result: python-digital-watermark
四、PDF数字水印
除了图像以外,当前PDF资源也越来越多,对与PDF资源的保护的需要也迫在眉睫,接下来我们呈现了一种PDF的数字水印的嵌入和提取,这里我们用到了一些tricks。
首先,我们需要安装PyPDF2这个PDF文档处理的库文件以及reportlab库文件中的canvas工具,我们通过canvas工具设置了一个画布,然后将水印信息隐藏在画布中;通过page.mergePage将含有水印信息的pdf页融合到需要嵌入水印的pdf文档中。具体的通过canvas工具创造数字水印新的函数如下:
def create_watermark(file_name, content):
c = canvas.Canvas(file_name, pagesize=(30 * cm, 30 * cm))
# 移动坐标原点(坐标系左下为(0,0))
c.translate(50, 600)
# 设置字体
c.setFont("Helvetica", 5)
# 指定填充颜色
c.setFillColorRGB(255, 255, 255)
# 设置透明度,1为不透明
c.setFillAlpha(0)
# 画几个文本,注意坐标系旋转的影响
c.drawString(2 * cm, 0 * cm, content)
# 关闭并保存pdf文件
c.save()
return file_name
接着,我们将嵌入的水印融合到需要嵌入水印的文档中:
嵌入:
def embed_watermark(pdf_file_in, tmp_file, watermarkInfo, pdf_file_out):
pdf_file_mark = create_watermark(tmp_file, watermarkInfo)
# 输入文件
pdf_input = PyPDF2.PdfFileReader(open(pdf_file_in, 'rb'))
# 读入水印pdf文件
pdf_watermark = PyPDF2.PdfFileReader(open(pdf_file_mark, 'rb'))
# 输出文件
pdf_output = PyPDF2.PdfFileWriter()
# 获取输入pdf文件的页数
pageNum = pdf_input.getNumPages()
for i in range(pageNum):
page = pdf_input.getPage(i)
# 将水印信息嵌入在最后一页上
if i == pageNum - 1:
page.mergePage(pdf_watermark.getPage(0))
page.compressContentStreams() # 压缩内容
pdf_output.addPage(page)
pdf_output.write(open(pdf_file_out, 'wb'))
嵌入的数字水印图像通过img.save存储到新的图像中。当需要提取图像中的数字水印时,可通过提取函数来提取。如下
提取:
def extract_watermark(file_watermarked):
# 输入文件
pdf_input = PyPDF2.PdfFileReader(open(file_watermarked, 'rb'))
pageNum = pdf_input.getNumPages()
extractedText = pdf_input.getPage(pageNum - 1).extractText()
watermarkInfo = extractedText.split()[-1]
print(watermarkInfo)
总结
本文中,我们介绍了将数字水印信息嵌入到图像和pdf文档中,基本可以满足需求,但是仍然未做到真正的数字水印的隐藏效果。在图像水印中,我们嵌入的是明文信息,若是被破解,攻击者很容易得到嵌入的水印信息并进行篡改;在pdf水印中,我们是利用了一些tricks来模拟达到了数字水印的效果;所以上述的两种方法有可取之处,但仍有弊端。
在接下来的文章中,我们将会介绍基于Arnold置换的图像数字水印嵌入和验证方法,该方法做到了更高的安全性,更完美的数字水印。