圖像水印到處可見,如微博圖片右下角會有原創者 ID 水印,很多 PDF 文件中也夾有水印,而視頻圖像同樣可以添加水印,最著名的視頻圖像水印便是電視台的標志了。圖像水印在文化產權上起到非常重要的作用,對所有者的權益起到一定的保護。
數字圖像的水印疊加公式為:f
x = (1-a)f + aw
未加水印的圖像表示為 f,水印表示為 w,常數 a 控制水印和襯底圖像的相對可見性。如果 a 為 1,則水印是不透明的,並且襯底圖像完全是暗的;隨着 a 接近 0,會逐漸看到更多的襯底圖像和更少的水印,通常 a 在 0 和 1 之間。
本篇博客整理一下如何使用 FPGA 為視頻圖像添加自定義的水印圖案。
一、水印圖像
水印圖像可以自己找一張喜歡的圖片或字符,但不要太大,否則就喧賓奪主了。如下是我本次實驗選取的水印圖案:

這是我博客的頭像,我將其分辨率改為 50x50。
二、圖像轉mif文件
網上很多圖像轉 16位 mif 文件的小軟件,當然如果你 MATLAB 學的好,也可以用 MATLAB 來實現。此次我使用的是正點原子開發的 “PicToMif_V1.0.exe”。
三、rom生成
rom的位寬設置為 16,深度設置為 2500(水印圖像分辨率),然后把第二步生成的 mif 文件包含進去即可,不添加鎖存器,讓數據輸出的延時為1個時鍾周期,rden信號也不需要,有讀地址就夠了。
四、添加水印圖案的設計
1、直接添加水印
可以單獨設計一個 .v 模塊為水印圖案模塊,這個設計代碼量小,我直接寫在頂層模塊的 TFT_driver 之后,連接管腳之前。代碼如下所示:
//========================================================================== //== TFT //========================================================================== TFT_driver u_TFT_driver ( .clk (clk_10m ), .rst_n (rst_n ), .TFT_req (rd_en ), .TFT_x (TFT_x ), .TFT_y (TFT_y ), .TFT_din (rd_data ), .TFT_clk (TFT_clk ), .TFT_de (TFT_de ), .TFT_pwm (TFT_pwm ), .TFT_hsync (TFT_hsync ), .TFT_vsync (TFT_vsync ), .TFT_data (rgb ) ); //========================================================================== //== 添加視頻水印 //========================================================================== mark_rom u_mark_rom ( .clock (clk_10m ), .address (rom_addr ), .q (rom_data ) ); //-------------------------------------------------------------------------- //水印范圍 assign rom_rden = (TFT_x >= 1) && (TFT_x <=50) && (TFT_y >= 1) && (TFT_y <=50); //rom地址 always @(posedge clk_10m or negedge rst_n) begin if(!rst_n) rom_addr <= 12'b0; else if(rom_addr==2500-1) rom_addr <= 12'b0; else if(rom_rden) rom_addr <= rom_addr + 1'b1; end //數據顯示 always @(*) begin if(rom_rden) begin TFT_data <= rom_data; //顯示水印 end else TFT_data <= rgb; //顯示原圖 end
其中 rgb 是原本輸出到管腳上的,拿來到“添加視頻水印”模塊做進一步的處理,而 TFT_x、TFT_y 信號是 TFT_driver 里就設計好的坐標信號,這里不得不又說一下,模塊設計的好,移植性就非常強!
如果你的 TFT 或 VGA 驅動模塊沒有設計坐標信號,那么在后面用 de 使能信號設計一下就行,其實就是行列規划的計數器而已。
OK來看看下效果:
設計成功!我的FPGA開發板引腳有問題,那些紅點並不是本工程的 bug 。
2、半透明水印
上面添加的水印比較生硬,可不可以美化一下呢?是可以的,我們可以讓其變得半透明,並且會隨着圖像的變換跟着變換,仿佛是融入了圖像內部一樣。
代碼如下所示:
always @(*) begin if(rom_rden) begin TFT_data <= rom_data + rgb; //顯示水印 end else TFT_data <= rgb; //顯示原圖 end
將 rom_data 和 rgb 數據相加,最后實現的效果如下所示:
......好像有點打臉?
是打臉,也不是打臉,怎么說呢?因此我的水印圖案是彩色繽紛的,和原本像素值相加后效果不好,白色的略去了,但是其他色彩也變形了。但是如果我們把水印圖案的主要符號選擇為黑色,效果就會非常好,感興趣的同學可以試試。
3、略去背景的水印圖案
如果不喜歡上面第2種效果,那我們再改進一下吧,水印圖案的輸出就不進行和原像素相加了。觀察一下我的水印圖案,背景是白色的,因此我們可以想辦法把白色背景略去,只保留中心的圖案。代碼如下所示:
always @(*) begin if(rom_rden) begin if(rom_data==16'hffff) //背景白色顯示原圖 TFT_data <= rgb; else TFT_data <= rom_data;// + rgb; //否則顯示水印 end else TFT_data <= rgb; //顯示原圖 end
對水印圖案進行判斷,如果是白色,則顯示原圖,否則顯示水印,非水印區域也顯示原圖。最后效果如下所示:
效果還是不錯的,水印圖案還是沒有選的特別好,頭發那有些白點沒有略去,那的像素並不是肉眼以為的純白,而是一些像白色的灰度數據。如果要達到最好的效果,可以好好對水印圖案進行一番選擇。
實驗整體效果如下所示:
從視頻上可以看到,最后的效果還是不錯的,可惜我的板卡有問題,很多地方有紅點,而且顏色有些失真了,此外 OV7670 攝像頭的成像效果本身也不行。不過我們關注水印就夠了,本次FPGA實現視頻圖像的水印添加實驗宣告成功!
后記
熟悉rom的同學會發現本工程的代碼時序應該再調整一下,因為ROM給出地址到出數據是有一定延遲的。但是本工程只是為了看看水印效果,要時序完美對齊也很簡單,但代碼量明顯增多,de,vsync,hsync等都得拿出來打拍。而且現在這樣效果挺完美,就這樣吧。
參考資料:[1]OpenS Lee:FPGA開源工作室(公眾號)