经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Python » 查看文章
Python批量生成幻影坦克图片实例代码
来源:jb51  时间:2019/6/4 15:28:20  对本文有异议

前言

说到幻影坦克,我就想起红色警戒里的……

幻影坦克(Mirage Tank),《红色警戒2》以及《尤里的复仇》中盟军的一款伪装坦克,盟军王牌坦克之一。是爱因斯坦在德国黑森林中研发的一种坦克。虽然它无法隐形,但它却可以利用先进的光线偏折原理可以伪装成树木(岩石或草丛)来隐藏自己。

在一些MOD中,幻影坦克可以选择变换的树木,这样便可以和背景的树木融合,而不会令人生疑。

额!这是从什么百科ctrl+v过来的吗。我跟你说个P~ UBG

不过话说回来,里面有一句说到和背景融合,这大概就是这种图片的原理所在了。

一些聊天软件或网站总是以白色背景和黑色背景(夜间模式)显示图片,你在默认的白色背景下看到一张图(图A),但是点击放大却变成另一张图(图B)。这是因为查看详情使用的背景是黑色背景。

之前在网上看到用PS制作幻影坦克效果图的方法,了解到几个图层混合模式的公式,也录制过PS动作来自动化操作。但总感觉不够效率,作为极客嘛,当然是要用代码来完成这些事情。

一、准备图片

创建一个文件夹Import,将你要处理的所有图片都放到这个文件夹里

图片的命名方式:

  • 白色背景显示图A、黑色背景显示图B这种形式的,图B的文件名字是图A的名字加后缀_d
    例如,图A为1.png,图B则为1_d.png,与之配对成为一组即可
  • 表面是白色图片(图A),点击显示隐藏图片(图B)。这里并不需要你指定一张白色图片,不需要更改图片名字,程序找不到与之配对的后缀_d图片,会自动生成白色图片(图A)
  • 相反的,表面看是图片(图A),点击却消失成纯黑色(图B)。只需要在图片名字加后缀_black

二、Python+PIL代码实现过程

Ⅰ. 初始化

注:脚本文件与 Import文件夹在同一目录

运行,导入模块,定义变量,创建导出目录Export,并将工作目录切换到Import

  1. # -*- coding: utf-8 -*-
  2. # python 3.7.2
  3. # 2019/04/21 by sryml.
  4.  
  5. import os
  6. import math
  7.  
  8. from timeit import timeit
  9. from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
  10. from multiprocessing import cpu_count
  11.  
  12. #
  13. import numba as nb
  14. import numpy as np
  15.  
  16. from PIL import Image
  17.  
  18.  
  19. # ---
  20. IMPORT_FOLDER = 'Import'
  21. EXPORT_FOLDER = 'Export'
  22. IMAGE_FILES = []
  23.  
  24. #
  25. ALIGN2_A = 0
  26. ALIGN2_B = 1
  27. ALIGN2_MAX = 'max'
  28.  
  29. NO_MODIFT = 0
  30. STRETCH = 1
  31. CONSTRAINT_RATIO = 2
  32.  
  33. # ---
  34.  
  35.  
  36. if __name__ == '__main__':
  37. if not os.path.exists(EXPORT_FOLDER):
  38. os.makedirs(EXPORT_FOLDER)
  39. os.chdir(IMPORT_FOLDER)

Ⅱ. 将所有要处理的图片文件添加到列表

执行all_img2list()

获取当前目录(Import)所有文件,按名字升序排序。将后缀带_d的图B与图A配对一组,白图到原图,原图到黑图的图片也进行相关标记并存到一个列表。每个元组将生成一张幻影坦克图片

  1. def all_img2list():
  2. global IMAGE_FILES
  3. IMAGE_FILES= []
  4. Imgs = os.listdir('./')
  5. Imgs.sort(key= lambda i: os.path.splitext(i)[0])
  6.  
  7. for i in Imgs:
  8. name = os.path.splitext(i)
  9. imgB= name[0]+'_d' + name[1]
  10.  
  11. if imgB in Imgs:
  12. Imgs.remove(imgB)
  13. img_group= (i,imgB)
  14. elif name[0][-6:].lower() == '_black':
  15. img_group= (i,'_black')
  16. else:
  17. img_group= (i,None)
  18.  
  19. IMAGE_FILES.append(img_group)

