共计 3011 个字符,预计需要花费 8 分钟才能阅读完成。
视频水印,作为爱护知识产权的重要伎俩,早已被公众习惯且承受。然而这种办法依然存在着多方面的有余。对于观众来说,盖在画面一角的 logo 多少会影响到他们的参观体验。对于视频所有者来说,这种间接显示在画面上的水印也很容易被定位和攻打。一些厂家为了应答这些攻打,将水印时不时地从随机的方向插入到画面里,从而减少 delogo 的难度,但这就更进一步升高了观众的观看体验。针对这些问题,隐形水印这门技术被提出并逐步倒退了起来。接下来,本文将分高低篇为读者介绍几种次要的隐形水印技术。
水印都能藏在哪些地方
把水印加到封装构造里
首先,咱们用一个最简略的例子来引入一类齐全不会批改画面内容的隐形水印,也即封装结构层的水印。这个例子叫图种。一部分读者兴许据说过这种货色。在国内版权意识逐步遍及的期间,网络上公开可下载的无受权资源逐步被封禁,图种开始作为资源传播的伎俩小范畴流行起来。它看上去就是一张一般的 jpeg 图片,然而下载下来后,zip 解压软件竟然能对它进行解压,之后就能失去暗藏在图片背地的其余数据。其原理,就是利用了文件的封装构造来暗藏数据。说得具体一点,jpeg 格局的文件,是以一串固定数据(0xFFD9)结尾的[1],而 zip 格局的文件,也有其固定的起始码(0x04034B50)[2]。通过将一个 jpeg 文件和一个 zip 文件间接拼接,就能够产生图种。大部分图片浏览器和解压软件都有对数据首尾地位不失常的状况做兼容,所以它们能够失常解决这类文件。
图种的生成与解压
图种能够被失常显示
那么,图种毕竟只是图片,如果是视频,是否也存在利用封装构造来暗藏数据的办法呢?那可太多了。事实上,大部分视频格式都有存储从属信息的构造,咱们只有把数据藏在那里就行了。上面简略举两个例子。
flv 文件 flv 文件由一个 header 和大量的 tag 组成,这些 tag 分为三类:audio、video 和 script data。其中 script data 的格局规范容许咱们插入各种自定义数据[3],咱们能够对照官网文档来实现代码批改这个 tag 的数据。嫌麻烦的话,一些开源软件也反对在肯定水平上的批改它,比方 flvmeta:
应用 flvmeta 批改 flv 文件,退出自定义数据
理论存储在文件中的数据
H.264 码流 在 flv、mp4、mkv 等文件格式上增加数据,意味着视频必须以离线文件的模式存在。如果咱们心愿在直播或者视频通话中的视频数据上增加水印呢?这时咱们能够在更下一层,也就是视频码流层下手。可能有读者对码流这个概念不是很分明。视频数据本来只是一个个像素值,通过编码器压缩后的数据个别就称为码流或比特流(bitstream)。视频码流尽管也能够间接解码播放,然而只能失去一帧一帧的画面,不蕴含精确的工夫信息,也不蕴含给播放器跳转用的数据偏移量,更没有音频和字幕。所以它须要更上一级的封装格局提供给播放器附加信息以保障失常播放。视频编码有很多规范,当初应用最宽泛的 H.264 规范中,有一段数据叫做 SEI(补充加强信息,supplemental enhancement information),它被用来存储辅助解码与显示的信息,反对增加用户的自定义数据[4]。驰名的开源编码器 x264 就在这段数据里写了本人的版本信息以及编码参数。咱们能够参照格局规范生成本人定义的 SEI 数据,再嵌入视频码流中,从而实现隐形水印。
自定义 SEI 的语法规范
x264 生成的 SEI 数据
封装结构层的水印是所有隐形水印中运算量最小的,因为它不会对视频原始数据进行解决。然而其毛病也很显著。因为视频在被盗用时极可能被人从新编码存储,在这个过程中,当时增加在这一层的水印个别都会失落。因而这类办法仅在一些非凡的场景被应用。接下来,本文将介绍间接增加在画面内容中的隐形水印。
把水印加到像素里
通过批改像素值增加隐形水印的办法十分之多,本节仅介绍其中最简略一种办法。即间接批改 LSB 数据。所谓 LSB,指的是最低无效位(Least Significant Bit),能够认为是像素值中最无关紧要的一个比特。间接批改它对视觉影响很小。下图的十个方块,蓝色重量的像素值顺次由 246 递增至 255,相邻的两个方块相当于批改了 LSB 数据。
批改 LSB 数据较难被肉眼分辨
有了这个意识,再来做水印就很简略了。咱们先把水印数据转化成只有 0 和 1 的二值图像,而后间接写到指标图像的最低位上,这样就实现了水印的嵌入。除了能够用这种办法嵌入纯黑白的 logo,二维码也是个不错的抉择。毕竟二维码自身就有纠错的能力,能在肯定水平上避免水印被毁坏。上面咱们用 python 简略实现一下:
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)
原始图片
待增加水印
增加完水印的图片
提取出的水印
仔细的读者会发现,代码中其余图像都是应用 jpg 格局存储的,唯独增加完水印的图片代码中应用了 png 格局。这是因为最低无效位的数据十分软弱,极容易被有损压缩算法批改,导致水印无奈失常提取。而 png 格局是无损压缩格局,不会引入这个烦扰。如果把上述代码中的 png 换成 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