pure python and c implement
authorvunhat_minh <vunhat_minh@dwango.co.jp>
Fri, 6 Jun 2014 11:14:11 +0000 (20:14 +0900)
committervunhat_minh <vunhat_minh@dwango.co.jp>
Fri, 6 Jun 2014 11:14:11 +0000 (20:14 +0900)
1.png [deleted file]
rainbowstream/c_image.py [new file with mode: 0644]
rainbowstream/image.c [new file with mode: 0644]
rainbowstream/pure_image.py [moved from rainbowstream/image.py with 74% similarity]

diff --git a/1.png b/1.png
deleted file mode 100644 (file)
index 3e82812..0000000
Binary files a/1.png and /dev/null differ
diff --git a/rainbowstream/c_image.py b/rainbowstream/c_image.py
new file mode 100644 (file)
index 0000000..be3d52b
--- /dev/null
@@ -0,0 +1,43 @@
+from PIL import Image,ImageFilter
+from functools import partial
+from os.path import join, dirname, getmtime, exists, expanduser
+
+import ctypes, sys, os
+
+def call_c():
+    library = expanduser('~/.image.so')
+    sauce = join(dirname(__file__), 'image.c')
+    if not exists(library) or getmtime(sauce) > getmtime(library):
+        build = "gcc -fPIC -shared -o %s %s" % (library, sauce)
+        assert os.system(build + " >/dev/null 2>&1") == 0
+    image_c= ctypes.cdll.LoadLibrary(library)
+    image_c.init()
+    return image_c.rgb_to_ansi
+
+rgb2short = call_c()
+def pixel_print(ansicolor):
+    sys.stdout.write('\033[48;5;%sm \033[0m' % (ansicolor))
+  
+def image_to_display(path):
+    i = Image.open(path) 
+    i = i.convert('RGBA')
+    w,h = i.size
+    i.load()
+    rows, columns = os.popen('stty size', 'r').read().split()
+    width = min(w, int(columns)-2*6)
+    height = int(float(h) * (float(width) / float(w)))
+    height //= 2
+    i = i.resize((width, height), Image.BICUBIC)
+    height = min(height,30)
+
+    for y in xrange(height):
+        print ' '*6 ,
+        for x in xrange(width):
+            p = i.getpixel((x,y))
+            r, g, b = p[:3]
+            pixel_print(rgb2short(r,g,b))
+        print ''
+
+if __name__ == '__main__':
+    image_to_display(sys.argv[1])
diff --git a/rainbowstream/image.c b/rainbowstream/image.c
new file mode 100644 (file)
index 0000000..5de90b0
--- /dev/null
@@ -0,0 +1,57 @@
+typedef struct {
+        int r;
+        int g;
+        int b;
+} rgb_t;
+int CUBE_STEPS[] = { 0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF };
+rgb_t BASIC16[] = 
+      { {   0,   0,   0 }, { 205,   0,   0}, {   0, 205,   0 },
+        { 205, 205,   0 }, {   0,   0, 238}, { 205,   0, 205 },
+        {   0, 205, 205 }, { 229, 229, 229}, { 127, 127, 127 },
+        { 255,   0,   0 }, {   0, 255,   0}, { 255, 255,   0 },
+        {  92,  92, 255 }, { 255,   0, 255}, {   0, 255, 255 },
+        { 255, 255, 255 } };
+rgb_t COLOR_TABLE[256];
+
+
+rgb_t xterm_to_rgb(int xcolor)
+{
+  rgb_t res;
+  if (xcolor < 16) {
+    res = BASIC16[xcolor];
+  } else if (16 <= xcolor && xcolor <= 231) {
+    xcolor -= 16;
+    res.r = CUBE_STEPS[(xcolor / 36) % 6];
+    res.g = CUBE_STEPS[(xcolor / 6) % 6];
+    res.b = CUBE_STEPS[xcolor % 6];
+  } else if (232 <= xcolor && xcolor <= 255) {
+    res.r = res.g = res.b = 8 + (xcolor - 232) * 0x0A;
+  }
+  return res;
+}
+
+int init()
+{
+  int c;
+  for (c = 0; c < 256; c++) {
+    COLOR_TABLE[c] = xterm_to_rgb(c);
+  }
+  return 0;
+}
+
+int rgb_to_ansi(int r, int g, int b)
+{
+  int best_match = 0;
+  int smallest_distance = 1000000000;
+  int c, d;
+  for (c = 16; c < 256; c++) {
+    d = (COLOR_TABLE[c].r - r)*(COLOR_TABLE[c].r - r) +
+        (COLOR_TABLE[c].g - g)*(COLOR_TABLE[c].g - g) +
+        (COLOR_TABLE[c].b - b)*(COLOR_TABLE[c].b - b);
+    if (d < smallest_distance) {
+      smallest_distance = d;
+      best_match = c;
+    }
+  }
+  return best_match;
+}
similarity index 74%
rename from rainbowstream/image.py
rename to rainbowstream/pure_image.py
index fa76f20ab3c519836bb0d67618b75db9bba928cf..0a75d7795755b8e8ae7d585fd67bd35c00a55940 100644 (file)
@@ -1,29 +1,7 @@
 from PIL import Image,ImageFilter
-import sys,os
 from functools import partial
 
