我们知道添加水印方法是
一般情况下透明度是一个常数,即logo的各个位置的 α α 相同,本文中遇到的水印图片皆为灰白色,各颜色通道的值相同(不同也OK),假设水印为白色透明的文字,很自然可以想到利用纯黑色背景的图像获得该文字水印,可以进一步假设白色文字的颜色为255,即 W=255 W = 255 , α α (logo在水印图的占比)也就是 α=Jmax255 α = J m a x 255 ,相反,如果水印强度不一致,则可以假设一个 α α (可以选择该图中较大的灰度值除以255,不是最大的灰度值,因为图片和视频压缩产生噪声的缘故,见下文), W=Jmaxα W = J m a x α ,本文中的水印图W,alpha通道图以及消减后结果(以下简称消减图)如图6-10,
水印强度不一致的Logo,有时候仅从黑色背景下提取亮色区域是不够,还需要从白色背景下提取暗色背景。
问题:如何获取纯色背景下带有水印的图片?
从上一步处理的结果来看,logo边缘处理效果不太好,跟椒盐噪声很相似,如图11,这通常是由于制作logo时的抗锯齿效果(见下文),使得logo的边缘虚化导致。为了处理这些区域,考虑使用中值滤波修复这些区域,如果直接对整个水印区域进行中值滤波,虽然可以将水印去除干净,但信息损失量很大,使原本分辨率不高的视频变得更加模糊,如图12;另一方面,如果能够定位这些区域,仅替换这些坏的区域,则能得到一个折中的效果。
定位这些区域的简单办法是将消减图和其中值滤波图像相减,取绝对值,得到差值图,目标区域则是这些差值较大的像素,为了减少误差,可以用多个差值图求中值或均值,取一个阈值,将较小的噪声置零,得到最终差值图如图13,利用该差值图替换消减图中的像素点,获得接近原图的处理结果,如图14,如此形成了一个不甚完美的简单解决方案。
为什么消减图的边缘处理效果不好?因为制作水印图时抗锯齿处理会使我们绘制的黑白图像中存在其他颜色像素,我们得到的时不纯的水印图。什么是抗锯齿?抗锯齿就是修改图像边界的像素值,使看起来更加圆润,为什么要抗锯齿?因为显示器的像素是离散的,与真实世界不同,坚持非黑即白会让图像很难看,抗锯齿能减少这种缺陷的影响,玩过很多视频游戏的人肯定不陌生。
举个例子,假设你要制作一个logo水印图,内容是“Dove”,外面包裹一个椭圆,打开windows画图板,制作该图如图15,理想情况下会以为整个图像中除了黑色像素就是白色像素,其实不然,将图像放大三倍如图16,发现“Dove”附近有许多其他颜色的像素,而椭圆的边缘是明显的锯齿状,反过来从原图中也可以看出来,“Dove”的字母很圆润,这种对于边界的处理叫抗锯齿,许多图片编辑工具都会默认抗锯齿处理,比如在画图板中的铅笔工具没有抗锯齿,但刷子工具会做抗锯齿处理,有空可以试一下Photoshop。
若纯黑背景下水印图Logo的灰度看起来一致,在计算alpha时为什么不选择最高灰度值除以255?因为图像压缩产生了其他分量,这些分量比原来更亮或更暗。
举个例子,打开windows画图板,填充背景为纯黑,画一个无需抗锯齿的图像,选择矩形,填充选择纯色,颜色1(前景色)取黑色RGB=(0,0,0),颜色2(背景色)取接近白色的灰色RGB=(230,230,230),随手画一个矩形,如图17所示,分别另存为bmp、jpg图像,然后重新打开bmp图像另存为png图像(否则是在jpg图像的基础上另存为png),用软件查看图片几乎是一模一样的,但是bmp图像的大小是jpg的几十倍,bmp是未经压缩的原图,逐个像素保存,体积较大,而jpg和png都使用了图像压缩算法。
这里使用python的opencv和numpy工具查看像素值的详情,jpg图像除了0和230外,还有在0和230附近还有其他分量,而png是无损压缩格式,没有产生其他分量。
>>> import cv2 as cv
>>> import numpy as np
>>> img_bmp = cv.imread("imgs/image_format.bmp")
>>> img_jpg = cv.imread("imgs/image_format.jpg")
>>> img_png = cv.imread("imgs/image_format.png")
>>> np.bincount(img_bmp.reshape(-1),minlength=256)
array([24969, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
... 23行0 ...
0, 0, 0, 0, 0, 4212, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0], dtype=int64)
>>> np.bincount(img_jpg.reshape(-1),minlength=256)
array([24738, 189, 33, 6, 3, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
... 23行0 ...
0, 6, 3, 21, 309, 3816, 27, 18, 6,
3, 3, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0], dtype=int64)
>>> np.bincount(img_png.reshape(-1),minlength=256)
array([24969, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
... 23行0 ...
0, 0, 0, 0, 0, 4212, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0], dtype=int64)
请自行百度一下jpg和png的压缩算法:)。
常见的视频编码如mp4格式等,也是有损压缩的,与图像压缩一样,原来图像中的灰度分量一般都会泄露到邻近的分量上去,视频压缩中更重要的是帧间压缩,比如每10帧为一段,每段只存1个关键帧以及帧间信息,其余9帧依靠关键帧还原回来,在播放时需要持续不断地解压,需要大量的计算支持,有专门的硬件解码器会很快,软件解码器也可以,如果没有硬件和软件的支持,就打不开这种格式的视频文件。有兴趣的可以了解下H.264 (H.265)
本文源码的GitHub地址:https://github.com/ziweipolaris/watermark-removal.git
推荐参考其中shutterstock水印的去除