#!/usr/bin/env python #Roll-your-own In Rainbows coverart # #Copyright (c) 2007 Andrew Walkingshaw # #Permission is hereby granted, free of charge, to any person #obtaining a copy of this software and associated documentation #files (the "Software"), to deal in the Software without #restriction, including without limitation the rights to use, #copy, modify, merge, publish, distribute, sublicense, and/or sell #copies of the Software, and to permit persons to whom the #Software is furnished to do so, subject to the following #conditions: # #The above copyright notice and this permission notice shall be #included in all copies or substantial portions of the Software. # #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, #EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES #OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND #NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT #HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, #WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING #FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR #OTHER DEALINGS IN THE SOFTWARE. # Requirements: # # This program needs at least Python 2.3 and the Python Imaging Library. Get # it from http://www.pythonware.com/products/pil/index.htm. import sys, random, Image, ImageDraw, ImageFont, ImageEnhance class Grid(object): def __init__(self, max, size, startpoints, pchange, start=None): self.size = size self.p = pchange self.max = max self.grid = [[None for x in range(size)] for y in range(size)] self.neighbours = [] # mutate start points if start == None: for idx in range(startpoints): x = int(random.random()*size) y = int(random.random()*size) self.grid[x][y] = 0 self.addneighbours(x,y) else: x,y = start self.grid[x][y] = 0 self.addneighbours(x,y) def calcneighbours(self, px, py): if px == 0: xn = [px+1] elif px == self.size-1: xn = [px-1] else: xn = [px+1, px-1] if py == 0: yn = [py+1] elif py == self.size-1: yn = [py-1] else: yn = [py+1, py-1] neighbours = [(px, y) for y in yn] + [(x, py) for x in xn] return neighbours def addneighbours(self, px, py): neighbours = self.calcneighbours(px, py) for pt in neighbours: if pt not in self.neighbours: if self.grid[pt[0]][pt[1]] is None: self.neighbours.append((pt[0],pt[1])) def mutate(self): size = self.size # pick a point px, py = random.choice(self.neighbours) # print px, py self.addneighbours(px,py) if self.grid[px][py] == None: neighbours = self.calcneighbours(px, py) nvals = set([self.grid[a[0]][a[1]] for a in neighbours if self.grid[a[0]][a[1]] is not None]) if len(nvals) == 1: col, = nvals if random.random() > (1-self.p): if col == 0: col = col+1 elif col == self.max: col = col elif random.random() > 0.5: col = col+1 else: col = col self.grid[px][py] = col elif len(nvals) == 2: col1, col2 = nvals if abs(col1-col2) == 1: # col1 or col2 if random.random() > 0.5: col = col1 else: col = col2 self.grid[px][py] = col elif len(neighbours) == 2: col = int((col1+col2)/2) self.grid[px][py] = col elif len(nvals) == 3: col1, col2, col3 = nvals if abs(col3-col1) == 2: self.grid[px][py] = col2 elif len(neighbours)==3: self.grid[px][py] = int((col1+col2+col3)/3) elif len(nvals) == 4: self.grid[px][py] = int(sum(list(nvals))/4) self.neighbours.remove((px,py)) def display(self): sys.stdout.write("\n") for line in self.grid: for char in line: if char == None: sys.stdout.write(".") else: sys.stdout.write(str(char)) sys.stdout.write("\n") def render(self, xpx, ypx, show=True, backdrop=None, colours=None, returnmask=False): if colours == None: colours = [(255,255,0), (255,127,0), (220,220,0), (0,127,0), (0,0,255), (64,0,127), (127,0,127)] img = Image.new("RGB", (int(float(xpx)/ypx)*self.size,self.size)) mask = Image.new("1", (int(float(xpx)/ypx)*self.size,self.size)) offset = int(((float(xpx)*self.size)/(ypx) - self.size)/2) + 1 canvas = ImageDraw.Draw(img) maskcanvas = ImageDraw.Draw(mask) for i in range(self.size): for j in range(self.size): col = self.grid[j][i] if col == None: fc = (0,0,0) maskcanvas.rectangle([(i+offset, j), ((i+1)+offset,(j+1))], fill=0) else: fc = colours[col] maskcanvas.rectangle([(i+offset, j), ((i+1)+offset,(j+1))], fill=1) canvas.rectangle([(i+offset, j), ((i+1)+offset,(j+1))], fill=fc) # blur the small image enhancer = ImageEnhance.Sharpness(img) img = enhancer.enhance(0) # and scale it up to the size we want img = img.transform((xpx,ypx), Image.AFFINE, ((float(ypx)/self.size)**-1,0,0,0, (float(ypx)/self.size)**-1,0)) mask = mask.transform((xpx,ypx), Image.AFFINE, ((float(ypx)/self.size)**-1,0,0,0, (float(ypx)/self.size)**-1,0)) if backdrop: img = Image.composite(img, backdrop, mask) if show == True: img.show() else: if returnmask: return img, mask else: return img def test(filename=None, format="PNG"): max = 6 size = 60 starts = 1 pchange = 0.1 steps = 3600 g = Grid(max,size,starts,pchange) count = 0 for s in xrange(steps): g.mutate() count += 1 fn = str(count) padding = 4-len(fn) fn = "0"*padding+fn img = g.render(500,371, show=False) img.save("%s%s.png" % (filename,fn), format) if __name__ == "__main__": test(filename="ir3600/")