视频水印作为保护知识产权的重要手段,早已为大众所使用和接受。但是这种方法仍然存在很多不足。对于观众来说,屏幕一角的标志多少会影响他们的观看体验。对于视频拥有者来说,这种直接显示在屏幕上的水印也很容易被定位和攻击。针对这些攻击,一些厂商不时从随机方向在画面中插入水印,从而增加了去日志的难度,但这也进一步降低了观众的观看体验。针对这些问题,隐形水印技术被提出并逐步发展起来。接下来,本文将分为两部分为读者介绍几种主要的隐形水印技术。哪里可以隐藏水印将水印添加到封装结构中首先,我们用最简单的例子来介绍一种完全不会修改图片内容的隐形水印,即封装结构层中的水印。这个例子叫做图形种类。有些读者可能听说过这种事情。在国内版权意识逐渐普及的时期,网络上公开可供下载的未授权资源逐渐被禁止,地图物种作为资源传播的一种手段开始在小范围内流行起来。看起来就是一张普通的jpeg图片,但是下载下来之后,zip解压软件其实是可以解压的,然后就可以得到隐藏在图片后面的其他数据了。原理是利用文件的封装结构来隐藏数据。更具体地说,jpeg格式的文件以一串固定数据(0xFFD9)[1]结尾,而zip格式的文件也有其固定的起始码(0x04034B50)[2]。直接拼接一个jpeg文件和一个zip文件,就可以生成地图。大部分图片浏览器和解压软件都兼容数据首尾异常位置,因此可以正常处理此类文件。图片类型的生成和解压都可以正常显示所以,图片类型毕竟只是图片。如果是视频,有没有办法使用封装结构来隐藏数据?这太多了。事实上,大多数视频格式都有存储边信息的结构,我们只需要将数据隐藏在那里即可。这里有两个简单的例子。flv文件flv文件由一个文件头和大量标签组成,这些标签分为三类:音频、视频和脚本数据。其中,脚本数据的格式标准允许我们插入各种自定义数据[3],我们可以参考官方文档修改这个标签的数据。如果嫌麻烦,一些开源软件也支持一定程度的修改,比如flvmeta:使用flvmeta修改flv文件,添加自定义数据。文件中实际存储的数据H.264码流在flv、mp4、mkv中添加数据到其他文件格式意味着视频必须作为离线文件存在。如果我们想在直播或视频通话中给视频数据加水印怎么办?这时候我们可以从下一层开始,也就是视频流层。可能有些读者对码流的概念不是很清楚。视频数据本来只是一个像素值,经过编码器压缩后的数据一般称为码流或比特流(bitstream)。视频码流虽然也可以直接解码播放,但是只能得到一帧一帧的画面,不包含准确的时间信息,也不包含播放器跳转的数据偏移量,也没有音频和字幕.所以需要更高层的封装格式,为播放器提供额外的信息,以保证正常播放。视频编码有很多标准。在应用最广泛的H.264标准中,有一段数据叫做SEI(supplementalenhancementinformation,补充增强信息),用于存储辅助解码和显示信息,支持添加用户自定义数据[4]。大名鼎鼎的开源编码器x264,在这个数据里面写了自己的版本信息和编码参数。我们可以参照格式标准生成自定义的SEI数据,然后将其嵌入到视频流中,实现隐形水印。自定义SEI语法标准x264生成的SEI数据封装结构层水印是所有隐形水印中计算量最小的,因为它不对原始视频数据进行处理。但其缺点也很明显。因为视频在被盗时很可能会被重新编码存储,所以在这个过程中,预先添加到这一层的水印一般都会丢失。因此,此类方法仅在某些特殊场景下使用。接下来,本文将介绍直接在屏幕内容中添加隐形水印。给像素添加水印通过修改像素值来添加隐形水印的方法有很多种,本节只介绍其中一种最简单的方法。即直接修改LSB数据。所谓LSB是指最低有效位(LeastSignificantBit),可以认为是像素值中的最低有效位。直接修改,视觉影响不大。下图中的十个方块中,蓝色分量的像素值从246依次增加到255,相邻的两个方块相当于修改了LSB数据。修改LSB数据,肉眼更难分辨。有了这个认识,做水印就很简单了。我们先将水印数据转化为只有0和1的二值图像,然后直接写入目标图像的最低位,这样就完成了水印的嵌入。除了用这种方式嵌入纯黑白的logo,二维码也是不错的选择。毕竟二维码本身就具有纠错能力,可以在一定程度上防止水印被破坏。我们简单用python实现一下:importcv2读取图片,将水印缩放到目标图片大小,二值化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嵌入水印并保存Resultoutput_img=ori_img&0xFE|binary_watermarkcv2.imwrite('Lenna_with_watermark.png',output_img)提取的水印img_with_watermark=cv2.imread('Lenna_with_watermark.png')extracted_watermark=((target_img&0x01)*2.imwritev(255ritev)extracted_watermark.jpg',extracted_watermark)Original待加水印的图片在加水印后的图片中提取的水印细心的读者会发现代码中其他图片存储的都是jpg格式,除了加了水印的图片代码使用的是png格式。这是因为最低有效位的数据非常脆弱,很容易被有损压缩算法修改,导致无法正常提取水印。png格式是无损压缩格式,不会引入这种干扰。如果将上述代码中的png替换为jpg,您将看到提取的水印变得完全不可读。有损压缩后的图像提取的水印从这个角度来看,似乎LSB方法并没有那么安全。毕竟视频编码基本都是有损压缩,更何况被盗的视频在重新发布的时候一般都会重新编码,LSB的丢失会更加严重。因此,目前主流的隐形水印算法大多选择变换后的数据进行处理。由于这部分内容太多,将在下一篇介绍。总结本文简单介绍了在封装层和变换前的原始像素数据上处理的隐形水印嵌入方法,内容更集中在格式标准上。在下一篇文章中,读者将看到更多与图像处理相关的内容,包括DCT(离散余弦变换)、DWT(离散小波变换)和SVD(奇异值分解)在隐形水印上的应用。不可见水印的鲁棒性,使得水印的内容在经过有损压缩和人为攻击后仍能在一定程度上得到保证。参考文献[1]CCITTRec.T.81[2]APPNOTE.TXT-.ZIP文件格式规范[3]Adob??eFlash视频文件格式规范[4]ITU-TH.264建议书