Ⅲ. 自动化处理,多进程任务分配

执行AutoMTank()

不想让cpu满载运行,进程数量为cpu总核心减1,将列表里所有元组分成N等份集合的列表task_assign(N为进程数量)

  1. def AutoMTank():
  2. cpu = cpu_count()-1
  3. pool = ProcessPoolExecutor(cpu) #max_workers=4
  4. L = IMAGE_FILES
  5. F = int(len(L)/cpu)
  6. task_assign = [L[n*F:] if (n+1)==cpu else L[n*F:(n+1)*F] for n in range(cpu)]
  7. results = list(pool.map(FlashMakeMTank, task_assign))
  8.  
  9. pool.shutdown()
  10.  
  11. print ('\n%d辆幻影坦克制作完成!' % len(IMAGE_FILES))

每个进程对接到的任务列表进行多线程处理:FlashMakeMTank

因为是图片算法处理,属于计算密集型,线程数量不需要太多。经过测试多线程还是有点效率提升的,线程数就设置为cpu核心数吧。

  1. def FlashMakeMTank(task):
  2. pool = ThreadPoolExecutor(cpu_count())
  3. results = list(pool.map(MakeMTank, task))
  4. pool.shutdown()

Ⅳ. 盟军战车工厂

每个线程都将它接到的任务 - 图片组丢给我们的盟军战车工厂:MakeMTank 来生产幻影坦克

开头是打开图A和图B文件对象赋值给imgA和imgB,判断到那些想要白图到原图效果的图片,则在内存中生成一张纯白色的图片对象赋值给imgA。原图到黑图则生成纯黑色图片对象赋值给imgB

别以为这战车工厂看起来这么短,实际上算法都是通过调用函数获得返回结果,解释起来可有点费劲

  1. def MakeMTank(i_group):
  2. ratios= [0,0]
  3. align= []
  4. if not i_group[1]:
  5. imgB= Image.open(i_group[0])
  6. imgA= Image.new('L',imgB.size,(255,))
  7. elif i_group[1]=='_black':
  8. imgA= Image.open(i_group[0])
  9. imgB= Image.new('L',imgA.size,(0,))
  10. else:
  11. imgA= Image.open(i_group[0])
  12. imgB= Image.open(i_group[1])
  13. ratios= [0.5,-0.5] #明度比值
  14.  
  15. # ALIGN2_MAX(取最大的宽和最大的高) ALIGN2_A(缩放到图A) ALIGN2_B(缩放到图B)
  16. # NO_MODIFT(不修改) STRETCH(拉伸) CONSTRAINT_RATIO(约束比例)
  17. align= [ALIGN2_B, CONSTRAINT_RATIO]
  18.  
  19. A_Size,B_Size= imgA.size,imgB.size
  20. img_objs= [imgA,imgB]
  21. for n,img in enumerate(img_objs):
  22. if img.mode== 'RGBA':
  23. img= img.convert('RGB')
  24. img_array= np.array(img)
  25. if img.mode != 'L' and ( [(img_array[:,:,i]==img_array[:,:,2]).all() for i in range(2)]!= [True,True] ):
  26. img= Desaturate(img_array) #去色
  27. else:
  28. img= img.convert('L')
  29.  
  30. if align and (A_Size!=B_Size):
  31. img= ImgAlign(n,img,A_Size,B_Size,align) #图像对齐
  32.  
  33. if ratios[n]:
  34. img= Lightness(img,ratios[n]) #明度
  35. img_objs[n]= img
  36.  
  37. imgA,imgB = img_objs
  38.  
  39. imgA = Invert(imgA) #反相
  40. imgO = LinearDodge(imgA, imgB) #线性减淡(添加)
  41. imgR = Divide(imgO, imgB) #划分
  42. imgR_mask = AddMask(imgR, imgO) #添加透明蒙版
  43.  
  44. name= os.path.splitext(i_group[0])[0]
  45. imgR_mask.save('../'+EXPORT_FOLDER+'/' + name+'.png')

图片对象打开完成之后呢,把它们放到一个列表里遍历它进行操作

首先判断到图片模式是否为RGBA,最后的A表示这张图片是带有透明通道的。而我们的幻影坦克原理就是利用的透明通道,怎能让它来胡搅蛮缠呢,速速将它转换为RGB模式

