Convert Photos to ASCII Arts with Python
Printed ASCII art is a fabulous gift for a geeky friend. The great thing about coded artwork is that you can easily give it your own personal touch! It’s a stylish decoration that looks great on mugs, t-shirts, and even curtains. Making ASCII art is much easier than it looks, and I’m going to show you how to do it in Python!
We’ll use a few Python libraries– Pillow, Colour and Numpy. Pillow handles all the image processing - reading, resizing, writing. Colour gives us the beautiful gradient color. Numpy makes the grayscale conversion easy. Once you understand how sample codes work, you can change fonts/symbols and try out different colors, to turn your favorite picture into your unique signature artwork.
Prerequisits: Install Python 3 and libraries Pillow, Colour and Numpy
Step 1: load a picture
The Image class in PIL (Pillow) provides a lazy function Image.open
to open an image file. At this step, we only identify the image but not actually load it into memory. Image
supports a variety of image types including bmp, jpeg, gif, eps.
Step 2: resize the picture
ASCII art is composed of symbols. Symbols naturally have different degrees of darkness because of their shapes. For example, .
is whiter than :
which is whiter than !
. It’s easy to illustrate a range of grayscale with symbol patches.
One challenge as you can see from above is that symbols are not necessarily square. If we simply replace image pixels with symbols one-to-one, we will get a picture with distorted width and height. To avoid deformation, we need to carefully calculate how many symbols to use in each row and column in the new image. As a simple example, to draw a small square using symbols of shape ratio 3:4 (width:height), we need to use 4 columns and 3 rows.
Another thing to notice is that a symbol is much larger than a pixel(px). So it’s not a bad idea to reduce the picture resolution before the conversion. Our program handles the pixel reduction using a scaling factor SC
between 0(exclusive) and 1(inclusive). The bigger the number is, the more details you would see in the output and the bigger the output image would be.
Step 3: convert colors to grayscale
Since img.resize(...)
returns an Image object, it contains RGB values of each pixel on the image. Assume the images is m px width and n px height, np.asarray(img)
returns a m* n *3 matrix.
There are a few methods to convert RGB color to grayscale (John D. Cook describes them well in his post). Here we simply take the average of RGB values and normalize them to get monochrome values. The result is a m *n matrix of float numbers between (0,1). The whitest point of the picture is 0, and the darkest point is 1. Optionally, we can further tune the image brightness using parameter GCF
. If GCF
is set to be >1, the picture would look brighter. If 0<GCF
<1, the picture would look darker.
Step 4: create an ASCII art image
The next step is to map grayscale to symbols of different darkness. The first line of the code below defines an array of symbols from the whitest to the darkest. Next, grayscale values are mapped to symbols in the array. There is a lot of flexibility how you can define the array: letters, punctuations, symbols, special characters, etc. It’s totally up to you to make it more fun.
Image.new
and Image.Draw
provide easy ways to create a blank image, where we will print the mapped symbols line by line. The for
loop is where the printing actually happen. You can certainly try using the same color for all the symbols, or use functions such as range_to
in Colour
to create color patterns.
Step 5: save the image
Click here to download the sample code. Change the input file name in the bottom section and try it with your own image!
Leave a Comment