溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Python批量生成幻影坦克圖片實(shí)例代碼

發(fā)布時(shí)間:2020-10-20 08:19:04 來(lái)源:腳本之家 閱讀:388 作者:Sryml 欄目:開發(fā)技術(shù)

前言

說(shuō)到幻影坦克,我就想起紅色警戒里的……

幻影坦克(Mirage Tank),《紅色警戒2》以及《尤里的復(fù)仇》中盟軍的一款偽裝坦克,盟軍王牌坦克之一。是愛因斯坦在德國(guó)黑森林中研發(fā)的一種坦克。雖然它無(wú)法隱形,但它卻可以利用先進(jìn)的光線偏折原理可以偽裝成樹木(巖石或草叢)來(lái)隱藏自己。

在一些MOD中,幻影坦克可以選擇變換的樹木,這樣便可以和背景的樹木融合,而不會(huì)令人生疑。

額!這是從什么百科ctrl+v過(guò)來(lái)的嗎。我跟你說(shuō)個(gè)P~ UBG

不過(guò)話說(shuō)回來(lái),里面有一句說(shuō)到和背景融合,這大概就是這種圖片的原理所在了。

一些聊天軟件或網(wǎng)站總是以白色背景和黑色背景(夜間模式)顯示圖片,你在默認(rèn)的白色背景下看到一張圖(圖A),但是點(diǎn)擊放大卻變成另一張圖(圖B)。這是因?yàn)椴榭丛斍槭褂玫谋尘笆呛谏尘啊?/p>

之前在網(wǎng)上看到用PS制作幻影坦克效果圖的方法,了解到幾個(gè)圖層混合模式的公式,也錄制過(guò)PS動(dòng)作來(lái)自動(dòng)化操作。但總感覺(jué)不夠效率,作為極客嘛,當(dāng)然是要用代碼來(lái)完成這些事情。

Python批量生成幻影坦克圖片實(shí)例代碼

一、準(zhǔn)備圖片

創(chuàng)建一個(gè)文件夾Import,將你要處理的所有圖片都放到這個(gè)文件夾里

圖片的命名方式:

  • 白色背景顯示圖A、黑色背景顯示圖B這種形式的,圖B的文件名字是圖A的名字加后綴_d
    例如,圖A為1.png,圖B則為1_d.png,與之配對(duì)成為一組即可
  • 表面是白色圖片(圖A),點(diǎn)擊顯示隱藏圖片(圖B)。這里并不需要你指定一張白色圖片,不需要更改圖片名字,程序找不到與之配對(duì)的后綴_d圖片,會(huì)自動(dòng)生成白色圖片(圖A)
  • 相反的,表面看是圖片(圖A),點(diǎn)擊卻消失成純黑色(圖B)。只需要在圖片名字加后綴_black

二、Python+PIL代碼實(shí)現(xiàn)過(guò)程

Ⅰ. 初始化

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

運(yùn)行,導(dǎo)入模塊,定義變量,創(chuàng)建導(dǎo)出目錄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)

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

執(zhí)行all_img2list()

獲取當(dāng)前目錄(Import)所有文件,按名字升序排序。將后綴帶_d的圖B與圖A配對(duì)一組,白圖到原圖,原圖到黑圖的圖片也進(jìn)行相關(guān)標(biāo)記并存到一個(gè)列表。每個(gè)元組將生成一張幻影坦克圖片

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)

Ⅲ. 自動(dòng)化處理,多進(jìn)程任務(wù)分配

執(zhí)行AutoMTank()

不想讓cpu滿載運(yùn)行,進(jìn)程數(shù)量為cpu總核心減1,將列表里所有元組分成N等份集合的列表task_assign(N為進(jìn)程數(shù)量)

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))

每個(gè)進(jìn)程對(duì)接到的任務(wù)列表進(jìn)行多線程處理:FlashMakeMTank

因?yàn)槭菆D片算法處理,屬于計(jì)算密集型,線程數(shù)量不需要太多。經(jīng)過(guò)測(cè)試多線程還是有點(diǎn)效率提升的,線程數(shù)就設(shè)置為cpu核心數(shù)吧。

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

Ⅳ. 盟軍戰(zhàn)車工廠

每個(gè)線程都將它接到的任務(wù) - 圖片組丟給我們的盟軍戰(zhàn)車工廠:MakeMTank 來(lái)生產(chǎn)幻影坦克

開頭是打開圖A和圖B文件對(duì)象賦值給imgA和imgB,判斷到那些想要白圖到原圖效果的圖片,則在內(nèi)存中生成一張純白色的圖片對(duì)象賦值給imgA。原圖到黑圖則生成純黑色圖片對(duì)象賦值給imgB

別以為這戰(zhàn)車工廠看起來(lái)這么短,實(shí)際上算法都是通過(guò)調(diào)用函數(shù)獲得返回結(jié)果,解釋起來(lái)可有點(diǎn)費(fèi)勁

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) #圖像對(duì)齊

 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')

圖片對(duì)象打開完成之后呢,把它們放到一個(gè)列表里遍歷它進(jìn)行操作

首先判斷到圖片模式是否為RGBA,最后的A表示這張圖片是帶有透明通道的。而我們的幻影坦克原理就是利用的透明通道,怎能讓它來(lái)胡攪蠻纏呢,速速將它轉(zhuǎn)換為RGB模式

接著將圖像對(duì)象轉(zhuǎn)為數(shù)組,判斷這張圖片如果不是灰度模式并且還沒(méi)有去色的情況下,那就要對(duì)它進(jìn)行去色操作了。
去完色的再將它轉(zhuǎn)為灰度模式。

有些人可能對(duì)灰度和去色有什么誤解,灰度 ≠ 去色,這是重點(diǎn)。雖然它們的結(jié)果都是灰色的圖片,但是算法不一樣,呈現(xiàn)的圖片對(duì)比度也不一樣,直接轉(zhuǎn)成灰度的坦克是沒(méi)有靈魂的。RGB圖片直接轉(zhuǎn)灰度會(huì)丟失一些細(xì)節(jié),所以要對(duì)它進(jìn)行去色操作。下面的操作都是仿照PS的步驟來(lái)處理了

(1) 去色函數(shù):Desaturate

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

每個(gè)像素取其RGB顏色中最大與最小值的均數(shù)

這個(gè)函數(shù)接受一個(gè)數(shù)組參數(shù)

例如某個(gè)像素RGB值(233,50,23),計(jì)算得出 (233+23) / 2 = 128,這時(shí)候此像素點(diǎn)三個(gè)通道都是同一個(gè)值(128,128,128)
這個(gè)算法過(guò)程消耗的性能較多,像一張1000*1000的圖片就得進(jìn)行一百萬(wàn)次計(jì)算,因此我使用了numba.jit加速。

對(duì)圖片數(shù)組進(jìn)行操作,使用argsort()將所有像素的RGB值從小到大排序并返回一個(gè)索引數(shù)組。

uint8類型的值的范圍在0~255,若計(jì)算出的值不在這范圍則會(huì)拋出溢出錯(cuò)誤,因此使用了int。

我創(chuàng)建了一個(gè)灰度圖片數(shù)組data,將每一個(gè)對(duì)應(yīng)像素的均值賦值給它,相當(dāng)于去色后再轉(zhuǎn)為灰度模式。

最后返回由數(shù)組轉(zhuǎn)換成的圖片對(duì)象

@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) 圖像對(duì)齊:ImgAlign

對(duì)齊方式(列表類型兩個(gè)值)

對(duì)齊目標(biāo) 縮放圖像
ALIGN2_MAX 取最大的寬和最大的高 NO_MODIFT 不修改(縮小或僅畫布)
ALIGN2_A 圖A STRETCH 拉伸
ALIGN2_B 圖B CONSTRAINT_RATIO 約束比例

例如我要把圖A對(duì)齊到圖B且按比例縮放:mode = [ALIGN2_B, CONSTRAINT_RATIO]

這個(gè)函數(shù)接受5個(gè)參數(shù)

①當(dāng)前圖片序號(hào)(0代表圖A,1代表圖B)

②當(dāng)前圖片對(duì)象

③ - ④圖A和圖B的尺寸

⑤對(duì)齊方式

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) 明度函數(shù):Lightness

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

       0 * ratio + img * (1-ratio)

為什么是兩條公式呢,可以看到只有 255和 0的區(qū)別,一個(gè)是提高明度,一個(gè)是降低

注意,明度 ≠ 亮度,用亮度做出來(lái)的坦克是畸形的。亮度對(duì)顏色0和255不會(huì)起任何作用,任你怎么加亮度,我白是白,黑仍然是黑。這又涉及到幻影坦克效果的原理了,圖A每個(gè)像素值必須大于圖B對(duì)應(yīng)的像素值,否則將沒(méi)有透明度效果。

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

這個(gè)函數(shù)接受2個(gè)參數(shù)

①圖片對(duì)象

②明度比值(-1~1)

盡量仿照PS的算法結(jié)果,提高明度的值為向下取整,降低明度為向上取整

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)))

實(shí)際上這是圖層的不透明度混合公式,PS中,明度的實(shí)現(xiàn)就是在當(dāng)前圖層的上方創(chuàng)建一個(gè)白色或黑色圖層,然后調(diào)整其透明度即可。所以,

明度調(diào)  100% 相當(dāng)于白色圖層的不透明度為100%,顯示純白

明度調(diào) -100% 相當(dāng)于黑色圖層的不透明度為100%,顯示純黑。

