失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > python实现“幻影坦克”效果(点开图片是隐藏的另一张图)【详解】

python实现“幻影坦克”效果(点开图片是隐藏的另一张图)【详解】

时间:2020-11-16 22:02:56

相关推荐

python实现“幻影坦克”效果(点开图片是隐藏的另一张图)【详解】

开篇先附上实验成果。下图点击后会显示另一张图片,若不能正常显示,请另存到本地后,用QQ发送消息后打开,手机QQ效果最佳(具体原因后面会解释)。

下面我们先从原理说起,在PS中有三种算法如下:

① 线性减淡(添加):

Img输出 = Img上+ Img下;

② 划分:

Img输出 = Img下/ Img上;

③ 反相:

Img输出 = 1 – Img ;

上面这些公式是什么意思呢?首先,既然两种背景(黑色与白色)下有两种显示效果,那么必须准备两张图片,这两张图片在一个PSD里面有图层顺序上下之分,我们用Img上和 Img下区分。这里有个问题,我们现在讨论的是图像的色彩值,所谓色彩值,就是某像素的像素值除以255得到的一个大于0小于1的值。所以,我们这里就用Img代表这个色彩值,比如Img上,就代表“上层图像的色彩值”,Img输出——代表最终输出的图像的色彩值。那么毋庸置疑,黑色图像的色彩值为0,白色为1.

完了再看一个问题:两张图层,调整上层图像的不透明度opacity,从100%直到0%,画布上输出的图像肯定也经历了“Img上——混合——Img下”这样的变换。用不透明度混合公式来表达,设Img上的不透明度为O,那么:

Img输出 = Img上 * O + Img下 * (1 - O)

O为0%时,Img输出 = Img下,这时候完全显示下层图像;O为100%时,Img输出 = Img上,这时候完全显示上层图像;而O介于0~100%之间,将显示上下层的混合图像。而在QQ聊天或者贴吧这样的环境下,没有像PS里面那种混合模式的处理,我们之所以能看到不同的图像,也无非就是在黑色白色两种背景下,图像的透明度起到的作用而已,明白了这一点,那么OK——

现在,我们假设最终制作出的图像为ImgR,这ImgR在白色背景下显示图像ImgA,点击放大进入黑色背景,马上变成图像ImgB~ 再假设ImgR的不透明度为O,加上前面交代的那些基础知识,我们是不是就可以列方程了呢?来——

① 白色背景下

ImgA = ImgR * O + 1*(1 - O)

② 点击放大(黑色背景)

ImgB= ImgR * O + 0*(1 - O)

不知道这两个公式大家是不是看懂了。简单解释下:对于①,白色背景下,由前面不透明度混合公式可得:ImgR乘以其不透明度O,与白色背景混合~~ 白色的透明度显然就是1-O,白色色彩值为1,那么我们会看到A图像,即:Img输出 = ImgA.同理可得公式②.

解这两个方程式,由①-②,最终得到下面两个公式:

ImgR = imgB / O

O = 1 – ImgA + ImgB

现在,我们可以确定两个目标,一是计算图A的反向并将其与图B进行线性减淡,然后将得到的图O再与图B进行划分得到图R,然后给图R进行通道蒙版,最终结果就是我们想要的那张图片。

还有个小细节要交代,因为在白色背景下显示ImgA,黑色背景下显示ImgB~这就意味着:构成A图像的所有像素点的明度值,必将大于构成B图像的所有像素点的明度值。只有这样,在黑白背景下,不透明度才能发挥作用。现在说一下刚刚的问题,由于电脑QQ查看图片时背景有一定透明度,所以显示效果不太好,而手机QQ最为合适。

实现步骤与具体函数

下面我们来分别介绍一下上述算法的python实现:

Function0(增加对比度):

1.选取两张目标图片img1 和img2,分别变至灰度模式,并调高两者的明暗对比度。

这个步骤需要用到函数如下

def main(img1,img2):img1_g = img1.convert("L")img2_g = img2.convert("L")img1=light_degree(img1_g,0)img2=light_degree(img2_r,1)line=opposed_line(img2,img1)............def light_degree(img,i):if i>0:img = img.point(lambda i: i * 1.1) #提高图A的亮度 else:img = img.point(lambda i: i * 0.3) #降低图B的亮度return img

Function1(反相与线性减淡):

2.出于减少时间复杂度的考虑,我们同时进行反相与线性减淡的运算。由于灰度模式下图片仅有一个通道,灰度值即该点的色彩值,因此计算255-a即能完成反相操作,而计算a+b即能完成线性减淡(即色差值相加)操作。该步得到的最终图片我们命名为“line”。

