揭祕版權保護下的視頻隱形水印算法(上篇)

視頻水印,做爲保護知識產權的重要手段,早已被大衆習慣且接受。可是這種方法仍然存在着多方面的不足。對於觀衆來講,蓋在畫面一角的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

相關文章
相關標籤/搜索