存在着这样一种图片,在不同颜色的背景下(通常是黑白两色)呈现出两种截然不同的图像。由于这样的性质,这类图片在缩略图时可以显示为一张正常的图片,而点开来看又可能是另外一般光景。这类图片常常被称作“幻影坦克”,今天就来探讨一下其原理并用Python进行实现。
原理
首先要知道的是,幻影坦克通常是以png的图像格式传播的,只因为幻影坦克需要alpha通道,也就是不透明度,来完成这二象性的诡术。而其中最核心的,就是
alpha通道作用于图片的计算公式:
Color合=Color前∗Alpha+Color后∗(1−Alpha)
其中的Color合是混合后的颜色,Color前是前景色,Color后是背景色。
而幻影坦克要和谁混合呢?自然就是查看图片的背景,也就是:
(r白,g白,b白,a白)=(1,1,1,1) 和
(r黑,g黑,b黑,a黑)=(0,0,0,1)
于是就得到了一套方程组:
⎩⎨⎧r1=rtank×atank+(1−atank)g1=gtank×atank+(1−atank)b1=btank×atank+(1−atank)r2=rtank×atankg2=gtank×atankb2=btank×atank
其中(r1,g1,b1)将是我们将幻影坦克在白色背景下呈现的颜色(我们称之为表图),(r2,g2,b2)是黑色背景下呈现的颜色(成为里图)。鉴于我们的目的是将两张已有的图片“合成”为幻影坦克,因此r1,g1,b1,r2,g2,b2是已知数,rtank,gtank,btank,atank是未知数。
调整方程组可得:
⎩⎨⎧atank=1−r1+r2atank=1−b1+b2atank=1−g1+g2rtank=r2/(1−r1+r2)gtank=g2/(1−g1+g2)btank=b2/(1−b1+b2)
rtank,gtank,btank虽然可以很快求得,但决定atank的有三个式子,显然在绝大多数情况下无解。
而这就引申出了两种处理路径
灰度图
众所周知(并不),灰度图的像素只会是从白到黑之间的过渡,不会显示别的颜色。因此r灰=g灰=b灰,如果我们退一步,让表图和里图都是灰度图,也就是r1=g1=b1且r2=g2=b2,此时我们就惊讶的发现1−r1+r2=1−b1+b2=1−g1+g2,此时atank有且只有唯一解。
随后我们验证一下rtank,gtank,btank,atank的值域是否为[0,1)
atank显然不会小于0,而若要小于1,需要r1>r2
rtank也不会小于0,要rtank<1,则r2/(1−r1+r2)<1,化简得r1<1,总会满足
gtank和btank同理
因此唯一的要求便是r1>r2,我们通过图像变化将r1映射到[0.5,1),r2映射到[0,0.5)即可。此时表图会更亮一些,里图则更暗一些
彩图
完成了灰度图的实现,我们自然会想,能否生成彩色的幻影坦克?
其实在之前的方程组中就可以看出,想要把任意两张图片合成为彩色幻影坦克是不可能的。然而我们可以降低要求,就像刚刚把彩图转化成灰度图以及映射r1和r2的值域一样。如果我们能做到r1≈g1≈b1且r2≈g2≈b2,那也理应达到不错的效果。
在映射r1和r2值域的同时,其实还会有一个副作用。由于值域被压缩,r,g,b之间的差也会等比例缩小,因此将图片的亮度调低就可以接近r≈g≈b的效果。查阅资料可知,即使把亮度调整为原来的0.22,肉眼上感觉到的亮度也有原来的0.5。因此在处理里图时,可以通过调低亮度的方式来减小r,g,b的插值。
对于表图,我们显然不能再将其变暗,反而还要压缩值域保证其满足r1>r2的要求。然而仅仅压缩值域显然不够,此时就可以再调整图片的饱和度。饱和度的计算公式较为复杂,但大体上看,饱和度越低,便越接近于灰度图,适量调低表图的饱和度,r≈g≈b也能够做到。
如此一来,虽然有所瑕疵(视觉上表现为会出现另一侧的色块与幻影),但彩色幻影坦克也算是就此实现了。