揭秘版权保护下的视频隐形水印算法(上篇)

视频水印,做为保护知识产权的重要手段,早已被大众习惯且接受。可是这种方法仍然存在着多方面的不足。对于观众来讲,盖在画面一角的logo多少会影响到他们的观赏体验。对于视频全部者来讲,这种直接显示在画面上的水印也很容易被定位和攻击。一些厂家为了应对这些攻击,将水印时不时地从随机的方向插入到画面里,从而增长delogo的难度,但这就更进一步下降了观众的观看体验。针对这些问题,隐形水印这门技术被提出并逐渐发展了起来。接下来,本文将分上下篇为读者介绍几种主要的隐形水印技术。python

水印都能藏在哪些地方

把水印加到封装结构里

首先,咱们用一个最简单的例子来引入一类彻底不会修改画面内容的隐形水印,也即封装结构层的水印。这个例子叫图种。一部分读者也许据说过这种东西。在国内版权意识逐渐普及的时期,网络上公开可下载的无受权资源逐渐被封禁,图种开始做为资源传播的手段小范围流行起来。它看上去就是一张普通的jpeg图片,可是下载下来后,zip解压软件竟然能对它进行解压,以后就能获得隐藏在图片背后的其余数据。其原理,就是利用了文件的封装结构来隐藏数据。 说得详细一点,jpeg格式的文件,是以一串固定数据(0xFFD9)结尾的[1],而zip格式的文件,也有其固定的起始码(0x04034B50)[2]。经过将一个jpeg文件和一个zip文件直接拼接,就能够产生图种。大部分图片浏览器和解压软件都有对数据首尾位置不正常的状况作兼容,因此它们能够正常处理这类文件。算法

lenna_produce.jpg

图种的生成与解压
复制代码

lenna_show.jpg 图种能够被正常显示浏览器

那么,图种毕竟只是图片,若是是视频,是否也存在利用封装结构来隐藏数据的方法呢?那可太多了。事实上,大部分视频格式都有存储附属信息的结构,咱们只要把数据藏在那里就好了。下面简单举两个例子。markdown

flv文件 flv文件由一个header和大量的tag组成,这些tag分为三类:audio、video和script data。其中script data的格式标准容许咱们插入各类自定义数据[3],咱们能够对照官方文档来实现代码修改这个tag的数据。嫌麻烦的话,一些开源软件也支持在必定程度上的修改它,好比flvmeta:网络

flvmeta.jpg

使用flvmeta修改flv文件,加入自定义数据
复制代码

flvmeta_dump.jpg 实际存储在文件中的数据less

H.264码流 在flv、mp四、mkv等文件格式上添加数据,意味着视频必须以离线文件的形式存在。若是咱们但愿在直播或者视频通话中的视频数据上添加水印呢?这时咱们能够在更下一层,也就是视频码流层下手。可能有读者对码流这个概念不是很清楚。视频数据本来只是一个个像素值,通过编码器压缩后的数据通常就称为码流或比特流(bitstream)。视频码流虽然也能够直接解码播放,可是只能获得一帧一帧的画面,不包含准确的时间信息,也不包含给播放器跳转用的数据偏移量,更没有音频和字幕。因此它须要更上一级的封装格式提供给播放器附加信息以保证正常播放。 视频编码有不少标准,如今使用最普遍的H.264标准中,有一段数据叫作SEI(补充加强信息,supplemental enhancement information),它被用来存储辅助解码与显示的信息,支持添加用户的自定义数据[4]。著名的开源编码器x264就在这段数据里写了本身的版本信息以及编码参数。咱们能够参照格式标准生成本身定义的SEI数据,再嵌入视频码流中,从而实现隐形水印。ide

user_SEI_syntax.jpg

自定义SEI的语法标准
复制代码

x264_SEI.jpg

x264生成的SEI数据
复制代码

封装结构层的水印是全部隐形水印中运算量最小的,由于它不会对视频原始数据进行处理。可是其缺点也很明显。由于视频在被盗用时很可能被人从新编码存储,在这个过程当中,事先添加在这一层的水印通常都会丢失。所以这类方法仅在一些特殊的场景被使用。接下来,本文将介绍直接添加在画面内容中的隐形水印。编码

把水印加到像素里 经过修改像素值添加隐形水印的方法很是之多,本节仅介绍其中最简单一种方法。即直接修改LSB数据。所谓LSB,指的是最低有效位(Least Significant Bit),能够认为是像素值中最可有可无的一个比特。直接修改它对视觉影响很小。下图的十个方块,蓝色份量的像素值依次由246递增至255,相邻的两个方块至关于修改了LSB数据。spa

LSB_show.jpg

修改LSB数据较难被肉眼分辨
复制代码

有了这个认识,再来作水印就很简单了。咱们先把水印数据转化成只有0和1的二值图像,而后直接写到目标图像的最低位上,这样就完成了水印的嵌入。除了能够用这种方法嵌入纯黑白的logo,二维码也是个不错的选择。毕竟二维码自己就有纠错的能力,能在必定程度上防止水印被破坏。下面咱们用python简单实现一下:3d

`import cv2 ​

读取图像,将水印缩放至目标图像大小,并二值化

ori_img = cv2.imread('Lenna.jpg') watermark = cv2.imread('watermark.jpg') resized_watermark = cv2.resize(watermark, ori_img.shape[:2], interpolation=cv2.INTER_NEAREST) #既然是二值图像,使用最近邻方式来拉伸比较合适 binary_watermark = resized_watermark >> 7 ​

嵌入水印并保存结果

output_img = ori_img & 0xFE | binary_watermark cv2.imwrite('Lenna_with_watermark.png', output_img) ​

提取水印

img_with_watermark = cv2.imread('Lenna_with_watermark.png') extracted_watermark = ((target_img & 0x01) * 255) cv2.imwrite('extracted_watermark.jpg', extracted_watermark)

Lenna_with_watermak.png

原始图片

watermark.jpg

待添加水印

Lenna.jpg

添加完水印的图片

extracted_watermark_lossless.jpg

提取出的水印

细心的读者会发现,代码中其余图像都是使用jpg格式存储的,惟独添加完水印的图片代码中使用了png格式。这是由于最低有效位的数据很是脆弱,极容易被有损压缩算法修改,致使水印没法正常提取。而png格式是无损压缩格式,不会引入这个干扰。若是把上述代码中的png换成jpg,你会看到提取出的水印变得彻底没法辨认。

extracted_watermark_lossy.jpg

图片通过有损压缩后提取的水印

这样看来,彷佛LSB方法也不是那么保险。毕竟视频编码基本都是有损压缩,更别提被盗取的视频被从新发布时通常都会经历二次编码,LSB的损失会更加严重。因此,如今主流的隐形水印算法,大多选择变换后的数据进行处理。因为这部份内容过多,将放在下一篇中介绍。

总结 本文大体介绍了在封装层和在变换前的原始像素数据上进行处理的隐形水印嵌入方法,内容比较集中在格式标准上。在下一篇中,读者将见到更多图像处理相关的内容,包括DCT(离散余弦变换)、DWT(离散小波变换)以及SVD(奇异值分解)在隐形水印上的应用,这些方法可以大幅提升隐形水印的鲁棒性,从而在有损压缩以及人为攻击后仍能在必定程度上保证水印的内容。

参考文献 [1] CCITT Rec. T.81

[2] APPNOTE.TXT - .ZIP File Format Specification

[3] Adobe Flash Video File Format Specification

[4] Recommendation ITU-T H.264

相关文章
相关标签/搜索