-""" Convert values between RGB hex codes and xterm-256 color codes.
-
-Nice long listing of all 256 colors and their codes. Useful for
-developing console color themes, or even script output schemes.
-
-Resources:
-* http://en.wikipedia.org/wiki/8-bit_color
-* http://en.wikipedia.org/wiki/ANSI_escape_code
-* /usr/share/X11/rgb.txt
-
-I'm not sure where this script was inspired from. I think I must have
-written it from scratch, though it's been several years now.
-"""
-
-__author__    = 'Micah Elliott http://MicahElliott.com'
-__version__   = '0.1'
-__copyright__ = 'Copyright (C) 2011 Micah Elliott.  All rights reserved.'
-__license__   = 'WTFPL http://sam.zoy.org/wtfpl/'
-
-#---------------------------------------------------------------------
-
-import sys, re
+import sys,os
 
 CLUT = [  # color look-up table
 #    8-bit, RGB hex
@@ -276,33 +254,23 @@ CLUT = [  # color look-up table
     ('238', '444444'),
     ('239', '4e4e4e'),
     ('240', '585858'),
-
     ('241', '626262'),
     ('242', '6c6c6c'),
     ('243', '767676'),
     ('244', '808080'),
-
     ('245', '8a8a8a'),
     ('246', '949494'),
     ('247', '9e9e9e'),
     ('248', 'a8a8a8'),
-
     ('249', 'b2b2b2'),
     ('250', 'bcbcbc'),
     ('251', 'c6c6c6'),
     ('252', 'd0d0d0'),
     ('253', 'dadada'),
-
     ('254', 'e4e4e4'),
     ('255', 'eeeeee'),
 ]
 
-def _strip_hash(rgb):
-    # Strip leading `#` if exists.
-    if rgb.startswith('#'):
-        rgb = rgb.lstrip('#')
-    return rgb
-
 def _create_dicts():
     short2rgb_dict = dict(CLUT)
     rgb2short_dict = {}
@@ -310,72 +278,32 @@ def _create_dicts():
         rgb2short_dict[v] = k
     return rgb2short_dict, short2rgb_dict
 
+RGB2SHORT_DICT, SHORT2RGB_DICT = _create_dicts()
+
+
 def short2rgb(short):
     return SHORT2RGB_DICT[short]
 
+
 def pixel_print(ansicolor):
     sys.stdout.write('\033[48;5;%sm \033[0m' % (ansicolor))
 
+
 def hex_to_rgb(value):
     value = value.lstrip('#')
     lv = len(value)
     return tuple(int(value[i:i+lv/3], 16) for i in range(0, lv, lv/3))
 
+
 def rgb_to_hex(rgb):
-    return '#%02x%02x%02x' % rgb
+    return '%02x%02x%02x' % rgb
 
-def binary_search(ary,(r,g,b)):
-    ary.sort()
-    if len(ary) == 1 : return ary[0]
-    mid = len(ary)//2
-    left = binary_search(ary[:mid],(r,g,b))
-    right = binary_search(ary[mid:],(r,g,b))
-    ld = (left[0]-r)**2 + (left[1]-g)**2 + (left[2]-b)**2
-    rd = (right[0]-r)**2 + (right[1]-g)**2 + (right[2]-b)**2
-    if ld < rd :
-        return left
-    else:
-        return right
 
-def rgb2short(rgb):
-    """ Find the closest xterm-256 approximation to the given RGB value.
-    @param rgb: Hex code representing an RGB value, eg, 'abcdef'
-    @returns: String between 0 and 255, compatible with xterm.
-    """
-    rgb = _strip_hash(rgb)
-    r = int(rgb[:2],16)
-    g = int(rgb[2:4],16)
-    b = int(rgb[4:],16)
+def rgb2short(r, g, b):
     dist = lambda s,d: (s[0]-d[0])**2+(s[1]-d[1])**2+(s[2]-d[2])**2
     ary = [hex_to_rgb(hex) for hex in RGB2SHORT_DICT]
-    m = binary_search(ary,(r,g,b))
     m = min(ary, key=partial(dist, (r,g,b)))
-    return RGB2SHORT_DICT[_strip_hash(rgb_to_hex(m))]
-
-   
-#    incs = (0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff)
-#    parts = [ int(h, 16) for h in re.split(r'(..)(..)(..)', rgb)[1:4] ]
-#    res = []
-#    for part in parts:
-#        i = 0
-#        while i < len(incs)-1:
-#            s, b = incs[i], incs[i+1]  # smaller, bigger
-#            if s <= part <= b:
-#                s1 = abs(s - part)
-#                b1 = abs(b - part)
-#                if s1 < b1: closest = s
-#                else: closest = b
-#                res.append(closest)
-#                break
-#            i += 1
-#    #print '***', res
-#    res = ''.join([ ('%02.x' % i) for i in res ])
-#    equiv = RGB2SHORT_DICT[ res ]
-#    #print '***', res, equiv
-#    return equiv 
-#
-RGB2SHORT_DICT, SHORT2RGB_DICT = _create_dicts()
+    return RGB2SHORT_DICT[rgb_to_hex(m)]
 
 def image_to_display(path):
     i = Image.open(path) 
@@ -387,19 +315,16 @@ def image_to_display(path):
     height = int(float(h) * (float(width) / float(w)))
     height //= 2
     i = i.resize((width, height), Image.BICUBIC)
+    height = min(height,30)
 
     for y in xrange(height):
         print ' '*6 ,
         for x in xrange(width):
             p = i.getpixel((x,y))
             r, g, b = p[:3]
-            hex = rgb_to_hex((r,g,b))
-            pixel_print(rgb2short(hex))
+            pixel_print(rgb2short(r,g,b))
         print ''
 
-#---------------------------------------------------------------------
-
 if __name__ == '__main__':
-    path = sys.argv[1]
-    image_to_display(path)
+    image_to_display(sys.argv[1])