看到這里,要暫停一下了。是不是感覺(jué)說(shuō)了這么多都沒(méi)有提到幻影坦克的詳細(xì)原理,是的,只有當(dāng)你理解了PS的不透明度混合公式,你才能理解后面的步驟。

(3-x) 重點(diǎn)??!推導(dǎo)幻影坦克的原理……

這里需要用到PS的幾個(gè)圖層混合模式

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

小字母o代表不透明度。想一想,把兩張圖片導(dǎo)入到PS,上面的圖層命名為imgA,下面的圖層為imgB。

當(dāng)imgA的不透明度為100%(o=1)時(shí),根據(jù)圖層混合公式得到img輸出=imgA,也就是完全顯示上層圖像。

當(dāng)imgA的不透明度為0%(o=0)時(shí),得到img輸出=imgB,完全顯示下層圖像。

當(dāng)不透明度為50%,自然就看到了A與B的混合圖像。

但是我們要將這兩張圖給整進(jìn)一張圖里,然后在類似手機(jī)QQ這種只有白色背景和黑色背景的環(huán)境下,分別顯示出imgA和imgB。聽起來(lái)有點(diǎn)抽象,不要慌,我們來(lái)列方程。假設(shè)這張最終成果圖為imgR

① ImgA = ImgR * o + 255 * (1 - o) 白色背景下
② ImgB = ImgR * o +     0 * (1 - o) 黑色背景下(點(diǎn)擊放大后)

這時(shí)候ImgR充當(dāng)上圖層(Img上)。它有一個(gè)固定不透明度o,或者說(shuō)是它的圖層蒙版(ImgO表示ImgR的蒙版),蒙版的像素值為0~255的單通道灰度色值。填充為黑色0相當(dāng)于圖層的不透明度為0%,填充為白色相當(dāng)于圖層不透明度為100%。那么這個(gè)固定不透明度 o 實(shí)際上就是 ⑨ o = ImgO / 255

而Img下就是聊天軟件中的白色背景和黑色背景兩種可能了。

現(xiàn)在來(lái)解一下方程,由②得:

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

那么現(xiàn)在,ImgB是我們已知的要在黑色背景下顯示的圖像,只要拿到ImgO就可以得出成品圖ImgR了。

(255 - ImgA) 這個(gè)是什么意思,就是PS中的反相操作啦。讓我們回到代碼操作

(4) 反相函數(shù):Invert

公式:255 - Img

即對(duì)每個(gè)像素進(jìn)行 255-像素值

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

反ImgA = Invert(ImgA )

然后這個(gè)反相后的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)

注:之前我們說(shuō)過(guò)ImgA的所有像素值必須大于ImgB。如果小于或等于,那么反相后加自身(或加比自身大的值)就是255了。因?yàn)镮mgO是成果圖ImgR的透明蒙版,ImgO=255意味著不透明度為100%,就沒(méi)有透明效果了。

接著看方程式子③ ImgR = ImgB / ImgO * 255,這便是PS的一種圖層混合模式劃分了

(6) 劃分:Divide

公式:Img下 / Img上 * 255

幾個(gè)注意的條件

①若混合色為黑色,基色非黑結(jié)果為白色、基色為黑結(jié)果為黑色(混合色是Img上,基色是Img下)

②若混合色為白色則結(jié)果為基色

③若混合色與基色相同則結(jié)果為白色

不妨可以在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:
  #如混合色為黑色,基色非黑結(jié)果為白色、基色為黑結(jié)果為黑色
  color= (b and 255 or 0,)
  elif o==255:
  #混合色為白色則結(jié)果為基色
  color=(b,)
  elif o==b:
  #混合色與基色相同則結(jié)果為白色
  color=(255,)
  else:
  color=(round((b/o)*255),)
  pxR[x,y] = color
 return imgR

調(diào)用劃分函數(shù)ImgR = Divide(ImgO, ImgB),終于,我們得到了夢(mèng)寐以求的成果圖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')

保存在導(dǎo)出文件夾。。。

個(gè)人感覺(jué)

這個(gè)腳本生成的幻影坦克與PS做的相比就猶如真假美猴王一般,說(shuō)到美猴王,我就想起……

三、完整代碼文件

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

# ---



### 圖像對(duì)齊
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:
    #如混合色為黑色,基色非黑結(jié)果為白色、基色為黑結(jié)果為黑色
    color= (b and 255 or 0,)
   elif o==255:
    #混合色為白色則結(jié)果為基色
    color=(b,)
   elif o==b:
    #混合色與基色相同則結(jié)果為白色
    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) #圖像對(duì)齊
     
  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進(jìn)入坦克:')
  if s.upper()== 'F':
   print ('少女祈禱中...')
   Fire() #開炮
   break
  elif not s:
   break

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)億速云的支持。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI