├── Image.png ├── README.md ├── main.py └── measure.py /Image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TyperOfCode/image_algorithm/1a649fdd156fce8e1e71d048e8acdcbf9d25eb02/Image.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # image_algorithm 2 | A Image Generator algorithm which the task is to recreate the Image.png with triangles 3 | 4 | 5 | Feel free to use and edit the code :) 6 | 7 | 8 | Replace "Image.png" with another image and start the program with `python3.6 main.py` , optimal run time is overnight or around 12-13h. 9 | 10 | 11 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from random import randint 2 | import os 3 | 4 | import numpy as np 5 | from PIL import Image, ImageDraw, ImageFile 6 | 7 | 8 | FILEPATH = os.path.abspath(__file__) 9 | FILEDIR = FILEPATH.replace(os.path.basename(FILEPATH),'') 10 | ImageFile.LOAD_TRUNCATED_IMAGES = True 11 | 12 | 13 | def getArea(x1,x2,x3, y1,y2,y3): 14 | return abs((x1*(y2-y3) + x2*(y3-y1) + x3*(y1-y2))/2) 15 | 16 | 17 | def makeTriangle(points, color, img): 18 | D = ImageDraw.Draw(img) 19 | D.polygon(points, fill=color) 20 | 21 | 22 | def score(OrgImg, ScoreImg): 23 | w1, h1 = ScoreImg.size 24 | if w1 != w or h1 != h: 25 | print("Error at Score funciton\n\n") 26 | exit() 27 | 28 | pix1=np.array(OrgImg, dtype=np.uint64).reshape((h, w, 3))[:, :, :3] 29 | pix2 = np.array(ScoreImg, dtype=np.uint64).reshape((h1, w1, 3)) 30 | 31 | return np.sqrt(np.square(pix1 - pix2).sum(axis=-1)).sum() 32 | 33 | 34 | def randPos(w, h): 35 | return (randint(0, w), randint(0, h)) 36 | 37 | 38 | def randColor(preBest=None): 39 | if not preBest: 40 | return (randint(0,255),randint(0,255),randint(0,255)) 41 | return (preBest[3][0] + randint(-200,200), preBest[3][1] + randint(-200,200),preBest[3][2] + randint(-200,200)) 42 | 43 | 44 | def drawOutlined(t, outlineC, thickness, img, name): 45 | D = ImageDraw.Draw(img) 46 | D.polygon(t[:3],fill=t[3]) 47 | D.line((t[0],t[1],t[2],t[0]),outlineC,thickness) 48 | img.save(name) 49 | 50 | 51 | #func returning copy of an image resized to desired heigth (dh) keeping the proportions 52 | def condvert(img, dh): 53 | w, h = img.size 54 | print("resizing from {}/{}".format(w,h)) 55 | 56 | hRatio = dh/h 57 | dw = int(w*hRatio) 58 | img = img.resize((dw, dh), Image.ANTIALIAS) 59 | 60 | print("resized to {}/{}".format(dw,dh)) 61 | return img 62 | 63 | 64 | def inArea(lis, img): 65 | for i in lis: 66 | try: 67 | z = img[i[0],i[1]] 68 | except: 69 | return False 70 | return True 71 | 72 | 73 | def get(w, h, img): 74 | rand1 = randPos(w, h) 75 | rand2 = (rand1[0]+randint(-100,100), rand1[1]+randint(-100,100)) 76 | rand3 = (rand2[0]+randint(-100,100), rand2[1]+randint(-100,100)) 77 | 78 | area = getArea(rand1[0],rand1[1],rand2[0],rand2[1],rand3[0],rand3[1]) 79 | 80 | if area / (w*h) * 100 > 2 or not inArea([rand1,rand2,rand3], img): 81 | return get(w, h, img) 82 | 83 | return (rand1, rand2, rand3) 84 | 85 | 86 | try: 87 | Real_IM = condvert(Image.open(FILEDIR+'Image.png', 'r'), 160) 88 | except FileNotFoundError: 89 | print('[!] You gotta put a Image.png in this directory!') 90 | exit() 91 | 92 | 93 | w, h = Real_IM.size 94 | 95 | #message below in unnessesary - prints in condvert func say the same thing 96 | print('> Converted the image to the dimensions of: %s x %s'%(w,h)) 97 | 98 | 99 | blank = Image.new('RGB', (w,h), (255,255,255)) 100 | cycles = 0 101 | generations = 0 102 | Bests = [] 103 | Scores = [] 104 | imgList = [] 105 | 106 | try: 107 | m = Image.open(FILEDIR+'best.png', 'r') 108 | if (w,h) != m.size: 109 | blank.save(FILEDIR+'best.png') 110 | blank.save(FILEDIR+'gbest.png') 111 | except FileNotFoundError: 112 | blank.save(FILEDIR+'best.png') 113 | 114 | try: 115 | Best = Image.open(FILEDIR+'best.png') 116 | m = open(FILEDIR+'gbest.png', 'r') 117 | Scores.append(score(Real_IM,Best)) 118 | except FileNotFoundError: 119 | blank.save(FILEDIR+'gbest.png') 120 | 121 | 122 | Draw = ImageDraw.Draw(Best) 123 | 124 | accuracy = 5 125 | if accuracy > 999: 126 | print('[!] The accuracy cannot be over 999!') 127 | exit() 128 | 129 | cycleRate = accuracy*2 130 | colorTry = round(accuracy/2.0) 131 | 132 | 133 | while True: 134 | RM = Image.open(FILEDIR+'best.png').load() 135 | GhostBest = Image.new('RGB', (w,h), (255,255,255)) 136 | GG = GhostBest.load() 137 | 138 | for x1 in range(w): 139 | for y1 in range(h): 140 | GG[x1,y1] = RM[x1,y1] 141 | count = 0 142 | 143 | jobs = [] 144 | print('starting gen...') 145 | for i1 in range(cycleRate): 146 | 147 | Norm = Image.open(FILEDIR+'best.png') 148 | 149 | rand = get(w,h,GG) 150 | 151 | for i2 in range(colorTry): 152 | if Bests == []: 153 | col = randColor() 154 | else: 155 | zBests = list(Bests) 156 | zBests.reverse() 157 | col = randColor(zBests[0]) 158 | 159 | makeTriangle(rand, col, Norm) 160 | imgList.append([score(Real_IM, Norm), (rand[0],rand[1],rand[2],col)]) 161 | cycles += 1 162 | count += 1 163 | 164 | b = 10**62 165 | b1 = [] 166 | 167 | for i1 in imgList: 168 | if i1[0] < b: 169 | b = i1[0] 170 | b1 = i1[1] 171 | 172 | Bests.append(b1) 173 | 174 | DrawG = ImageDraw.Draw(GhostBest) 175 | DrawG.polygon(b1[:3], fill=b1[3]) 176 | GhostBest.save(FILEDIR+'gbest.png') 177 | scoreG = score(Real_IM, GhostBest) 178 | 179 | if len(Scores) != 0: 180 | if scoreG <= Scores[len(Scores)-1]: 181 | Draw.polygon(b1[:3], fill=b1[3]) 182 | Best.save(FILEDIR+'best.png') 183 | else: 184 | Best.save(FILEDIR+'best.png') 185 | print('\n\n--------------------\n\nSkipping and undoing Error --- Image (Undo) Error: %d '%(scoreG)) 186 | continue 187 | else: 188 | Draw.polygon(b1[:3], fill=b1[3]) 189 | Best.save(FILEDIR+'best.png') 190 | 191 | drawOutlined(b1, (255,0,0), 3, GhostBest, FILEDIR+'gbest.png') 192 | GhostBest.save(FILEDIR+'gbest.png') 193 | imgList = [] 194 | generations += 1 195 | 196 | scoreR = score(Real_IM, Best) 197 | print('\n\n--------------------\n\nGeneration: %s \tCycles: %s\t Chose Best. Image error: %s'%(generations,cycles,scoreR)) 198 | Scores.append(scoreR) 199 | -------------------------------------------------------------------------------- /measure.py: -------------------------------------------------------------------------------- 1 | from time import time 2 | 3 | ''' 4 | Simple decorator measuring time of function exevution 5 | Usage: 6 | @measureTime 7 | def f(a, b, c): 8 | pass 9 | ''' 10 | 11 | def measureTime(func): 12 | def wrapped(*args, **kwargs): 13 | start = time() 14 | ret = func(*args, **kwargs) 15 | print('\033[94m', end='') 16 | print('Execution time for ' + func.__name__ + ':', '%f'%round(time()-start, 15)) 17 | print('\033[0m', end='') 18 | return ret 19 | 20 | return wrapped 21 | 22 | 23 | if __name__ == 'main': 24 | pass 25 | --------------------------------------------------------------------------------