接着将图像对象转为数组,判断这张图片如果不是灰度模式并且还没有去色的情况下,那就要对它进行去色操作了。
去完色的再将它转为灰度模式。

有些人可能对灰度和去色有什么误解,灰度 ≠ 去色,这是重点。虽然它们的结果都是灰色的图片,但是算法不一样,呈现的图片对比度也不一样,直接转成灰度的坦克是没有灵魂的。RGB图片直接转灰度会丢失一些细节,所以要对它进行去色操作。下面的操作都是仿照PS的步骤来处理了

(1) 去色函数:Desaturate

公式:( max(r,g,b) + min(r,g,b) ) / 2

每个像素取其RGB颜色中最大与最小值的均数

这个函数接受一个数组参数

例如某个像素RGB值(233,50,23),计算得出 (233+23) / 2 = 128,这时候此像素点三个通道都是同一个值(128,128,128)
这个算法过程消耗的性能较多,像一张1000*1000的图片就得进行一百万次计算,因此我使用了numba.jit加速。

对图片数组进行操作,使用argsort()将所有像素的RGB值从小到大排序并返回一个索引数组。

uint8类型的值的范围在0~255,若计算出的值不在这范围则会抛出溢出错误,因此使用了int。

我创建了一个灰度图片数组data,将每一个对应像素的均值赋值给它,相当于去色后再转为灰度模式。

最后返回由数组转换成的图片对象

  1. @nb.jit
  2. def Desaturate(img_array):
  3. idx_array = img_array.argsort()
  4. width = img_array.shape[1]
  5. height = img_array.shape[0]
  6. data = np.zeros((height,width),dtype=np.uint8)
  7. for x in range(height):
  8. for y in range(width):
  9. idx= idx_array[x,y]
  10. color_min= img_array[x,y, idx[0]]
  11. color_max= img_array[x,y, idx[2]]
  12. data[x,y]= round( (int(color_min) + int(color_max)) / 2 )
  13. return Image.fromarray(data)

(2) 图像对齐:ImgAlign

对齐方式(列表类型两个值)

对齐目标 缩放图像
ALIGN2_MAX 取最大的宽和最大的高 NO_MODIFT 不修改(缩小或仅画布)
ALIGN2_A 图A STRETCH 拉伸
ALIGN2_B 图B CONSTRAINT_RATIO 约束比例

例如我要把图A对齐到图B且按比例缩放:mode = [ALIGN2_B, CONSTRAINT_RATIO]

这个函数接受5个参数

①当前图片序号(0代表图A,1代表图B)

②当前图片对象

③ - ④图A和图B的尺寸

