图像水印到处可见,如微博图片右下角会有原创者 ID 水印,很多 PDF 文件中也夹有水印,而视频图像同样可以添加水印,最著名的视频图像水印便是电视台的标志了。图像水印在文化产权上起到非常重要的作用,对所有者的权益起到一定的保护。
数字图像的水印叠加公式为:fx = (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开源工作室(公众号)