1 #!/usr/bin/env python 2 3 from array import array 4 from itertools import combinations 5 from random import randint 6 import PIL.Image 7 import sys 8 9 def scale(v): 10 return (v + 64) / 128 11 12 def point(rgb): 13 return tuple(map(scale, rgb)) 14 15 def index(p): 16 return p[0] * 9 + p[1] * 3 + p[2] 17 18 def colour(i): 19 return (255 * (i % 2), 255 * ((i / 2) % 2), 255 * ((i / 4) % 2)) 20 21 def add(d, v): 22 d[v] = (d.has_key(v) and d[v] or 0) + 1 23 24 def by_frequency(d): 25 l = [(f, t) for (t, f) in d.items()] 26 l.sort(reverse=True) 27 return [i[1] for i in l] 28 29 def match(b, bases): 30 return b in bases and b 31 32 def match_lighter(b, bases): 33 return upward[b] in bases and upward[b] 34 35 def match_darker(b, bases): 36 return downward[b] in bases and downward[b] 37 38 def neutral(bases, light): 39 l = ["W", "C", "Y", "M", "G", "R", "B", "_"] 40 if not light: 41 l.reverse() 42 for b in l: 43 if b in bases: 44 return b 45 return bases[randint(0, 3)] 46 47 tones = [ 48 "__", "_B", "BB", # 00x 49 "_G", "_C", "BC", # 01x 50 "GG", "GC", "CC", # 02x 51 "_R", "_M", "BM", # 10x 52 "_Y", "**", "WB", # 11x 53 "GY", "WG", "WC", # 12x 54 "RR", "RM", "MM", # 20x 55 "RY", "WR", "WM", # 21x 56 "YY", "WY", "WW", # 22x 57 ] 58 59 colours = ["_", "R", "G", "Y", "B", "M", "C", "W"] 60 61 upward = { 62 "_" : ["R", "G", "B"], 63 "R" : ["Y", "M"], 64 "G" : ["Y", "C"], 65 "B" : ["C", "M"], 66 "Y" : ["W"], 67 "C" : ["W"], 68 "M" : ["W"], 69 "W" : ["W"], 70 "*" : ["W", "Y", "C", "M"], 71 } 72 73 downward = { 74 "_" : ["_"], 75 "R" : ["_"], 76 "G" : ["_"], 77 "B" : ["_"], 78 "Y" : ["R", "G"], 79 "C" : ["G", "B"], 80 "M" : ["R", "B"], 81 "W" : ["Y", "C", "M"], 82 "*" : ["R", "G", "B", "_"], 83 } 84 85 if __name__ == "__main__": 86 width = 320 87 input_filename, output_filename = sys.argv[1:3] 88 89 im = PIL.Image.open(input_filename) 90 w, h = im.size 91 height = (width * h) / w 92 im = im.resize((width, height)) 93 94 usage = [] 95 base_usage = [] 96 toned = [] 97 98 for row in range(0, height): 99 u = {} 100 usage.append(u) 101 bu = {} 102 base_usage.append(bu) 103 tr = [] 104 toned.append(tr) 105 for column in range(0, width): 106 rgb = im.getpixel((column, row)) 107 p = point(rgb) 108 i = index(p) 109 t = tones[i] 110 add(u, t) 111 if t[0] != "*": 112 add(bu, t[0]) 113 if t[1] != "*": 114 add(bu, t[1]) 115 tr.append(t) 116 117 chosen = [] 118 119 for row, (u, bu) in enumerate(zip(usage, base_usage)): 120 light = row % 2 121 best = 0 122 best_bases = None 123 best_missing = None 124 best_map = None 125 for bases in combinations(bu, 4): 126 count = 0 127 missing = [] 128 tone_map = {} 129 for tone, freq in u.items(): 130 base = match(light and tone[1] or tone[0], bases) 131 if base: 132 tone_map[tone] = base 133 count += freq 134 else: 135 missing.append(tone) 136 if count > best: 137 best_bases = bases 138 best_missing = missing 139 best_map = tone_map 140 best = count 141 chosen.append((best, best_bases or bases, best_map or tone_map, best_missing or missing)) 142 143 output = [] 144 145 for row, (tr, ch) in enumerate(zip(toned, chosen)): 146 light = row % 2 147 o = [] 148 for column, t in enumerate(tr): 149 best, bases, tone_map, missing = ch 150 base = tone_map.get(t) or neutral(bases, light) 151 o.append(base) 152 i = colours.index(base) 153 im.putpixel((column, row), colour(i)) 154 155 output.append("".join(o)) 156 157 im.save(output_filename) 158 159 # vim: tabstop=4 expandtab shiftwidth=4