archive

- Random tools & helpful resources for IRC
git clone git://git.acid.vegas/archive.git
Log | Files | Refs | Archive

img2irc_perplexa.py (4413B)

      1 #!/usr/bin/env python
      2 # -*- coding: utf-8 -*-
      3 
      4 """
      5 img2irc
      6 Copyright (C) 2012 perplexa
      7 """
      8 
      9 __author__ = "perplexa"
     10 __version__ = "0.3"
     11 
     12 import sys
     13 if sys.version_info[:2] < (2, 6):
     14     raise RuntimeError("Python v2.6 or later required")
     15 
     16 # requires python image library
     17 import Image
     18 import getopt
     19 import StringIO
     20 import sys
     21 import time
     22 import urllib2
     23 
     24 # define our palettes
     25 palette = {
     26   "mirc": {
     27      0: [255,255,255],
     28      1: [0,0,0],
     29      2: [0,0,127],
     30      3: [0,147,0],
     31      4: [255,0,0],
     32      5: [127,0,0],
     33      6: [156,0,156],
     34      7: [252,127,0],
     35      8: [255,255,0],
     36      9: [0,252,0],
     37     10: [0,147,147],
     38     11: [0,255,255],
     39     12: [0,0,252],
     40     13: [255,0,255],
     41     14: [127,127,127],
     42     15: [210,210,210]
     43   },
     44   "perplexa": {
     45      0: [255,255,255],
     46      1: [0,0,0],
     47      2: [58,80,120],
     48      3: [174,206,146],
     49      4: [207,97,113],
     50      5: [158,24,40],
     51      6: [150,60,89],
     52      7: [150,138,56],
     53      8: [255,247,150],
     54      9: [197,247,121],
     55     10: [65,129,121],
     56     11: [113,190,190],
     57     12: [65,134,190],
     58     13: [207,158,190],
     59     14: [102,102,102],
     60     15: [190,190,190]
     61   }
     62 }
     63 
     64 # define default parameter values
     65 use_palette = "perplexa"
     66 line_delay = None
     67 max_width = None
     68 
     69 #convert RGB to CIE-L*a*b* color space (takes floats between 0.0-1.0)
     70 def rgb_to_cielab(r, g, b):
     71   return xyz_to_cielab(*rgb_to_xyz(r, g, b))
     72   
     73 def rgb_to_xyz(r, g, b):
     74   r = _r1(r)
     75   g = _r1(g)
     76   b = _r1(b)
     77 
     78   # observer. = 2°, illuminant = D65
     79   x = r * 0.4124 + g * 0.3576 + b * 0.1805
     80   y = r * 0.2126 + g * 0.7152 + b * 0.0722
     81   z = r * 0.0193 + g * 0.1192 + b * 0.9505
     82 
     83   return x, y, z
     84 
     85 def _r1(y):
     86   y = ((y + 0.055) / 1.055) ** 2.4 if y > 0.04045 else y / 12.92
     87   return y * 100
     88 
     89 def _r2(y):
     90   return y ** (1.0 / 3.0) if y > 0.008856 else (7.787 * y) + (16.0 / 116.0)
     91 
     92 def xyz_to_cielab(x, y, z):
     93   x = x / 95.047
     94   y = y / 100.0
     95   z = z / 108.883
     96 
     97   x = _r2(x)
     98   y = _r2(y)
     99   z = _r2(z)
    100 
    101   L = (116 * y) - 16
    102   a = 500 * (x - y)
    103   b = 200 * (y - z)
    104 
    105   return L, a, b
    106 
    107 def euclidian_distance(v1, v2):
    108   return sum((a - b) ** 2 for a, b in zip(v1, v2)) ** 0.5
    109 
    110 def nearest_color(lab):
    111   distance = None
    112   color = None
    113   for col in palette[use_palette]:
    114     clab = rgb_to_cielab(*[x / 255.0 for x in palette[use_palette][col]])
    115     cdist = euclidian_distance(clab, lab)
    116     if distance == None or cdist < distance:
    117       distance = cdist
    118       color = col
    119   return color
    120 
    121 def usage():
    122   return """Usage: %s [OPTIONS] IMAGE
    123 Convert IMAGE to IRC text.
    124 
    125 Options:
    126   -d, --delay      define delay per line (seconds, float)
    127   -p, --palette    use specific palette (%s)
    128   -w, --width      limit image width to X pixels
    129 """ % (sys.argv[0], ", ".join(palette.keys()))
    130 
    131 # ## ### ##### ######## ##### ### ## #
    132 # ## ### ##### ######## ##### ### ## #
    133 
    134 if __name__ == "__main__":
    135   try:
    136     opts, args = getopt.getopt(sys.argv[1:], "p:d:w:", ["palette=", "delay=", "width="])
    137   except getopt.GetoptError, err:
    138     sys.stderr.write(str(err) + "\n")
    139     sys.exit(2)
    140 
    141   for opt, arg in opts:
    142     if opt in ("-p", "--palette"):
    143       if arg not in palette:
    144         sys.stderr.write("Invalid palette: %s\nUse one of the following: %s\n" % (arg, ", ".join(palette.keys())))
    145         sys.exit(2)
    146       use_palette = arg
    147     
    148     if opt in ("-d", "--delay"):
    149       line_delay = float(arg)
    150 
    151     if opt in ("-w", "--width"):
    152       max_width = int(arg)
    153 
    154   if not len(args):
    155     sys.stderr.write(usage())
    156     sys.exit(2)
    157   fn = args[0]
    158 
    159   try:
    160     if fn[0:4] == "http":
    161       sock = urllib2.urlopen(fn)
    162       data = sock.read()
    163       sock.close()
    164       fil = StringIO.StringIO(data)
    165     else:
    166       fil = fn
    167     img = Image.open(fil)
    168   except:
    169     sys.stderr.write("Could not open file: %s\n" % fn)
    170     sys.exit(2)
    171 
    172   img = img.convert(mode="RGBA")
    173 
    174   bg = Image.new("RGBA", img.size, (255, 255, 255))
    175   bg.paste(img, img)
    176 
    177   width, height = bg.size
    178   if max_width:
    179     height = int(height * (max_width / float(width)))
    180     width = max_width
    181     bg = bg.resize((width, height))
    182 
    183   pxl = bg.load()
    184   prv = None
    185 
    186   for y in range(height):
    187     for x in range(width):
    188       r, g, b, a = pxl[x, y]
    189       col = nearest_color(rgb_to_cielab(r / 255.0, g / 255.0, b / 255.0))
    190       if prv == None or prv != col:
    191         sys.stdout.write("\x03%s,%sXX" % (col, col))
    192         prv = col
    193       else:
    194         sys.stdout.write("XX")
    195     sys.stdout.write("\n")
    196     prv = None
    197 
    198     if line_delay:
    199       sys.stdout.flush()
    200       time.sleep(line_delay)