用到的函数为

def opposed_line(img2, img1): imgb=img2.load()imga=img1.load()for x in range(0, img2.width, 1):for y in range(0, img2.height, 1):b=imgb[x,y] #逐点读取像素值a=imga[x,y]color=(255-b+a,) #(255-)b表示该像素点反相,(255-b)+a即为线性减淡运算if color>(220,):#此步是为了避免线性减淡过程中有灰度值超过255的情况imgb[x, y] = (160 - b + a,)else:imgb[x, y] = colorreturn img2

Function2(划分):

3.划分操作即计算a/b,当然可能出现a/b等于0,或者结果为正整数但非常小的情况,这时我们考虑到是否可以对商的结果进行放大,增加差异的显著性。该步得到的最终图片我们命名为“divided”。

def divide(img1, imgO): #传入img1=图A,img0=Function1处理结果图片imga=img1.load()imgo=imgO.load()for x in range(0, img1.width, 1):for y in range(0, img1.height, 1):A=imga[x,y] #逐点读取像素值O=imgo[x,y]if O==0: #A/O结果分布在0,1上的像素点非常多,需要通过放大差异将它们分开color=(int(A*0.3),)elif A/O>=1:color = (int(A*6.2),)else:color=(int(255*A/O),)imga[x,y] = colorreturn img1

Function3(透明蒙版):

4.前面处理的图片都是处在L模式下,为了实现透明通道蒙版,我们需要将图片转为RGBA模式。

并令divided_RGBA的透明通道等于line的灰度值。至此,最终得到的图片就实现了“幻影坦克”效果。

def final(divided,line):LINE = line.load()divided_RGBA = divided.convert("RGBA")DIV_RGBA = divided_RGBA.load()for x in range(0,line.width,1):for y in range(0, line.height, 1):DIV_RGBA[x,y] = (DIV_RGBA[x,y][0],DIV_RGBA[x,y][1],DIV_RGBA[x,y][2],LINE[x,y])

主要函数都已经介绍完毕。下面贴出我做的一个完整程序源码,然后本文就此结束。

main.py

import functions as funfrom PIL import Imageclass UI(object):def __init__(self):self.img1 = Image.open('C:\\Users\\10698\\Desktop\\project\\11.jpg') # 上层self.img2 = Image.open('C:\\Users\\10698\\Desktop\\project\\22.jpg') # 下层def to_fun(self):fun.timecost(self.img1, self.img2)if __name__ =='__main__':ui=UI()ui.to_fun()

functions.py

import timedef timecost(img1,img2):start = time.time()main(img1,img2)end = time.time()print("time cost:"+str(end-start))def main(img1,img2):img1_g = img1.convert("L")img2_g = img2.convert("L")img2_r = img2_g.resize((int(img1_g.width), int(img1_g.height))) #可调大小img1_g = img1_g.resize((int(img1_g.width), int(img1_g.height)))img1=light_degree(img1_g,0)img2=light_degree(img2_r,1)line=opposed_line(img2,img1)divided=divide(img1,line)final(divided,line)def save(img1_g,filename):img1_g.save("C:\\Users\\10698\\Desktop\\project\\"+filename+".png")def light_degree(img,i):if i>0:img = img.point(lambda i: i * 1.1)else:img = img.point(lambda i: i * 0.3)return imgdef opposed_line(img2, img1):imgb=img2.load()imga=img1.load()for x in range(0, img2.width, 1):for y in range(0, img2.height, 1):b=imgb[x,y]a=imga[x,y]color=(255-b+a,)if color>(220,):imgb[x, y] = (160 - b + a,)else:imgb[x, y] = colorreturn img2def divide(img1, imgO):imga=img1.load()imgo=imgO.load()for x in range(0, img1.width, 1):for y in range(0, img1.height, 1):A=imga[x,y]O=imgo[x,y]if O==0:color=(int(A*0.3),)elif A/O>=1:color = (int(A*6.2),)else:color=(int(255*A/O),)imga[x,y] = colorreturn img1def final(divided,line):LINE = line.load()divided_RGBA = divided.convert("RGBA")DIV_RGBA = divided_RGBA.load()line = line.convert("RGBA")for x in range(0,line.width,1):for y in range(0, line.height, 1):DIV_RGBA[x,y] = (DIV_RGBA[x,y][0],DIV_RGBA[x,y][1],DIV_RGBA[x,y][2],LINE[x,y])divided_RGBA.show()save(divided_RGBA,"final")

如果觉得《python实现“幻影坦克”效果(点开图片是隐藏的另一张图)【详解】》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。