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 fallback(bases): 39 for b in by_frequency(bases): 40 if b not in ["_", "W"]: 41 return b 42 return by_frequency(bases)[0] 43 44 tones = [ 45 "__", "_B", "BB", # 00x 46 "_G", "_C", "BC", # 01x 47 "GG", "GC", "CC", # 02x 48 "_R", "_M", "BM", # 10x 49 "_Y", "**", "WB", # 11x 50 "GY", "WG", "WC", # 12x 51 "RR", "RM", "MM", # 20x 52 "RY", "WR", "WM", # 21x 53 "YY", "WY", "WW", # 22x 54 ] 55 56 colours = ["_", "R", "G", "Y", "B", "M", "C", "W"] 57 58 if __name__ == "__main__": 59 width = 320 60 input_filename, output_filename = sys.argv[1:3] 61 62 im = PIL.Image.open(input_filename) 63 w, h = im.size 64 height = (width * h) / w 65 im = im.resize((width, height)) 66 67 usage = [] 68 base_usage = [] 69 toned = [] 70 71 for row in range(0, height): 72 u = {} 73 usage.append(u) 74 bu = {} 75 base_usage.append(bu) 76 tr = [] 77 toned.append(tr) 78 for column in range(0, width): 79 rgb = im.getpixel((column, row)) 80 p = point(rgb) 81 i = index(p) 82 t = tones[i] 83 add(u, t) 84 if t[0] != "*": 85 add(bu, t[0]) 86 if t[1] != "*": 87 add(bu, t[1]) 88 tr.append(t) 89 90 chosen = [] 91 92 for row, (u, bu) in enumerate(zip(usage, base_usage)): 93 best = 0 94 best_bases = None 95 best_missing = None 96 best_map = None 97 for bases in combinations(bu, 4): 98 bases = dict([(base, bu[base]) for base in bases]) 99 count = 0 100 missing = [] 101 tone_map = {} 102 for tone, freq in u.items(): 103 base = match(tone[1], bases) or match(tone[0], bases) 104 if base: 105 tone_map[tone] = base 106 count += freq 107 else: 108 missing.append(tone) 109 if count > best: 110 best_bases = bases 111 best_missing = missing 112 best_map = tone_map 113 best = count 114 chosen.append((best, best_bases or bases, best_map or tone_map, best_missing or missing)) 115 116 output = [] 117 118 for row, (tr, ch) in enumerate(zip(toned, chosen)): 119 o = [] 120 for column, t in enumerate(tr): 121 best, bases, tone_map, missing = ch 122 base = tone_map.get(t) or fallback(bases) 123 o.append(base) 124 i = colours.index(base) 125 im.putpixel((column, row), colour(i)) 126 127 output.append("".join(o)) 128 129 im.save(output_filename) 130 131 # vim: tabstop=4 expandtab shiftwidth=4