Python - 批量生成幻影坦克圖片

說到幻影坦克,我就想起紅色警惕裏的……ios

幻影坦克(Mirage Tank),《紅色警惕2》以及《尤里的復仇》中盟軍的一款假裝坦克,盟軍王牌坦克之一。是愛因斯坦在德國黑森林中研發的一種坦克。雖然它沒法隱形,但它卻能夠利用先進的光線偏折原理能夠假裝成樹木(岩石或草叢)來隱藏本身。
在一些MOD中,幻影坦克能夠選擇變換的樹木,這樣即可以和背景的樹木融合,而不會使人生疑。算法

額!這是從什麼百科ctrl+v過來的嗎。我跟你說個P~ UBG
不過話說回來,裏面有一句說到和背景融合,這大概就是這種圖片的原理所在了。
一些聊天軟件或網站老是以白色背景和黑色背景(夜間模式)顯示圖片,你在默認的白色背景下看到一張圖(圖A),可是點擊放大卻變成另外一張圖(圖B)。這是由於查看詳情使用的背景是黑色背景。數組

以前在網上看到用PS製做幻影坦克效果圖的方法,瞭解到幾個圖層混合模式的公式,也錄製過PS動做來自動化操做。但總感受不夠效率,做爲極客嘛,固然是要用代碼來完成這些事情。多線程

這個腳本生成的最終效果:
點擊放大查看,這類圖片使用手機QQ瀏覽效果最佳
幻影坦克效果app


1、準備圖片

  • 建立一個文件夾Import,將你要處理的全部圖片都放到這個文件夾裏
  • 圖片的命名方式:
    • 白色背景顯示圖A、黑色背景顯示圖B這種形式的,圖B的文件名字是圖A的名字加後綴_d
      例如,圖A爲1.png,圖B則爲1_d.png,與之配對成爲一組便可
    • 表面是白色圖片(圖A),點擊顯示隱藏圖片(圖B)。這裏並不須要你指定一張白色圖片,不須要更改圖片名字,程序找不到與之配對的後綴_d圖片,會自動生成白色圖片(圖A)
    • 相反的,表面看是圖片(圖A),點擊卻消失成純黑色(圖B)。只須要在圖片名字加後綴_black

2、Python+PIL代碼實現過程

Ⅰ. 初始化

注:腳本文件與 Import文件夾在同一目錄ide

  • 運行,導入模塊,定義變量,建立導出目錄Export,並將工做目錄切換到Import函數

    # -*- coding: utf-8 -*-
    # python 3.7.2
    # 2019/04/21 by sryml.
    
    import os
    import math
    
    from timeit import timeit
    from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
    from multiprocessing import cpu_count
    
    #
    import numba as nb
    import numpy as np
    
    from PIL import Image
    
    
    # ---
    IMPORT_FOLDER = 'Import'
    EXPORT_FOLDER = 'Export'
    IMAGE_FILES = []
    
    #
    ALIGN2_A = 0
    ALIGN2_B = 1
    ALIGN2_MAX = 'max'
    
    NO_MODIFT = 0
    STRETCH = 1
    CONSTRAINT_RATIO = 2
    
    # ---
    
    
    if __name__ == '__main__':
        if not os.path.exists(EXPORT_FOLDER):
            os.makedirs(EXPORT_FOLDER)
        os.chdir(IMPORT_FOLDER)


Ⅱ. 將全部要處理的圖片文件添加到列表

  • 執行all_img2list()
    獲取當前目錄(Import)全部文件,按名字升序排序。將後綴帶_d的圖B與圖A配對一組,白圖到原圖,原圖到黑圖的圖片也進行相關標記並存到一個列表。每一個元組將生成一張幻影坦克圖片性能

    def all_img2list():
        global IMAGE_FILES
        IMAGE_FILES= []
        Imgs = os.listdir('./')
        Imgs.sort(key= lambda i: os.path.splitext(i)[0])
    
        for i in Imgs:
            name = os.path.splitext(i)
            imgB= name[0]+'_d' + name[1]
    
            if imgB in Imgs:
                Imgs.remove(imgB)
                img_group= (i,imgB)
            elif name[0][-6:].lower() == '_black':
                img_group= (i,'_black')
            else:
                img_group= (i,None)
    
            IMAGE_FILES.append(img_group)


Ⅲ. 自動化處理,多進程任務分配

  • 執行AutoMTank()
    不想讓cpu滿載運行,進程數量爲cpu總核心減1,將列表裏全部元組分紅N等份集合的列表task_assign(N爲進程數量)測試

    def AutoMTank():
        cpu  = cpu_count()-1
        pool = ProcessPoolExecutor(cpu) #max_workers=4
        L    = IMAGE_FILES
        F    = int(len(L)/cpu)
        task_assign = [L[n*F:] if (n+1)==cpu else L[n*F:(n+1)*F] for n in range(cpu)]
        results = list(pool.map(FlashMakeMTank, task_assign))
    
        pool.shutdown()
    
        print ('\n%d輛幻影坦克製做完成!' % len(IMAGE_FILES))
  • 每一個進程對接到的任務列表進行多線程處理:FlashMakeMTank
    由於是圖片算法處理,屬於計算密集型,線程數量不須要太多。通過測試多線程仍是有點效率提高的,線程數就設置爲cpu核心數吧。

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


Ⅳ. 盟軍戰車工廠

  • 每一個線程都將它接到的任務 - 圖片組丟給咱們的盟軍戰車工廠:MakeMTank 來生產幻影坦克
  • 開頭是打開圖A和圖B文件對象賦值給imgAimgB,判斷到那些想要白圖到原圖效果的圖片,則在內存中生成一張純白色的圖片對象賦值給imgA原圖到黑圖則生成純黑色圖片對象賦值給imgB
  • 別覺得這戰車工廠看起來這麼短,實際上算法都是經過調用函數得到返回結果,解釋起來可有點費勁

    def MakeMTank(i_group):
        ratios= [0,0]
        align= []
        if not i_group[1]:
            imgB= Image.open(i_group[0])
            imgA= Image.new('L',imgB.size,(255,))
        elif i_group[1]=='_black':
            imgA= Image.open(i_group[0])
            imgB= Image.new('L',imgA.size,(0,))
        else:
            imgA= Image.open(i_group[0])
            imgB= Image.open(i_group[1])
            ratios= [0.5,-0.5] #明度比值
    
            # ALIGN2_MAX(取最大的寬和最大的高) ALIGN2_A(縮放到圖A) ALIGN2_B(縮放到圖B) 
            # NO_MODIFT(不修改)  STRETCH(拉伸)  CONSTRAINT_RATIO(約束比例)
            align= [ALIGN2_B, CONSTRAINT_RATIO]
    
        A_Size,B_Size= imgA.size,imgB.size
        img_objs= [imgA,imgB]
        for n,img in enumerate(img_objs):
            if img.mode== 'RGBA':
                img= img.convert('RGB')
            img_array= np.array(img)
            if img.mode != 'L' and ( [(img_array[:,:,i]==img_array[:,:,2]).all() for i in range(2)]!= [True,True] ):
                img= Desaturate(img_array) #去色
            else:
                img= img.convert('L')
    
            if align and (A_Size!=B_Size):
                img= ImgAlign(n,img,A_Size,B_Size,align) #圖像對齊
    
            if ratios[n]:
                img= Lightness(img,ratios[n]) #明度
            img_objs[n]= img
    
        imgA,imgB = img_objs
    
        imgA = Invert(imgA) #反相
        imgO = LinearDodge(imgA, imgB) #線性減淡(添加)
        imgR = Divide(imgO, imgB) #劃分
        imgR_mask = AddMask(imgR, imgO) #添加透明蒙版
    
        name= os.path.splitext(i_group[0])[0]
        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,將每個對應像素的均值賦值給它,至關於去色後再轉爲灰度模式。
    最後返回由數組轉換成的圖片對象

    @nb.jit
    def Desaturate(img_array):
        idx_array = img_array.argsort()
        width   = img_array.shape[1]
        height  = img_array.shape[0]
        data    = np.zeros((height,width),dtype=np.uint8)
        for x in range(height):
            for y in range(width):
                idx= idx_array[x,y]
                color_min= img_array[x,y, idx[0]]
                color_max= img_array[x,y, idx[2]]
                data[x,y]= round( (int(color_min) + int(color_max)) / 2 )
        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的尺寸
      ⑤對齊方式

    def ImgAlign(idx,img,A_Size,B_Size,mode):
        size= img.size
        old_size= (A_Size,B_Size)
    
        if mode[0]== ALIGN2_MAX:
            total_size= max(A_Size[0], B_Size[0]), max(A_Size[1], B_Size[1])
            if size != total_size:
                if mode[1]== STRETCH:
                    img= img.resize(total_size, Image.ANTIALIAS)
                else:
                    new_img= Image.new('L',total_size, (255 if idx==0 else 0,))
                    diff= (total_size[0]-size[0],total_size[1]-size[1])
                    min_diff= min(diff[0],diff[1])
                    if min_diff != 0 and mode[1]:
                        idx= diff.index(min_diff)
                        scale= total_size[idx] / size[idx]
                        resize= [total_size[idx], round(size[1-idx]*scale)]
                        if idx:
                            resize.reverse()
                        img= img.resize(resize, Image.ANTIALIAS)
                    new_img.paste(img, [(total_size[i]-img.size[i])//2 for i in range(2)])
                    img= new_img
        elif idx != mode[0]:
            total_size= old_size[mode[0]]
            if mode[1]== STRETCH:
                img= img.resize(total_size, Image.ANTIALIAS)
            else:
                new_img= Image.new('L',total_size, (255 if idx==0 else 0,))
                diff= (total_size[0]-size[0],total_size[1]-size[1])
                min_diff= min(diff[0],diff[1])
                if (min_diff > 0 and mode[1]) or (min_diff < 0):
                    idx= diff.index(min_diff)
                    scale= total_size[idx] / size[idx]
                    resize= [total_size[idx], round(size[1-idx]*scale)]
                    if idx:
                        resize.reverse()
                    img= img.resize(resize, Image.ANTIALIAS)
                new_img.paste(img, [(total_size[i]-img.size[i])//2 for i in range(2)])
                img= new_img
    
        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的算法結果,提升明度的值爲向下取整,下降明度爲向上取整

      def Lightness(img,ratio):
          if ratio>0:
              return img.point(lambda i: int(i*(1-ratio) + 255*ratio))
          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-像素值

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

      反ImgA = Invert(ImgA )
      而後這個反相後的ImgA(反ImgA)與ImgB相加,即PS中的線性減淡模式

  • (5) 線性減淡(添加):LinearDodge
    • 公式:Img上 + Img下

      def LinearDodge(imgA, imgB):
          size = imgA.size
          imgO = Image.new('L',size,(0,))
          pxA= imgA.load()
          pxB= imgB.load()
          pxO= imgO.load()
          for x in range(size[0]):
              for y in range(size[1]):
                  pxO[x,y] = (pxA[x,y]+pxB[x,y],)
          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中一試便知真假

      def Divide(imgO, imgB):
          size = imgB.size
          imgR = Image.new('L',size,(0,))
          pxB= imgB.load()
          pxO= imgO.load()
          pxR= imgR.load()
          for x in range(size[0]):
              for y in range(size[1]):
                  o=pxO[x,y]
                  b=pxB[x,y]
                  if o==0:
                      #如混合色爲黑色,基色非黑結果爲白色、基色爲黑結果爲黑色
                      color= (b and 255 or 0,)
                  elif o==255:
                      #混合色爲白色則結果爲基色
                      color=(b,)
                  elif o==b:
                      #混合色與基色相同則結果爲白色
                      color=(255,)
                  else:
                      color=(round((b/o)*255),)
                  pxR[x,y] = color
          return imgR

      調用劃分函數ImgR = Divide(ImgO, ImgB),終於,咱們獲得了求之不得的成果圖ImgR
      但不要忘了它的不透明度,把ImgO添加爲它的圖層蒙版

  • (6) 最後:添加透明蒙版並保存
    def AddMask(imgR,imgO):
        img = imgR.convert("RGBA")
        img.putalpha(imgO)
        return img

    imgR_mask = AddMask(imgR, imgO)

    name= os.path.splitext(i_group[0])[0]
    imgR_mask.save('../'+EXPORT_FOLDER+'/' + name+'.png')
    保存在導出文件夾。。。
  • 我的感受

    這個腳本生成的幻影坦克與PS作的相比就猶如真假美猴王通常,說到美猴王,我就想起……



3、完整代碼文件

  • MirageTank.py
# -*- coding: utf-8 -*-
# python 3.7.2
# 2019/04/21 by sryml.

import os
import math

from timeit import timeit
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
from multiprocessing import cpu_count

#
import numba as nb
import numpy as np

from PIL import Image


# ---
IMPORT_FOLDER = 'Import'
EXPORT_FOLDER = 'Export'
IMAGE_FILES = []

#
ALIGN2_A = 0
ALIGN2_B = 1
ALIGN2_MAX = 'max'

NO_MODIFT = 0
STRETCH = 1
CONSTRAINT_RATIO = 2

# ---




### 圖像對齊
def ImgAlign(idx,img,A_Size,B_Size,mode):
    size= img.size
    old_size= (A_Size,B_Size)

    if mode[0]== ALIGN2_MAX:
        total_size= max(A_Size[0], B_Size[0]), max(A_Size[1], B_Size[1])
        if size != total_size:
            if mode[1]== STRETCH:
                img= img.resize(total_size, Image.ANTIALIAS)
            else:
                new_img= Image.new('L',total_size, (255 if idx==0 else 0,))
                diff= (total_size[0]-size[0],total_size[1]-size[1])
                min_diff= min(diff[0],diff[1])
                if min_diff != 0 and mode[1]:
                    idx= diff.index(min_diff)
                    scale= total_size[idx] / size[idx]
                    resize= [total_size[idx], round(size[1-idx]*scale)]
                    if idx:
                        resize.reverse()
                    img= img.resize(resize, Image.ANTIALIAS)
                new_img.paste(img, [(total_size[i]-img.size[i])//2 for i in range(2)])
                img= new_img
    elif idx != mode[0]:
        total_size= old_size[mode[0]]
        if mode[1]== STRETCH:
            img= img.resize(total_size, Image.ANTIALIAS)
        else:
            new_img= Image.new('L',total_size, (255 if idx==0 else 0,))
            diff= (total_size[0]-size[0],total_size[1]-size[1])
            min_diff= min(diff[0],diff[1])
            if (min_diff > 0 and mode[1]) or (min_diff < 0):
                idx= diff.index(min_diff)
                scale= total_size[idx] / size[idx]
                resize= [total_size[idx], round(size[1-idx]*scale)]
                if idx:
                    resize.reverse()
                img= img.resize(resize, Image.ANTIALIAS)
            new_img.paste(img, [(total_size[i]-img.size[i])//2 for i in range(2)])
            img= new_img
            
    return img


### 去色
@nb.jit
def Desaturate(img_array):
    idx_array = img_array.argsort()
    width   = img_array.shape[1]
    height  = img_array.shape[0]
    data    = np.zeros((height,width),dtype=np.uint8)
    for x in range(height):
        for y in range(width):
            idx= idx_array[x,y]
            color_min= img_array[x,y, idx[0]]
            color_max= img_array[x,y, idx[2]]
            data[x,y]= round( (int(color_min) + int(color_max)) / 2 )
    return Image.fromarray(data)
                

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

    
### 反相
def Invert(img):
    return img.point(lambda i: 255-i)
    

### 線性減淡(添加)
def LinearDodge(imgA, imgB):
    size = imgA.size
    imgO = Image.new('L',size,(0,))
    pxA= imgA.load()
    pxB= imgB.load()
    pxO= imgO.load()
    for x in range(size[0]):
        for y in range(size[1]):
            pxO[x,y] = (pxA[x,y]+pxB[x,y],)
    return imgO

    
### 劃分
def Divide(imgO, imgB):
    size = imgB.size
    imgR = Image.new('L',size,(0,))
    pxB= imgB.load()
    pxO= imgO.load()
    pxR= imgR.load()
    for x in range(size[0]):
        for y in range(size[1]):
            o=pxO[x,y]
            b=pxB[x,y]
            if o==0:
                #如混合色爲黑色,基色非黑結果爲白色、基色爲黑結果爲黑色
                color= (b and 255 or 0,)
            elif o==255:
                #混合色爲白色則結果爲基色
                color=(b,)
            elif o==b:
                #混合色與基色相同則結果爲白色
                color=(255,)
            else:
                color=(round((b/o)*255),)
            pxR[x,y] = color
    return imgR

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



####
#### 將全部要處理的圖片文件添加到列表
def all_img2list():
    global IMAGE_FILES
    IMAGE_FILES= []
    Imgs = os.listdir('./')
    Imgs.sort(key= lambda i: os.path.splitext(i)[0])
    
    for i in Imgs:
        name = os.path.splitext(i)
        imgB= name[0]+'_d' + name[1]
        
        if imgB in Imgs:
            Imgs.remove(imgB)
            img_group= (i,imgB)
        elif name[0][-6:].lower() == '_black':
            img_group= (i,'_black')
        else:
            img_group= (i,None)
            
        IMAGE_FILES.append(img_group)
    

def MakeMTank(i_group):
    ratios= [0,0]
    align= []
    if not i_group[1]:
        imgB= Image.open(i_group[0])
        imgA= Image.new('L',imgB.size,(255,))
    elif i_group[1]=='_black':
        imgA= Image.open(i_group[0])
        imgB= Image.new('L',imgA.size,(0,))
    else:
        imgA= Image.open(i_group[0])
        imgB= Image.open(i_group[1])
        ratios= [0.5,-0.5] #明度比值
        
        # ALIGN2_MAX(取最大的寬和最大的高) ALIGN2_A(縮放到圖A) ALIGN2_B(縮放到圖B) 
        # NO_MODIFT(不修改)  STRETCH(拉伸)  CONSTRAINT_RATIO(約束比例)
        align= [ALIGN2_B, CONSTRAINT_RATIO]
        
    A_Size,B_Size= imgA.size,imgB.size
    img_objs= [imgA,imgB]
    for n,img in enumerate(img_objs):
        if img.mode== 'RGBA':
            img= img.convert('RGB')
        img_array= np.array(img)
        if img.mode != 'L' and ( [(img_array[:,:,i]==img_array[:,:,2]).all() for i in range(2)]!= [True,True] ):
            img= Desaturate(img_array) #去色
        else:
            img= img.convert('L')
            
        if align and (A_Size!=B_Size):
            img= ImgAlign(n,img,A_Size,B_Size,align) #圖像對齊
                    
        if ratios[n]:
            img= Lightness(img,ratios[n]) #明度
        img_objs[n]= img
        
    imgA,imgB = img_objs
    
    imgA = Invert(imgA) #反相
    imgO = LinearDodge(imgA, imgB) #線性減淡(添加)
    imgR = Divide(imgO, imgB) #劃分
    imgR_mask = AddMask(imgR, imgO) #添加透明蒙版

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


    
def FlashMakeMTank(task):
    pool = ThreadPoolExecutor(cpu_count())
    results = list(pool.map(MakeMTank, task))
    pool.shutdown()
    
        
def AutoMTank():
    cpu  = cpu_count()-1
    pool = ProcessPoolExecutor(cpu) #max_workers=4
    L    = IMAGE_FILES
    F    = int(len(L)/cpu)
    task_assign = [L[n*F:] if (n+1)==cpu else L[n*F:(n+1)*F] for n in range(cpu)]
    results = list(pool.map(FlashMakeMTank, task_assign))

    pool.shutdown()
        
    print ('\n%d輛幻影坦克製做完成!' % len(IMAGE_FILES))

        
    
# ---

def Fire():
    all_img2list()
    sec = timeit(lambda:AutoMTank(),number=1)
    print ('Time used: {} sec'.format(sec))
    s= input('\n按回車鍵退出...\n')



if __name__ == '__main__':
    if not os.path.exists(EXPORT_FOLDER):
        os.makedirs(EXPORT_FOLDER)
    os.chdir(IMPORT_FOLDER)
    
    while True:
        s= input('>>> 按F進入坦克:')
        if s.upper()== 'F':
            print ('少女祈禱中...')
            Fire() #開炮
            break
        elif not s:
            break
相關文章
相關標籤/搜索