⑤对齐方式

  1. def ImgAlign(idx,img,A_Size,B_Size,mode):
  2. size= img.size
  3. old_size= (A_Size,B_Size)
  4.  
  5. if mode[0]== ALIGN2_MAX:
  6. total_size= max(A_Size[0], B_Size[0]), max(A_Size[1], B_Size[1])
  7. if size != total_size:
  8. if mode[1]== STRETCH:
  9. img= img.resize(total_size, Image.ANTIALIAS)
  10. else:
  11. new_img= Image.new('L',total_size, (255 if idx==0 else 0,))
  12. diff= (total_size[0]-size[0],total_size[1]-size[1])
  13. min_diff= min(diff[0],diff[1])
  14. if min_diff != 0 and mode[1]:
  15. idx= diff.index(min_diff)
  16. scale= total_size[idx] / size[idx]
  17. resize= [total_size[idx], round(size[1-idx]*scale)]
  18. if idx:
  19. resize.reverse()
  20. img= img.resize(resize, Image.ANTIALIAS)
  21. new_img.paste(img, [(total_size[i]-img.size[i])//2 for i in range(2)])
  22. img= new_img
  23. elif idx != mode[0]:
  24. total_size= old_size[mode[0]]
  25. if mode[1]== STRETCH:
  26. img= img.resize(total_size, Image.ANTIALIAS)
  27. else:
  28. new_img= Image.new('L',total_size, (255 if idx==0 else 0,))
  29. diff= (total_size[0]-size[0],total_size[1]-size[1])
  30. min_diff= min(diff[0],diff[1])
  31. if (min_diff > 0 and mode[1]) or (min_diff < 0):
  32. idx= diff.index(min_diff)
  33. scale= total_size[idx] / size[idx]
  34. resize= [total_size[idx], round(size[1-idx]*scale)]
  35. if idx:
  36. resize.reverse()
  37. img= img.resize(resize, Image.ANTIALIAS)
  38. new_img.paste(img, [(total_size[i]-img.size[i])//2 for i in range(2)])
  39. img= new_img
  40.  
  41. return img

(3) 明度函数:Lightness

公式:255 * ratio + img * (1-ratio)

       0 * ratio + img * (1-ratio)

为什么是两条公式呢,可以看到只有 255和 0的区别,一个是提高明度,一个是降低

注意,明度 ≠ 亮度,用亮度做出来的坦克是畸形的。亮度对颜色0和255不会起任何作用,任你怎么加亮度,我白是白,黑仍然是黑。这又涉及到幻影坦克效果的原理了,图A每个像素值必须大于图B对应的像素值,否则将没有透明度效果。

所以,最好的效果就是图A明度提高50%,图B降低50%

这个函数接受2个参数

①图片对象

②明度比值(-1~1)

尽量仿照PS的算法结果,提高明度的值为向下取整,降低明度为向上取整

  1. def Lightness(img,ratio):
  2. if ratio>0:
  3. return img.point(lambda i: int(i*(1-ratio) + 255*ratio))
  4. return img.point(lambda i: math.ceil(i*(1+ratio)))

实际上这是图层的不透明度混合公式,PS中,明度的实现就是在当前图层的上方创建一个白色或黑色图层,然后调整其透明度即可。所以,

明度调  100% 相当于白色图层的不透明度为100%,显示纯白

明度调 -100% 相当于黑色图层的不透明度为100%,显示纯黑。

看到这里,要暂停一下了。是不是感觉说了这么多都没有提到幻影坦克的详细原理,是的,只有当你理解了PS的不透明度混合公式,你才能理解后面的步骤。

(3-x) 重点!!推导幻影坦克的原理……

这里需要用到PS的几个图层混合模式

不透明度混合公式:Img输出 = Img上 * o + Img下 * (1 - o)

小字母o代表不透明度。想一想,把两张图片导入到PS,上面的图层命名为imgA,下面的图层为imgB。

当imgA的不透明度为100%(o=1)时,根据图层混合公式得到img输出=imgA,也就是完全显示上层图像。

当imgA的不透明度为0%(o=0)时,得到img输出=imgB,完全显示下层图像。

当不透明度为50%,自然就看到了A与B的混合图像。

但是我们要将这两张图给整进一张图里,然后在类似手机QQ这种只有白色背景和黑色背景的环境下,分别显示出imgA和imgB。听起来有点抽象,不要慌,我们来列方程。假设这张最终成果图为imgR

① ImgA = ImgR * o + 255 * (1 - o) 白色背景下
② ImgB = ImgR * o +     0 * (1 - o) 黑色背景下(点击放大后)

这时候ImgR充当上图层(Img上)。它有一个固定不透明度o,或者说是它的图层蒙版(ImgO表示ImgR的蒙版),蒙版的像素值为0~255的单通道灰度色值。填充为黑色0相当于图层的不透明度为0%,填充为白色相当于图层不透明度为100%。那么这个固定不透明度 o 实际上就是 ⑨ o = ImgO / 255

而Img下就是聊天软件中的白色背景和黑色背景两种可能了。

现在来解一下方程,由②得:

ImgR = ImgB / o

将⑨ o = ImgO / 255 代入得

③ ImgR = ImgB / ImgO * 255

将③和⑨代入①得:

ImgA = (ImgB / ImgO * 255) * (ImgO / 255) + 255 * (1 - ImgO / 255)

ImgA = ImgB / ImgO * ImgO / 255 * 255 + 255 * (1 - ImgO / 255)


ImgA = ImgB + 2551 - 255(ImgO / 255)

ImgA = ImgB + 255 - ImgO


④ ImgO = (255 - ImgA) + ImgB

那么现在,ImgB是我们已知的要在黑色背景下显示的图像,只要拿到ImgO就可以得出成品图ImgR了。

(255 - ImgA) 这个是什么意思,就是PS中的反相操作啦。让我们回到代码操作

(4) 反相函数:Invert

公式:255 - Img

即对每个像素进行 255-像素值

  1. def Invert(img):
  2. return img.point(lambda i: 255-i)

反ImgA = Invert(ImgA )

然后这个反相后的ImgA(反ImgA)与ImgB相加,即PS中的线性减淡模式

(5) 线性减淡(添加):LinearDodge

公式:Img上 + Img下

  1. def LinearDodge(imgA, imgB):
  2. size = imgA.size
  3. imgO = Image.new('L',size,(0,))
  4. pxA= imgA.load()
  5. pxB= imgB.load()
  6. pxO= imgO.load()
  7. for x in range(size[0]):
  8. for y in range(size[1]):
  9. pxO[x,y] = (pxA[x,y]+pxB[x,y],)
  10. return imgO

至此得到 ImgO = LinearDodge(反ImgA, ImgB)

注:之前我们说过ImgA的所有像素值必须大于ImgB。如果小于或等于,那么反相后加自身(或加比自身大的值)就是255了。因为ImgO是成果图ImgR的透明蒙版,ImgO=255意味着不透明度为100%,就没有透明效果了。

接着看方程式子③ ImgR = ImgB / ImgO * 255,这便是PS的一种图层混合模式划分了

(6) 划分:Divide

公式:Img下 / Img上 * 255

几个注意的条件

①若混合色为黑色,基色非黑结果为白色、基色为黑结果为黑色(混合色是Img上,基色是Img下)

②若混合色为白色则结果为基色

③若混合色与基色相同则结果为白色

不妨可以在PS中一试便知真假

  1. def Divide(imgO, imgB):
  2. size = imgB.size
  3. imgR = Image.new('L',size,(0,))
  4. pxB= imgB.load()
  5. pxO= imgO.load()
  6. pxR= imgR.load()
  7. for x in range(size[0]):
  8. for y in range(size[1]):
  9. o=pxO[x,y]
  10. b=pxB[x,y]
  11. if o==0:
  12. #如混合色为黑色,基色非黑结果为白色、基色为黑结果为黑色
  13. color= (b and 255 or 0,)
  14. elif o==255:
  15. #混合色为白色则结果为基色
  16. color=(b,)
  17. elif o==b:
  18. #混合色与基色相同则结果为白色
  19. color=(255,)
  20. else:
  21. color=(round((b/o)*255),)
  22. pxR[x,y] = color
  23. return imgR

调用划分函数ImgR = Divide(ImgO, ImgB),终于,我们得到了梦寐以求的成果图ImgR

但不要忘了它的不透明度,把ImgO添加为它的图层蒙版

(6) 最后:添加透明蒙版并保存

  1. def AddMask(imgR,imgO):
  2. img = imgR.convert("RGBA")
  3. img.putalpha(imgO)
  4. return img

imgR_mask = AddMask(imgR, imgO)

  1. name= os.path.splitext(i_group[0])[0]
  2. imgR_mask.save('../'+EXPORT_FOLDER+'/' + name+'.png')

保存在导出文件夹。。。

个人感觉

这个脚本生成的幻影坦克与PS做的相比就犹如真假美猴王一般,说到美猴王,我就想起……

三、完整代码文件

MirageTank.py

  1. # -*- coding: utf-8 -*-
  2. # python 3.7.2
  3. # 2019/04/21 by sryml.
  4.  
  5. import os
  6. import math
  7.  
  8. from timeit import timeit
  9. from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
  10. from multiprocessing import cpu_count
  11.  
  12. #
  13. import numba as nb
  14. import numpy as np
  15.  
  16. from PIL import Image
  17.  
  18.  
  19. # ---
  20. IMPORT_FOLDER = 'Import'
  21. EXPORT_FOLDER = 'Export'
  22. IMAGE_FILES = []
  23.  
  24. #
  25. ALIGN2_A = 0
  26. ALIGN2_B = 1
  27. ALIGN2_MAX = 'max'
  28.  
  29. NO_MODIFT = 0
  30. STRETCH = 1
  31. CONSTRAINT_RATIO = 2
  32.  
  33. # ---
  34.  
  35.  
  36.  
  37. ### 图像对齐
  38. def ImgAlign(idx,img,A_Size,B_Size,mode):
  39. size= img.size
  40. old_size= (A_Size,B_Size)
  41.  
  42. if mode[0]== ALIGN2_MAX:
  43. total_size= max(A_Size[0], B_Size[0]), max(A_Size[1], B_Size[1])
  44. if size != total_size:
  45. if mode[1]== STRETCH:
  46. img= img.resize(total_size, Image.ANTIALIAS)
  47. else:
  48. new_img= Image.new('L',total_size, (255 if idx==0 else 0,))
  49. diff= (total_size[0]-size[0],total_size[1]-size[1])
  50. min_diff= min(diff[0],diff[1])
  51. if min_diff != 0 and mode[1]:
  52. idx= diff.index(min_diff)
  53. scale= total_size[idx] / size[idx]
  54. resize= [total_size[idx], round(size[1-idx]*scale)]
  55. if idx:
  56. resize.reverse()
  57. img= img.resize(resize, Image.ANTIALIAS)
  58. new_img.paste(img, [(total_size[i]-img.size[i])//2 for i in range(2)])
  59. img= new_img
  60. elif idx != mode[0]:
  61. total_size= old_size[mode[0]]
  62. if mode[1]== STRETCH:
  63. img= img.resize(total_size, Image.ANTIALIAS)
  64. else:
  65. new_img= Image.new('L',total_size, (255 if idx==0 else 0,))
  66. diff= (total_size[0]-size[0],total_size[1]-size[1])
  67. min_diff= min(diff[0],diff[1])
  68. if (min_diff > 0 and mode[1]) or (min_diff < 0):
  69. idx= diff.index(min_diff)
  70. scale= total_size[idx] / size[idx]
  71. resize= [total_size[idx], round(size[1-idx]*scale)]
  72. if idx:
  73. resize.reverse()
  74. img= img.resize(resize, Image.ANTIALIAS)
  75. new_img.paste(img, [(total_size[i]-img.size[i])//2 for i in range(2)])
  76. img= new_img
  77. return img
  78.  
  79.  
  80. ### 去色
  81. @nb.jit
  82. def Desaturate(img_array):
  83. idx_array = img_array.argsort()
  84. width = img_array.shape[1]
  85. height = img_array.shape[0]
  86. data = np.zeros((height,width),dtype=np.uint8)
  87. for x in range(height):
  88. for y in range(width):
  89. idx= idx_array[x,y]
  90. color_min= img_array[x,y, idx[0]]
  91. color_max= img_array[x,y, idx[2]]
  92. data[x,y]= round( (int(color_min) + int(color_max)) / 2 )
  93. return Image.fromarray(data)
  94.  
  95. ### 明度
  96. def Lightness(img,ratio):
  97. if ratio>0:
  98. return img.point(lambda i: int(i*(1-ratio) + 255*ratio))
  99. return img.point(lambda i: math.ceil(i*(1+ratio)))
  100.  
  101. ### 反相
  102. def Invert(img):
  103. return img.point(lambda i: 255-i)
  104.  
  105. ### 线性减淡(添加)
  106. def LinearDodge(imgA, imgB):
  107. size = imgA.size
  108. imgO = Image.new('L',size,(0,))
  109. pxA= imgA.load()
  110. pxB= imgB.load()
  111. pxO= imgO.load()
  112. for x in range(size[0]):
  113. for y in range(size[1]):
  114. pxO[x,y] = (pxA[x,y]+pxB[x,y],)
  115. return imgO
  116.  
  117. ### 划分
  118. def Divide(imgO, imgB):
  119. size = imgB.size
  120. imgR = Image.new('L',size,(0,))
  121. pxB= imgB.load()
  122. pxO= imgO.load()
  123. pxR= imgR.load()
  124. for x in range(size[0]):
  125. for y in range(size[1]):
  126. o=pxO[x,y]
  127. b=pxB[x,y]
  128. if o==0:
  129. #如混合色为黑色,基色非黑结果为白色、基色为黑结果为黑色
  130. color= (b and 255 or 0,)
  131. elif o==255:
  132. #混合色为白色则结果为基色
  133. color=(b,)
  134. elif o==b:
  135. #混合色与基色相同则结果为白色
  136. color=(255,)
  137. else:
  138. color=(round((b/o)*255),)
  139. pxR[x,y] = color
  140. return imgR
  141.  
  142. def AddMask(imgR,imgO):
  143. img = imgR.convert("RGBA")
  144. img.putalpha(imgO)
  145. return img
  146.  
  147.  
  148. ####
  149. #### 将所有要处理的图片文件添加到列表
  150. def all_img2list():
  151. global IMAGE_FILES
  152. IMAGE_FILES= []
  153. Imgs = os.listdir('./')
  154. Imgs.sort(key= lambda i: os.path.splitext(i)[0])
  155. for i in Imgs:
  156. name = os.path.splitext(i)
  157. imgB= name[0]+'_d' + name[1]
  158. if imgB in Imgs:
  159. Imgs.remove(imgB)
  160. img_group= (i,imgB)
  161. elif name[0][-6:].lower() == '_black':
  162. img_group= (i,'_black')
  163. else:
  164. img_group= (i,None)
  165. IMAGE_FILES.append(img_group)
  166.  
  167. def MakeMTank(i_group):
  168. ratios= [0,0]
  169. align= []
  170. if not i_group[1]:
  171. imgB= Image.open(i_group[0])
  172. imgA= Image.new('L',imgB.size,(255,))
  173. elif i_group[1]=='_black':
  174. imgA= Image.open(i_group[0])
  175. imgB= Image.new('L',imgA.size,(0,))
  176. else:
  177. imgA= Image.open(i_group[0])
  178. imgB= Image.open(i_group[1])
  179. ratios= [0.5,-0.5] #明度比值
  180. # ALIGN2_MAX(取最大的宽和最大的高) ALIGN2_A(缩放到图A) ALIGN2_B(缩放到图B)
  181. # NO_MODIFT(不修改) STRETCH(拉伸) CONSTRAINT_RATIO(约束比例)
  182. align= [ALIGN2_B, CONSTRAINT_RATIO]
  183. A_Size,B_Size= imgA.size,imgB.size
  184. img_objs= [imgA,imgB]
  185. for n,img in enumerate(img_objs):
  186. if img.mode== 'RGBA':
  187. img= img.convert('RGB')
  188. img_array= np.array(img)
  189. if img.mode != 'L' and ( [(img_array[:,:,i]==img_array[:,:,2]).all() for i in range(2)]!= [True,True] ):
  190. img= Desaturate(img_array) #去色
  191. else:
  192. img= img.convert('L')
  193. if align and (A_Size!=B_Size):
  194. img= ImgAlign(n,img,A_Size,B_Size,align) #图像对齐
  195. if ratios[n]:
  196. img= Lightness(img,ratios[n]) #明度
  197. img_objs[n]= img
  198. imgA,imgB = img_objs
  199. imgA = Invert(imgA) #反相
  200. imgO = LinearDodge(imgA, imgB) #线性减淡(添加)
  201. imgR = Divide(imgO, imgB) #划分
  202. imgR_mask = AddMask(imgR, imgO) #添加透明蒙版
  203.  
  204. name= os.path.splitext(i_group[0])[0]
  205. imgR_mask.save('../'+EXPORT_FOLDER+'/' + name+'.png')
  206.  
  207.  
  208. def FlashMakeMTank(task):
  209. pool = ThreadPoolExecutor(cpu_count())
  210. results = list(pool.map(MakeMTank, task))
  211. pool.shutdown()
  212. def AutoMTank():
  213. cpu = cpu_count()-1
  214. pool = ProcessPoolExecutor(cpu) #max_workers=4
  215. L = IMAGE_FILES
  216. F = int(len(L)/cpu)
  217. task_assign = [L[n*F:] if (n+1)==cpu else L[n*F:(n+1)*F] for n in range(cpu)]
  218. results = list(pool.map(FlashMakeMTank, task_assign))
  219.  
  220. pool.shutdown()
  221. print ('\n%d辆幻影坦克制作完成!' % len(IMAGE_FILES))
  222.  
  223. # ---
  224.  
  225. def Fire():
  226. all_img2list()
  227. sec = timeit(lambda:AutoMTank(),number=1)
  228. print ('Time used: {} sec'.format(sec))
  229. s= input('\n按回车键退出...\n')
  230.  
  231.  
  232. if __name__ == '__main__':
  233. if not os.path.exists(EXPORT_FOLDER):
  234. os.makedirs(EXPORT_FOLDER)
  235. os.chdir(IMPORT_FOLDER)
  236. while True:
  237. s= input('>>> 按F进入坦克:')
  238. if s.upper()== 'F':
  239. print ('少女祈祷中...')
  240. Fire() #开炮
  241. break
  242. elif not s:
  243. break

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对w3xue的支持。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号