Commit | Line | Data |
---|---|---|
026caf17 | 1 | from PIL import Image,ImageFilter |
2b502606 | 2 | import sys,os |
026caf17 | 3 | from functools import partial |
1c50e2d1 O |
4 | |
5 | """ Convert values between RGB hex codes and xterm-256 color codes. | |
6 | ||
7 | Nice long listing of all 256 colors and their codes. Useful for | |
8 | developing console color themes, or even script output schemes. | |
9 | ||
10 | Resources: | |
11 | * http://en.wikipedia.org/wiki/8-bit_color | |
12 | * http://en.wikipedia.org/wiki/ANSI_escape_code | |
13 | * /usr/share/X11/rgb.txt | |
14 | ||
15 | I'm not sure where this script was inspired from. I think I must have | |
16 | written it from scratch, though it's been several years now. | |
17 | """ | |
18 | ||
19 | __author__ = 'Micah Elliott http://MicahElliott.com' | |
20 | __version__ = '0.1' | |
21 | __copyright__ = 'Copyright (C) 2011 Micah Elliott. All rights reserved.' | |
22 | __license__ = 'WTFPL http://sam.zoy.org/wtfpl/' | |
23 | ||
24 | #--------------------------------------------------------------------- | |
25 | ||
26 | import sys, re | |
27 | ||
28 | CLUT = [ # color look-up table | |
29 | # 8-bit, RGB hex | |
30 | ||
31 | # Primary 3-bit (8 colors). Unique representation! | |
32 | ('00', '000000'), | |
33 | ('01', '800000'), | |
34 | ('02', '008000'), | |
35 | ('03', '808000'), | |
36 | ('04', '000080'), | |
37 | ('05', '800080'), | |
38 | ('06', '008080'), | |
39 | ('07', 'c0c0c0'), | |
40 | ||
41 | # Equivalent "bright" versions of original 8 colors. | |
42 | ('08', '808080'), | |
43 | ('09', 'ff0000'), | |
44 | ('10', '00ff00'), | |
45 | ('11', 'ffff00'), | |
46 | ('12', '0000ff'), | |
47 | ('13', 'ff00ff'), | |
48 | ('14', '00ffff'), | |
49 | ('15', 'ffffff'), | |
50 | ||
51 | # Strictly ascending. | |
52 | ('16', '000000'), | |
53 | ('17', '00005f'), | |
54 | ('18', '000087'), | |
55 | ('19', '0000af'), | |
56 | ('20', '0000d7'), | |
57 | ('21', '0000ff'), | |
58 | ('22', '005f00'), | |
59 | ('23', '005f5f'), | |
60 | ('24', '005f87'), | |
61 | ('25', '005faf'), | |
62 | ('26', '005fd7'), | |
63 | ('27', '005fff'), | |
64 | ('28', '008700'), | |
65 | ('29', '00875f'), | |
66 | ('30', '008787'), | |
67 | ('31', '0087af'), | |
68 | ('32', '0087d7'), | |
69 | ('33', '0087ff'), | |
70 | ('34', '00af00'), | |
71 | ('35', '00af5f'), | |
72 | ('36', '00af87'), | |
73 | ('37', '00afaf'), | |
74 | ('38', '00afd7'), | |
75 | ('39', '00afff'), | |
76 | ('40', '00d700'), | |
77 | ('41', '00d75f'), | |
78 | ('42', '00d787'), | |
79 | ('43', '00d7af'), | |
80 | ('44', '00d7d7'), | |
81 | ('45', '00d7ff'), | |
82 | ('46', '00ff00'), | |
83 | ('47', '00ff5f'), | |
84 | ('48', '00ff87'), | |
85 | ('49', '00ffaf'), | |
86 | ('50', '00ffd7'), | |
87 | ('51', '00ffff'), | |
88 | ('52', '5f0000'), | |
89 | ('53', '5f005f'), | |
90 | ('54', '5f0087'), | |
91 | ('55', '5f00af'), | |
92 | ('56', '5f00d7'), | |
93 | ('57', '5f00ff'), | |
94 | ('58', '5f5f00'), | |
95 | ('59', '5f5f5f'), | |
96 | ('60', '5f5f87'), | |
97 | ('61', '5f5faf'), | |
98 | ('62', '5f5fd7'), | |
99 | ('63', '5f5fff'), | |
100 | ('64', '5f8700'), | |
101 | ('65', '5f875f'), | |
102 | ('66', '5f8787'), | |
103 | ('67', '5f87af'), | |
104 | ('68', '5f87d7'), | |
105 | ('69', '5f87ff'), | |
106 | ('70', '5faf00'), | |
107 | ('71', '5faf5f'), | |
108 | ('72', '5faf87'), | |
109 | ('73', '5fafaf'), | |
110 | ('74', '5fafd7'), | |
111 | ('75', '5fafff'), | |
112 | ('76', '5fd700'), | |
113 | ('77', '5fd75f'), | |
114 | ('78', '5fd787'), | |
115 | ('79', '5fd7af'), | |
116 | ('80', '5fd7d7'), | |
117 | ('81', '5fd7ff'), | |
118 | ('82', '5fff00'), | |
119 | ('83', '5fff5f'), | |
120 | ('84', '5fff87'), | |
121 | ('85', '5fffaf'), | |
122 | ('86', '5fffd7'), | |
123 | ('87', '5fffff'), | |
124 | ('88', '870000'), | |
125 | ('89', '87005f'), | |
126 | ('90', '870087'), | |
127 | ('91', '8700af'), | |
128 | ('92', '8700d7'), | |
129 | ('93', '8700ff'), | |
130 | ('94', '875f00'), | |
131 | ('95', '875f5f'), | |
132 | ('96', '875f87'), | |
133 | ('97', '875faf'), | |
134 | ('98', '875fd7'), | |
135 | ('99', '875fff'), | |
136 | ('100', '878700'), | |
137 | ('101', '87875f'), | |
138 | ('102', '878787'), | |
139 | ('103', '8787af'), | |
140 | ('104', '8787d7'), | |
141 | ('105', '8787ff'), | |
142 | ('106', '87af00'), | |
143 | ('107', '87af5f'), | |
144 | ('108', '87af87'), | |
145 | ('109', '87afaf'), | |
146 | ('110', '87afd7'), | |
147 | ('111', '87afff'), | |
148 | ('112', '87d700'), | |
149 | ('113', '87d75f'), | |
150 | ('114', '87d787'), | |
151 | ('115', '87d7af'), | |
152 | ('116', '87d7d7'), | |
153 | ('117', '87d7ff'), | |
154 | ('118', '87ff00'), | |
155 | ('119', '87ff5f'), | |
156 | ('120', '87ff87'), | |
157 | ('121', '87ffaf'), | |
158 | ('122', '87ffd7'), | |
159 | ('123', '87ffff'), | |
160 | ('124', 'af0000'), | |
161 | ('125', 'af005f'), | |
162 | ('126', 'af0087'), | |
163 | ('127', 'af00af'), | |
164 | ('128', 'af00d7'), | |
165 | ('129', 'af00ff'), | |
166 | ('130', 'af5f00'), | |
167 | ('131', 'af5f5f'), | |
168 | ('132', 'af5f87'), | |
169 | ('133', 'af5faf'), | |
170 | ('134', 'af5fd7'), | |
171 | ('135', 'af5fff'), | |
172 | ('136', 'af8700'), | |
173 | ('137', 'af875f'), | |
174 | ('138', 'af8787'), | |
175 | ('139', 'af87af'), | |
176 | ('140', 'af87d7'), | |
177 | ('141', 'af87ff'), | |
178 | ('142', 'afaf00'), | |
179 | ('143', 'afaf5f'), | |
180 | ('144', 'afaf87'), | |
181 | ('145', 'afafaf'), | |
182 | ('146', 'afafd7'), | |
183 | ('147', 'afafff'), | |
184 | ('148', 'afd700'), | |
185 | ('149', 'afd75f'), | |
186 | ('150', 'afd787'), | |
187 | ('151', 'afd7af'), | |
188 | ('152', 'afd7d7'), | |
189 | ('153', 'afd7ff'), | |
190 | ('154', 'afff00'), | |
191 | ('155', 'afff5f'), | |
192 | ('156', 'afff87'), | |
193 | ('157', 'afffaf'), | |
194 | ('158', 'afffd7'), | |
195 | ('159', 'afffff'), | |
196 | ('160', 'd70000'), | |
197 | ('161', 'd7005f'), | |
198 | ('162', 'd70087'), | |
199 | ('163', 'd700af'), | |
200 | ('164', 'd700d7'), | |
201 | ('165', 'd700ff'), | |
202 | ('166', 'd75f00'), | |
203 | ('167', 'd75f5f'), | |
204 | ('168', 'd75f87'), | |
205 | ('169', 'd75faf'), | |
206 | ('170', 'd75fd7'), | |
207 | ('171', 'd75fff'), | |
208 | ('172', 'd78700'), | |
209 | ('173', 'd7875f'), | |
210 | ('174', 'd78787'), | |
211 | ('175', 'd787af'), | |
212 | ('176', 'd787d7'), | |
213 | ('177', 'd787ff'), | |
214 | ('178', 'd7af00'), | |
215 | ('179', 'd7af5f'), | |
216 | ('180', 'd7af87'), | |
217 | ('181', 'd7afaf'), | |
218 | ('182', 'd7afd7'), | |
219 | ('183', 'd7afff'), | |
220 | ('184', 'd7d700'), | |
221 | ('185', 'd7d75f'), | |
222 | ('186', 'd7d787'), | |
223 | ('187', 'd7d7af'), | |
224 | ('188', 'd7d7d7'), | |
225 | ('189', 'd7d7ff'), | |
226 | ('190', 'd7ff00'), | |
227 | ('191', 'd7ff5f'), | |
228 | ('192', 'd7ff87'), | |
229 | ('193', 'd7ffaf'), | |
230 | ('194', 'd7ffd7'), | |
231 | ('195', 'd7ffff'), | |
232 | ('196', 'ff0000'), | |
233 | ('197', 'ff005f'), | |
234 | ('198', 'ff0087'), | |
235 | ('199', 'ff00af'), | |
236 | ('200', 'ff00d7'), | |
237 | ('201', 'ff00ff'), | |
238 | ('202', 'ff5f00'), | |
239 | ('203', 'ff5f5f'), | |
240 | ('204', 'ff5f87'), | |
241 | ('205', 'ff5faf'), | |
242 | ('206', 'ff5fd7'), | |
243 | ('207', 'ff5fff'), | |
244 | ('208', 'ff8700'), | |
245 | ('209', 'ff875f'), | |
246 | ('210', 'ff8787'), | |
247 | ('211', 'ff87af'), | |
248 | ('212', 'ff87d7'), | |
249 | ('213', 'ff87ff'), | |
250 | ('214', 'ffaf00'), | |
251 | ('215', 'ffaf5f'), | |
252 | ('216', 'ffaf87'), | |
253 | ('217', 'ffafaf'), | |
254 | ('218', 'ffafd7'), | |
255 | ('219', 'ffafff'), | |
256 | ('220', 'ffd700'), | |
257 | ('221', 'ffd75f'), | |
258 | ('222', 'ffd787'), | |
259 | ('223', 'ffd7af'), | |
260 | ('224', 'ffd7d7'), | |
261 | ('225', 'ffd7ff'), | |
262 | ('226', 'ffff00'), | |
263 | ('227', 'ffff5f'), | |
264 | ('228', 'ffff87'), | |
265 | ('229', 'ffffaf'), | |
266 | ('230', 'ffffd7'), | |
267 | ('231', 'ffffff'), | |
268 | ||
269 | # Gray-scale range. | |
270 | ('232', '080808'), | |
271 | ('233', '121212'), | |
272 | ('234', '1c1c1c'), | |
273 | ('235', '262626'), | |
274 | ('236', '303030'), | |
275 | ('237', '3a3a3a'), | |
276 | ('238', '444444'), | |
277 | ('239', '4e4e4e'), | |
278 | ('240', '585858'), | |
026caf17 | 279 | |
1c50e2d1 O |
280 | ('241', '626262'), |
281 | ('242', '6c6c6c'), | |
282 | ('243', '767676'), | |
283 | ('244', '808080'), | |
026caf17 | 284 | |
1c50e2d1 O |
285 | ('245', '8a8a8a'), |
286 | ('246', '949494'), | |
287 | ('247', '9e9e9e'), | |
288 | ('248', 'a8a8a8'), | |
026caf17 | 289 | |
1c50e2d1 O |
290 | ('249', 'b2b2b2'), |
291 | ('250', 'bcbcbc'), | |
292 | ('251', 'c6c6c6'), | |
293 | ('252', 'd0d0d0'), | |
294 | ('253', 'dadada'), | |
026caf17 | 295 | |
1c50e2d1 O |
296 | ('254', 'e4e4e4'), |
297 | ('255', 'eeeeee'), | |
298 | ] | |
299 | ||
1c50e2d1 O |
300 | def _strip_hash(rgb): |
301 | # Strip leading `#` if exists. | |
302 | if rgb.startswith('#'): | |
303 | rgb = rgb.lstrip('#') | |
304 | return rgb | |
305 | ||
306 | def _create_dicts(): | |
307 | short2rgb_dict = dict(CLUT) | |
308 | rgb2short_dict = {} | |
309 | for k, v in short2rgb_dict.items(): | |
310 | rgb2short_dict[v] = k | |
311 | return rgb2short_dict, short2rgb_dict | |
312 | ||
313 | def short2rgb(short): | |
314 | return SHORT2RGB_DICT[short] | |
315 | ||
2b502606 | 316 | def pixel_print(ansicolor): |
317 | sys.stdout.write('\033[48;5;%sm \033[0m' % (ansicolor)) | |
318 | ||
25e5e7f4 | 319 | def hex_to_rgb(value): |
320 | value = value.lstrip('#') | |
321 | lv = len(value) | |
322 | return tuple(int(value[i:i+lv/3], 16) for i in range(0, lv, lv/3)) | |
323 | ||
324 | def rgb_to_hex(rgb): | |
325 | return '#%02x%02x%02x' % rgb | |
326 | ||
026caf17 | 327 | def binary_search(ary,(r,g,b)): |
328 | ary.sort() | |
329 | if len(ary) == 1 : return ary[0] | |
330 | mid = len(ary)//2 | |
331 | left = binary_search(ary[:mid],(r,g,b)) | |
332 | right = binary_search(ary[mid:],(r,g,b)) | |
333 | ld = (left[0]-r)**2 + (left[1]-g)**2 + (left[2]-b)**2 | |
334 | rd = (right[0]-r)**2 + (right[1]-g)**2 + (right[2]-b)**2 | |
335 | if ld < rd : | |
336 | return left | |
337 | else: | |
338 | return right | |
339 | ||
340 | ||
1c50e2d1 O |
341 | def rgb2short(rgb): |
342 | """ Find the closest xterm-256 approximation to the given RGB value. | |
343 | @param rgb: Hex code representing an RGB value, eg, 'abcdef' | |
344 | @returns: String between 0 and 255, compatible with xterm. | |
1c50e2d1 O |
345 | """ |
346 | rgb = _strip_hash(rgb) | |
2b502606 | 347 | r = int(rgb[:2],16) |
348 | g = int(rgb[2:4],16) | |
349 | b = int(rgb[4:],16) | |
026caf17 | 350 | dist = lambda s,d: (s[0]-d[0])**2+(s[1]-d[1])**2+(s[2]-d[2])**2 |
2b502606 | 351 | ary = [hex_to_rgb(hex) for hex in RGB2SHORT_DICT] |
026caf17 | 352 | m = binary_search(ary,(r,g,b)) |
353 | m = min(ary, key=partial(dist, (r,g,b))) | |
354 | return RGB2SHORT_DICT[_strip_hash(rgb_to_hex(m))] | |
2b502606 | 355 | |
026caf17 | 356 | |
2b502606 | 357 | # incs = (0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff) |
358 | # parts = [ int(h, 16) for h in re.split(r'(..)(..)(..)', rgb)[1:4] ] | |
359 | # res = [] | |
360 | # for part in parts: | |
361 | # i = 0 | |
362 | # while i < len(incs)-1: | |
363 | # s, b = incs[i], incs[i+1] # smaller, bigger | |
364 | # if s <= part <= b: | |
365 | # s1 = abs(s - part) | |
366 | # b1 = abs(b - part) | |
367 | # if s1 < b1: closest = s | |
368 | # else: closest = b | |
369 | # res.append(closest) | |
370 | # break | |
371 | # i += 1 | |
372 | # #print '***', res | |
373 | # res = ''.join([ ('%02.x' % i) for i in res ]) | |
374 | # equiv = RGB2SHORT_DICT[ res ] | |
375 | # #print '***', res, equiv | |
376 | # return equiv | |
026caf17 | 377 | # |
1c50e2d1 O |
378 | RGB2SHORT_DICT, SHORT2RGB_DICT = _create_dicts() |
379 | ||
2b502606 | 380 | def image_to_display(path): |
25e5e7f4 | 381 | i = Image.open(path) |
2b502606 | 382 | i = i.convert('RGBA') |
383 | w,h = i.size | |
384 | i.load() | |
385 | rows, columns = os.popen('stty size', 'r').read().split() | |
386 | width = min(w, int(columns)-2*6) | |
387 | height = int(float(h) * (float(width) / float(w))) | |
388 | height //= 2 | |
389 | i = i.resize((width, height), Image.BICUBIC) | |
390 | ||
391 | for y in xrange(height): | |
392 | print ' '*6 , | |
393 | for x in xrange(width): | |
394 | p = i.getpixel((x,y)) | |
395 | r, g, b = p[:3] | |
396 | hex = rgb_to_hex((r,g,b)) | |
397 | pixel_print(rgb2short(hex)) | |
398 | print '' | |
25e5e7f4 | 399 | |
1c50e2d1 O |
400 | #--------------------------------------------------------------------- |
401 | ||
402 | if __name__ == '__main__': | |
2b502606 | 403 | path = sys.argv[1] |
404 | image_to_display(path) | |
026caf17 | 405 |