實例功能:
使用256個不同色彩位置透明度的三角形相互重疊構成給定圖片
實現思路:
1.采用遺傳算法的思想,以256個三角形為一組構成一個父代,開始時隨機生成20個父代,並分別計算其環境適應度,最終選取適應度最高的作為初始父代;
2.從當前父代隨機變異產生10個子代,計算該11個個體的環境適應度,選取最高的作為新的父代;
3.重復步驟2,直到給定的迭代次數,期間每100代輸出一次當前環境適應度,並保存當前父代圖片到本地。
初始定義:
class Color() # 顏色類,模式為RGBA色彩模式(RGB三色通道及A透明度通道)
class Triangle() # 三角形類,定義三角形三頂點,顏色
def Draw_Together(triangles) # 將所有三角形繪制重疊到一張圖片中,triangles:列表,包含一個父代的所有三角形
def Mutate(rate, triangle) # 三角形變異, rate:變異率, triangle:單個三角形
def Cal_match_rate(gene_img, target_img) # 計算環境適應度(兩圖像的RGB通道值的差值平方), gene_img:生成的圖像,target_im:目標圖像
具體實現:
頭文件 中,導入了os模塊用來生成保存結果圖片的路徑;
導入了PIL模塊下的Image和ImageDraw模塊,用來打開或創建所需圖片;
導入了numpy模塊用來做科學計算,用來高效的計算環境適應度;
導入了random模塊用來初始化相關三角形,及用於其變異;
導入了gc模塊來進行垃圾回收。
具體代碼如下:
1 # -*- coding: utf-8 -*- 2 import os 3 from PIL import Image, ImageDraw 4 import numpy as np 5 import random as r 6 import gc
1 class Color(): 2 """ 3 RGBA模式,RGB三色通道,A透明度通道 4 """ 5 def __init__(self): 6 #分別隨機生成[0,255]之間的一個整數,作為RGB的值 7 #隨機生成[95, 115]之間的一個整數,作為A的值 8 self.r = r.randint(0, 255) 9 self.g = r.randint(0, 255) 10 self.b = r.randint(0, 255) 11 self.a = r.randint(95, 115)
1 class Triangle(): 2 """ 3 三角形類,定義三角形三頂點,顏色 4 """ 5 def __init__(self): 6 self.color = Color() #隨機初始化顏色值 7 self.ax = r.randint(0, 255) #隨機初始化三頂點坐標 8 self.ay = r.randint(0, 255) 9 self.bx = r.randint(0, 255) 10 self.by = r.randint(0, 255) 11 self.cx = r.randint(0, 255) 12 self.cy = r.randint(0, 255)
1 def Draw_Together(triangles): 2 """ 3 該函數負責將輸入的三角形列表合並繪制到一張圖片中。 4 triangles: 一個父代列表,包含了一個父代包含的所有由三角形類定義的三角形。 5 """ 6 img = Image.new('RGBA', size=(256, 256)) # 新建一個畫布 7 draw_img = ImageDraw.Draw(img) # 創建一個img圖像上繪圖的對象 8 draw_img.polygon([(0, 0), (0, 255), (255, 255), (255, 0)], fill=(255, 255, 255, 255)) # 繪制多邊形,此處繪制了一個覆蓋全畫布大小的白色全不透明矩形,作為背景 9 for triangle in triangles: 10 triangle_img = Image.new('RGBA', size=(256, 256)) # 新建一個畫單個三角形的畫布 11 draw_triangle = ImageDraw.Draw(triangle_img) 12 draw_triangle.polygon([(triangle.ax, triangle.ay), 13 (triangle.bx, triangle.by), 14 (triangle.cx, triangle.cy)], 15 fill=(triangle.color.r, triangle.color.g, triangle.color.b, triangle.color.a)) # 在前面定義的畫布triangle_img上繪制出指定三角形 16 img = Image.alpha_composite(img, triangle_img) # 將兩個圖片按各自透明度疊加到一張圖中 17 return img
1 def Mutate(rate, triangle): 2 """ 3 在給定變異率下,對傳入的三角形在一定幅度內隨機變異 4 """ 5 if rate > r.random(): 6 child = Triangle() 7 child.ax = max(0, min(255, triangle.ax + r.randint(-20, 20))) 8 child.ay = max(0, min(255, triangle.ay + r.randint(-20, 20))) 9 child.bx = max(0, min(255, triangle.bx + r.randint(-20, 20))) 10 child.by = max(0, min(255, triangle.by + r.randint(-20, 20))) 11 child.cx = max(0, min(255, triangle.cx + r.randint(-20, 20))) 12 child.cy = max(0, min(255, triangle.cy + r.randint(-20, 20))) 13 14 child.color.r = max(0, min(triangle.color.r + r.randint(-10, 10), 255)) 15 child.color.g = max(0, min(triangle.color.g + r.randint(-10, 10), 255)) 16 child.color.b = max(0, min(triangle.color.b + r.randint(-10, 10), 255)) 17 child.color.a = max(90, min(triangle.color.a + r.randint(-10, 10), 120)) 18 return child 19 return triangle
1 def Cal_match_tate(gene_img ,target_img): 2 """ 3 計算傳入的兩圖像的像素差值的平方和作為其生成圖像的環境適應度, 4 其值大小越小,說明兩圖像相似度高,即環境適應度越高。 5 """ 6 # 將生成圖像和目標圖像的RGB值分別合並為一個一維向量,便於計算 7 gene_pixel = np.array([]) #生成圖像的像素向量 8 for p in gene_img.split()[:-1]: # split獲得一個元組,包含RGBA四個Image對象,[:-1]用來選取前三個結果,即對象的RGB值 9 gene_pixel = np.hstack((gene_pixel, np.hstack(np.array(p)))) 10 # np.array(p)將p對象轉化為numpy類型的數組, np.hstack()可將矩陣按行合並為一維向量 11 12 target_pixel = np.array([]) 13 for p in target_img.split()[:-1]: 14 target_pixel = np.hstack((target_pixel, np.hstack(np.array(p)))) 15 16 return np.sum(np.square(np.subtract(gene_pixel, target_pixel))) # 計算環境適應度,並返回
1 def main(result_address): 2 """ 3 流程: 4 1. 隨機初始化20個圖像(每個圖像由256個三角形繪成),挑選match_rate最小的當做父代 5 2. 從父代變異10個子代, 選出父代及十個子代中match_rate最小的當做新父代 6 3. 重復步驟2,30000次 7 4. 每100代輸出一次match_rate,並保存圖像到本地 8 """ 9 TARGET = input('enter the image fullname: ') 10 target_img = Image.open(TARGET).resize((256, 256)).convert("RGBA") #導入目標圖像並改變大小為256*256,色彩模式RGBA 11 # 流程1 12 ## 初始化父代 13 parent_images = [] #儲存每個父代的合並后的圖像,便於環境適應度計算 14 parent_triangles = [] #儲存每個父代所包含的所有三角形 15 for i in range(20): 16 print('正在初始化第%d個父代...' %(i+1)) 17 parent_triangles.append([]) 18 for j in range(256): 19 parent_triangles[i].append(Triangle()) # 隨機生成一個父代中的一個三角形 20 parent_images.append(Draw_Together(parent_triangles[i])) #每當一個父代生成好后,就繪制其合並圖像,並添加到parent_images中 21 22 ## 計算match_rate,並挑選最小的當父代 23 match_rates = [] 24 for i in range(20): 25 match_rates.append(Cal_match_tate(parent_images[i], target_img)) 26 27 parent = parent_triangles[match_rates.index(min(match_rates))] # 定義最終父代為match_rate最小的那個初始化父代 28 29 del parent_images # 刪除保存所有初始化父代圖像及三角形的列表,因為后面用不到了 30 del parent_triangles 31 gc.collect() 32 33 # 流程2,3 34 mutate_rate = 0.05 35 for cir in range(30000): 36 ## 從父代變異10個子代 37 child_images = [] 38 child_triangles = [] 39 for i in range(10): 40 child_triangles.append([]) 41 for j in range(256): 42 child_triangles[i].append(Mutate(mutate_rate, parent[j])) 43 child_images.append(Draw_Together(child_triangles[i])) 44 45 ## 計算match_rate,並挑選最小的當父代 46 match_rates = [] 47 for i in range(10): 48 match_rates.append(Cal_match_tate(child_images[i], target_img)) 49 50 if Cal_match_tate(Draw_Together(parent), target_img) > min(match_rates): 51 parent = child_triangles[match_rates.index(min(match_rates))] 52 53 del child_images 54 del child_triangles 55 gc.collect() 56 57 # 流程4 58 if cir % 100 == 0: # 每迭代100代就輸出一次環境適應度值,並保存當前父代圖像到本地 59 print('第%d代的match_rate:\t%d' %(cir, Cal_match_tate(Draw_Together(parent), target_img))) 60 save_img = Draw_Together(parent) 61 save_img.save(os.path.join(result_address, '%d.png' % (cir)))
最后的 程序啟動 代碼中,沒什么說的,只是定義了保存位置。可自行調整。
1 result_address = r'C:\Users\HASEE\Desktop\results' # 設定生成的圖片保存位置為桌面的results文件夾 2 if not os.path.isdir(result_address): os.makedirs(result_address) #為保證桌面一定有該文件夾,以免報錯,此處判斷了上述路徑是否存在,若不存在就創建該路徑 3 4 if __name__ == '__main__': # 運行主函數 5 main(result_address)
最后:
因為計算時間的原因,本程序中生成的是256*256的圖片,圖片太小,可能做頭像都不夠,不過網上有許多圖像放大網站可以高清晰度方法圖片,所以問題不大。
另一個問題也是算力問題,處於時間考慮,無法迭代足夠多的次數,因此對於復雜圖片的精確擬合會很困難,而對簡單圖形文件還算可以。不過使用復雜圖片擬合出來的結果也別有另一番風味。
結果奉上:
下面依次為:原始圖片、初始隨機生成圖片、1900代迭代圖片、4050代迭代圖片
其中1900代迭代圖片經過網上工具四倍放大后,得到下面結果: