编辑
2024-09-04
夺巧
00
请注意,本文编写于 240 天前,最后修改于 240 天前,其中某些信息可能已经过时。

目录

原理
灰度图
彩图

存在着这样一种图片,在不同颜色的背景下(通常是黑白两色)呈现出两种截然不同的图像。由于这样的性质,这类图片在缩略图时可以显示为一张正常的图片,而点开来看又可能是另外一般光景。这类图片常常被称作“幻影坦克”,今天就来探讨一下其原理并用Python进行实现。

原理

首先要知道的是,幻影坦克通常是以png的图像格式传播的,只因为幻影坦克需要alpha通道,也就是不透明度,来完成这二象性的诡术。而其中最核心的,就是 alpha通道作用于图片的计算公式:

Color=ColorAlpha+Color(1Alpha)Color_合 = Color_前 * Alpha + Color_后 * (1 - Alpha)

其中的ColorColor_合是混合后的颜色,ColorColor_前是前景色,ColorColor_后是背景色。

而幻影坦克要和谁混合呢?自然就是查看图片的背景,也就是:

(r,g,b,a)=(1,1,1,1)(r_白,g_白,b_白,a_白)=(1,1,1,1)

(r,g,b,a)=(0,0,0,1)(r_黑,g_黑,b_黑,a_黑)=(0,0,0,1)

于是就得到了一套方程组:

{r1=rtank×atank+(1atank)g1=gtank×atank+(1atank)b1=btank×atank+(1atank)r2=rtank×atankg2=gtank×atankb2=btank×atank\begin{cases} r_1=r_{tank}\times a_{tank} + (1-a_{tank})\\ g_1=g_{tank}\times a_{tank} + (1-a_{tank})\\ b_1=b_{tank}\times a_{tank} + (1-a_{tank})\\ \\ r_2=r_{tank}\times a_{tank}\\ g_2=g_{tank}\times a_{tank}\\ b_2=b_{tank}\times a_{tank}\\ \end{cases}

其中(r1,g1,b1)(r_1,g_1,b_1)将是我们将幻影坦克在白色背景下呈现的颜色(我们称之为表图),(r2,g2,b2)(r_2,g_2,b_2)是黑色背景下呈现的颜色(成为里图)。鉴于我们的目的是将两张已有的图片“合成”为幻影坦克,因此r1,g1,b1,r2,g2,b2r_1,g_1,b_1,r_2,g_2,b_2是已知数,rtank,gtank,btank,atankr_{tank},g_{tank},b_{tank},a_{tank}是未知数。

调整方程组可得:

{atank=1r1+r2atank=1b1+b2atank=1g1+g2rtank=r2/(1r1+r2)gtank=g2/(1g1+g2)btank=b2/(1b1+b2)\begin{cases} a_{tank}=1-r_1+r_2\\ a_{tank}=1-b_1+b_2\\ a_{tank}=1-g_1+g_2\\ \\ r_{tank}=r_2/(1-r_1+r_2)\\ g_{tank}=g_2/(1-g_1+g_2)\\ b_{tank}=b_2/(1-b_1+b_2)\\ \end{cases}

rtank,gtank,btankr_{tank},g_{tank},b_{tank}虽然可以很快求得,但决定atanka_{tank}的有三个式子,显然在绝大多数情况下无解。

而这就引申出了两种处理路径

灰度图

众所周知(并不),灰度图的像素只会是从白到黑之间的过渡,不会显示别的颜色。因此r=g=br_灰=g_灰=b_灰,如果我们退一步,让表图和里图都是灰度图,也就是r1=g1=b1r_1=g_1=b_1r2=g2=b2r_2=g_2=b_2,此时我们就惊讶的发现1r1+r2=1b1+b2=1g1+g21-r_1+r_2=1-b_1+b_2=1-g_1+g_2,此时atanka_{tank}有且只有唯一解。

随后我们验证一下rtank,gtank,btank,atankr_{tank},g_{tank},b_{tank},a_{tank}的值域是否为[0,1)[0,1)

atanka_{tank}显然不会小于0,而若要小于1,需要r1>r2r_1\gt r_2

rtankr_{tank}也不会小于0,要rtank<1r_{tank}\lt 1,则r2/(1r1+r2)<1r_2/(1-r_1+r_2)\lt 1,化简得r1<1r_1\lt 1,总会满足

gtankbtankg_{tank}和b_{tank}同理

因此唯一的要求便是r1>r2r_1\gt r_2,我们通过图像变化将r1r_1映射到[0.5,1)[0.5,1)r2r_2映射到[0,0.5)[0,0.5)即可。此时表图会更亮一些,里图则更暗一些

彩图

完成了灰度图的实现,我们自然会想,能否生成彩色的幻影坦克?

其实在之前的方程组中就可以看出,想要把任意两张图片合成为彩色幻影坦克是不可能的。然而我们可以降低要求,就像刚刚把彩图转化成灰度图以及映射r1r_1r2r_2的值域一样。如果我们能做到r1g1b1r_1\approx g_1\approx b_1r2g2b2r_2\approx g_2\approx b_2,那也理应达到不错的效果。

在映射r1r_1r2r_2值域的同时,其实还会有一个副作用。由于值域被压缩,r,g,br,g,b之间的差也会等比例缩小,因此将图片的亮度调低就可以接近rgbr\approx g\approx b的效果。查阅资料可知,即使把亮度调整为原来的0.22,肉眼上感觉到的亮度也有原来的0.5。因此在处理里图时,可以通过调低亮度的方式来减小r,g,br,g,b的插值。

对于表图,我们显然不能再将其变暗,反而还要压缩值域保证其满足r1>r2r_1>r_2的要求。然而仅仅压缩值域显然不够,此时就可以再调整图片的饱和度。饱和度的计算公式较为复杂,但大体上看,饱和度越低,便越接近于灰度图,适量调低表图的饱和度,rgbr\approx g\approx b也能够做到。

如此一来,虽然有所瑕疵(视觉上表现为会出现另一侧的色块与幻影),但彩色幻影坦克也算是就此实现了。