generating ASCII art from photographs in Python
I’m old school – I have vague recollections of seeing an ASCII art printout of Snoopy printed on mainframe feed paper as a child back in the 70s. I think I drew on top of this with Crayola crayons.
I thought it would be fun to try to recreate this with photos.
ASCII Art works by using bog-standard ASCII characters to build up an image. It’s the creative use of positive and negative space in typography; some characters use more ink (like #) and some use less (like .), and by using the ‘weight’ of characters, you can build up an image.
What we need to do is to shrink the image to thumbnail size, then convert the image to monochrome.
With Python, the standard image processing library is PIL. Creating a monochrome thumbnail is trivial:-
im=Image.open(r"c:\test.jpg") im=im.resize((75, 75), Image.ANTIALIAS) im=im.convert("L") # convert to mono
Then, it’s a matter of splitting the luminosity values into 7 bands, and assigning each pixel a random character from a group of ascii characters of similar “optical weight”.
Here’s the script…
''' ASCII Art maker Creates an ascii art image from an arbitrary image Created on 7 Sep 2009 @author: Steven Kay ''' from PIL import Image import random from bisect import bisect # greyscale.. the following strings represent # 7 tonal ranges, from lighter to darker. # for a given pixel tonal level, choose a character # at random from that range. greyscale = [ " ", " ", ".,-", "_ivc=!/|\\~", "gjez2]/(YL)t[+T7Vf", "mdK4ZGbNDXY5P*Q", "W8KMA", "#%$" ] # using the bisect class to put luminosity values # in various ranges. # these are the luminosity cut-off points for each # of the 7 tonal levels. At the moment, these are 7 bands # of even width, but they could be changed to boost # contrast or change gamma, for example. zonebounds=[36,72,108,144,180,216,252] # open image and resize # experiment with aspect ratios according to font im=Image.open(r"c:\test.jpg") im=im.resize((160, 75),Image.BILINEAR) im=im.convert("L") # convert to mono # now, work our way over the pixels # build up str str="" for y in range(0,im.size[1]): for x in range(0,im.size[0]): lum=255-im.getpixel((x,y)) row=bisect(zonebounds,lum) possibles=greyscale[row] str=str+possibles[random.randint(0,len(possibles)-1)] str=str+"\n" print str
The text can then be pasted into a text editor or web page, but you’ll need to use a monospaced font, where each character takes the same amount of space horizontally. And depending on your editor, you may need to turn off word wrap 🙂
It’s amazing how much difference there is between monospaced fonts; some work better than others. Some fonts are ‘wider’ than others, so it’s worth experimenting with aspect ratios.