# Python 2.7 Code # Jonathan Frech 3rd, 4th of March 2017 # rewritten 12th of April 2017 # edited 13th of April, 13th, 14th, 15th of July 2017 # Type 'python asciify.py -h' for help. # import import os, argparse, sys, cgi # turn main function's output into an html page def html(img, parsed): # encoding list (translate color) clrenc = { "\033[0m" :"", "\033[1;30m":"", "\033[1;31m":"", "\033[1;32m":"", "\033[1;33m":"", "\033[1;34m":"", "\033[1;35m":"", "\033[1;36m":"", "\033[1;37m":"" } # escape html special characters img = cgi.escape(img) # replace ANSI escape codes with html tags for enc in clrenc: img = img.replace(enc, clrenc[enc]) # write command beneath image cmd = "python %s" % sys.argv[0] if parsed.img : cmd += " \"%s\"" % parsed.img.replace("\"", "\\\"") if not parsed.htmlx else " \"%s\"" % (parsed.img.replace("\"", "\\\""), parsed.img.replace("\"", "\\\"")) if parsed.size : cmd += " --size %d" % parsed.size if parsed.charset : cmd += " --charset \"%s\"" % parsed.charset.replace("\"", "\\\"") if parsed.smallcharset: cmd += " --smallcharset" if parsed.cut : cmd += " --cut" if parsed.color : cmd += " --color" else: img = "%s" % img if parsed.invert : cmd += " --invert" if parsed.fullsize : cmd += " --fullsize" if parsed.html : cmd += " --html" if parsed.htmlx : cmd += " --htmlx" if parsed.link : cmd += " --link" # generate link if parsed.link: cmd += "\n \n<-" # html page html = "\n\t\n\t\n\t\t\n\t\t\tAsciify\n\t\t\t

Asciify

\n\t\t\n\t\t\n\t\t\n\t\t\t
%s\n \n%s
\n\t\t\n\t" % (img, cmd) # return finished html page return html # main function def main(): # import PIL (python imaging library) try: from PIL import Image except ImportError: return "Failed to import PIL. Is it installed?\nsudo apt-get install python-pil" # parse command line arguments parser = argparse.ArgumentParser(description = "Asciify an image; turn pixels into characters.") parser.add_argument("img" , type = str , help = "image file to asciify" ) parser.add_argument("--size" , "-s", type = int, metavar = "N", help = "image's size in characters" ) parser.add_argument("--charset" , type = str, metavar = "C", help = "character set used to interpret image" ) parser.add_argument("--smallcharset", action = "store_true" , help = "use smaller character set than default" ) parser.add_argument("--cut" , action = "store_true" , help = "cut away white border" ) parser.add_argument("--color" , "-c", action = "store_true" , help = "display image's colors" ) parser.add_argument("--invert" , "-i", action = "store_true" , help = "invert charset" ) parser.add_argument("--fullsize" , "-f", action = "store_true" , help = "use image's full size (default is 100px)") parser.add_argument("--html" , action = "store_true" , help = "format output to html page" ) parser.add_argument("--htmlx" , action = "store_true" , help = "add image hyperlink to html output" ) parser.add_argument("--link" , action = "store_true" , help = "link to index.html in html output" ) parsed = parser.parse_args() # you need hyperlink technology to link if parsed.link and not (parsed.html or parsed.htmlx): parser.error("Cannot link when not outputting html page.") # try to open image imgname = os.path.abspath(parsed.img) try: img = Image.open(imgname).convert("RGB") w, h = img.size except: parser.error("Could not open image file.") # crop image if too big s = parsed.size or 100 if not parsed.fullsize: k = float(w)/h # width larger if w > h: w = s h = int(w/k) # height larger or equal else: h = s w = int(h*k) # a character is twice as high as wide h //= 2 # resize (minimum size is 1x1) img = img.resize((max(w, 1), max(h, 1))) # special character set if parsed.charset is not None: charset = parsed.charset else: # smaller character set if parsed.smallcharset: charset = "@%#*+=-:. " # default character set else: charset = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'. " # invert charset if parsed.invert: charset = charset[::-1] # load pixels _pix = img.load() pix = [[_pix[x, y] for x in range(w)] for y in range(h)] # calculate black and white values k = [[sum(pix[y][x])/3. for x in range(w)] for y in range(h)] # determine darkest and brightest color kmin, kmax = 255, 0 for y in range(h): for x in range(w): v = k[y][x] if v < kmin: kmin = v if v > kmax: kmax = v # map color value to character set kdelta = kmax-kmin l = len(charset)-1 k = [[int((k[y][x]-kmin)/kdelta*l) for x in range(w)] for y in range(h)] # cut whitespace from all four sides if parsed.cut: empty = lambda array: map(lambda x: charset[x], array).count(" ") == len(array) for _ in (-1, 0): # cut top and bottom while len(k) > 0 and empty(k[_]): k.pop(_) pix.pop(_) h -= 1 # cut left and right while len(k[0]) > 0 and empty([k[y][_] for y in range(h)]): for y in range(h): k[y].pop(_) pix[y].pop(_) w -= 1 # output in color if parsed.color: # color encodings end, gray, red, green, yellow, blue, magenta, cyan, white = "\033[0m", "\033[1;30m", "\033[1;31m", "\033[1;32m", "\033[1;33m", "\033[1;34m", "\033[1;35m", "\033[1;36m", "\033[1;37m" # color array, so that c[r][g][b] is the desired encoding for r, g, b in [0, 1] c = [[[gray, blue], [green, cyan]], [[red, magenta], [yellow, white]]] # determine min and max color values rmin, rmax = 255, 0 gmin, gmax = 255, 0 bmin, bmax = 255, 0 for y in range(h): for x in range(w): rv, gv, bv = pix[y][x] if rv < rmin: rmin = rv if rv > rmax: rmax = rv if gv < gmin: gmin = gv if gv > gmax: gmax = gv if bv < bmin: bmin = bv if bv > bmax: bmax = bv rdelta, gdelta, bdelta = rmax-rmin, gmax-gmin, bmax-bmin rmiddle, gmiddle, bmiddle = rmin+rdelta//2, gmin+gdelta//2, bmin+bdelta//2 # old one-liner (ineffective, as characters sharing color and position get their own color tags) # img = "\n".join(["".join([c[pix[y][x][0]>rmiddle][pix[y][x][1]>gmiddle][pix[y][x][2]>bmiddle] + charset[k[y][x]] + end for x in range(w)]) for y in range(h)]) # image string, current color img, cclr = "", None # loop through pixels for y in range(h): for x in range(w): # determine character's color clr = c[pix[y][x][0]>rmiddle][pix[y][x][1]>gmiddle][pix[y][x][2]>bmiddle] # check if color changed, act accordingly if clr != cclr: img += end + clr; cclr = clr # add character img += charset[k[y][x]] # new line img += "\n" # remove last new line img = img[:-1] # add end if not yet present if img[-len(end):] != end: img += end # output in standard black and white else: img = "\n".join(["".join([charset[k[y][x]] for x in range(w)]) for y in range(h)]) # return as html page if parsed.html or parsed.htmlx: return html(img, parsed) # return as normal text (color is encoded using ANSI escape codes) else: return img # run if called as __main__ if __name__ == "__main__": print main()