由于计算机图像一开始使用的颜色系统,是不包含透明度通道的,因此一些比较老的游戏里面的发光特效依然沿用基于线性减淡的图层混合方法来实现。对,说的就是你,DNF。在下发链接中大佬对此进行了科普。
去黑底和线性减淡的区别。,沃特碧们的Colg,DNF地下城与勇士 - COLG社区
我之前看了上述链接很久,还是感觉对于本小白来说,如何通过RGB通道滤色确定透明度的方法没有讲明白,因此今天有空专门研究了一下。
前置知识:
设实际显示的颜色为C=(r,g,b),前景色A、背景色B。
若前景色带Alpha通道(透明度通道),则C=αA+(1-α)B,0≤α≤1。
实例:先找两个特效,一明一暗,如下 ,都是来自俄罗斯手绘特效大师Dmitry Sarkisov的作品。
下面的他主页地址,会科学上网的可以去观摩下。
https://www.artstation.com/dmitrysarkisovhttps://www.artstation.com/dmitrysarkisov
假设背景是黑色的,处理流程如下:
1、分解gif,导入图片,指定模式为RGBA,将图片转为numpy数组;
2、对于每个像素点,求RGB三个通道的最大值,该值越大,说明像素的颜色越亮。则对应的透明度应该越低,这里直接用该值作为该像素的透明度;
3、希望最后生成的带透明度的图片A,在放在黑色背景B上时,实际显示的图片C与原图片一致。即C=αA+(1-α)B,B=(0,0,0);因此A=C/α。将原图的每个像素点的RGB值除以该像素点的透明度。
效果如下:
左边为透明通道+黑色背景。右边为原图。基本达到预期,该方法甚至没有黑边。
from PIL import Image
import numpy as np
from PIL import Image
import os
def RemoveBlackBackground(input_path:str,file_name:str,output_path:str):
target_im = Image.open(os.path.join(input_path,file_name))
target_im = target_im.convert('RGBA')
target_img_array = np.array(target_im)
#透明度
target_img_Alpha = np.max(target_img_array[:,:,:3],axis=2)
target_img_array[:,:,3] = target_img_Alpha
#保证在黑色背景上时与原图一致
target_img_Alpha = target_img_Alpha/255
target_img_array[:,:,:3]/target_img_Alpha.reshape([target_img_array.shape[0],-1,1])
res_img = Image.fromarray(target_img_array)
if not file.endswith(".png"):
res_img.save(os.path.join(output_path,file_name[:-4]+".png"))
else:
res_img.save(os.path.join(output_path,file_name))
之前的链接去黑底和线性减淡的区别。,沃特碧们的Colg,DNF地下城与勇士 - COLG社区
中,提到的需要通过Gamma变换的去黑底方式这里也放出来一下
from PIL import Image
import numpy as np
from PIL import Image
import os
def RemoveBlackBackground2(input_path:str,file_name:str,output_path:str):
target_im = Image.open(os.path.join(input_path,file_name))
target_im = target_im.convert('RGBA')
target_img_array = np.array(target_im)
#透明度
target_img_Alpha = np.max(target_img_array[:,:,:3],axis=2)
#gamma变换
target_img_dis = np.power((target_img_dis/255),3)*255
target_img_array[:,:,3] = target_img_Alpha
res_img = Image.fromarray(target_img_array)
if not file.endswith(".png"):
res_img.save(os.path.join(output_path,file_name[:-4]+".png"))
else:
res_img.save(os.path.join(output_path,file_name))
效果如下:
前者的饱和度和光晕会保留得更完整,且后者会有黑边