
ASCII art is a way of creating images only using. It was originally created in the early to mid 60’s due to lots of printers having very limited printing capabilities and so people would often use text to represent images. Creating art with text goes back much further than this though, for instance in the late 19th century you’ll find people were creating text based art with typewriters, the earliest known example being a page from an 1893 typewriter manual displaying different shapes you can make with text.
There are several different ways of creating ASCII art such as typing it out by hand or using block characters like “█”. The program here generates ASCII versions of pre existing images, this is accomplished by making the image monochrome and then replacing each of its pixels with either “$” or “ “ depending on whether that pixel is black or white.
Before continuing you should go ahead and install the Python Imaging Library aka PIL. This library provides all the requisite tools you’ll need for interacting with images in this project. You can install this library by navigating to your pip installation in the command prompt and typing the following command: “pip install pil”. Pip is a library installation manager that by default comes packaged with Python, you can read more about it here.
Turning Images Monochrome
Once you’ve got PIL installed the next thing you should do is import the “image” module of the library into your current project. This is the part of PIL which deals specifically with opening and modifying images and is the only part of the library used in this program.
from PIL import Image
After this you can start creating the function for converting images from full colour into monochrome which takes the following two arguments: the name of the image being put through the program as well as the image’s file extension since the user will have to enter these separately. The reason for this is so later we’ll be able to call the final product after the original images name easily.
def monochrome(imgname, extension): # Function to make Images Monochrome
The first thing you have to do here is open up the image using the “Image.open(filename)” function from PIL, passing in the image name plus its extension as the argument. This doesn’t actually display the image to the user (the “.show()” function does that) but instead opens the image into memory allowing the program to modify it.
photo = Image.open(imgname+extension) # Open Original Image
Now that you’ve got the image open you can make it monochrome using the “.convert(mode)” function. This function can take an image and convert it into a number of different shading styles or “modes”such as monochrome, this can be done by passing the number 1 into the function when you call on it. 1 is just the arbitrary designation for monochrome in this specific PIL function and doesn’t really mean anything.
photo = photo.convert('1') # Converts Image to Monochrome
After you’ve completed the transformation simply have the function return the newly made monochrome image.
return photo # Returns the New Image
Getting the RGB value of each pixel
Now that we’ve made our image monochrome we’ll need a function that can read through the RGB values of each pixel. This function will take the following arguments: a loaded image and a pair of xy coordinates which refer to a specific pixel on the image (For example “20,5” would refer to the 20th pixel in the 5th row of an image).
def readpixels(img, x, y): # Function to Read RGB Value of Image Pixels
Inside the function you’ll need to call on “.load()”, this will convert the image into an array made up of its pixels which are represented by their RGB values. Once this conversion is finished return the value of the requested pixel (The array is formatted so that the first pixel in the first row of the image is at position “0,0”).
pixel = img.load() # Stores Image in Memory as Pixel Access Object return pixel[x,y] # Displays RGB Value
Outputting the ASCII Art
This last function will be what actually converts images into ASCII art. It takes two arguments, first a monochrome image and then the image’s original file name.
def makeascii(img, imgname): # Function to Convert Image to Ascii
Firstly you’ll need to determine the width and height of the image and save these values as two separate variables, later you’ll use them to determine if the program has reached the end of a line of pixels in the image. The “.size()” function returns both values simultaneously in a list of values aka a tuple and in this list width comes before height so be sure to save that variable first.
width, height = img.size # Get Width and Height
After this create both an x and a y variable and set them equal to 0 for now, these will be used for getting the pixel RGB values from the function we created earlier for that reason.
x = 0 # X Position on Image y = 0 # Y Position on Image
In the RGB colour system black is represented by the number 0 and white by 255 so we need to set up a dictionary which associates our chosen symbol for black with 0 and our chosen symbol for white with 255. Every time the program reads the value of a pixel it’ll consult with this dictionary to see which symbol it should use for replacing said pixel.
Once you’ve done this you should create and open a text file named after the image being processed and be sure to set it to write mode as well, this is the file where we’re going to write our ASCII art into.
chars = {0: '$', 255: ' '} text_file = open(imgname+'.txt', 'w') # Opens a Text File
Next you should create a while loop that iterates for as long as y is less than or equal to the images height minus 1 (the reason for this being that arrays index from 0 so the y element of the images pixel array will end up being one less than the actual height of the image). Every time the loop runs it should first call our pixel reading function, passing on the image and x and y variables coordinates.
while y <= height - 1: rgb = readpixels(img, x, y) # Reads Pixel at position X, Y
The result of this function call should then be passed into our characters dictionary with the resulting character being written into our text file we created earlier.
text_file.write(chars[rgb]) # Writes Pixel as Relevant Character In Text File
After this your x variable should be iterated up by one so the program can move on to the next pixel in the row.
x += 1 # Increases X position by 1
At the end of the loop you should have an if statement which checks if the program has reached the end of the current row. When this happens a new line needs to be written to the text file, the x value needs to be reset to 0 (so it can begin iterating through the next row) and the y value should be increased by 1.
if x == width - 1: text_file.write('\n') # Takes X to Next Line x = 0 y += 1 # Takes Y to next line
After the while loop has finished running all you have to do is close the text file which should contain a completed piece of ASCII art now.
text_file.close()
Bringing It All Together
We’ve made all the pieces but now we need to bring them together and make the program easy to use for the user. Start out by first printing a simple message that summarizes what the program does and then another one telling the user that the image they wish to convert must be located in the same folder as this program.
x = 0 # X Position on Image y = 0 # Y Position on Image
After that prompt them for the name of their image and its file extension, be sure to tell them to give each one separately beforehand as well.
imgname = input("Enter Image Name (without its file extension): ") # Get User Chosen Image extension = input("Enter its file extension (EX: .jpg)")
Finally pass the user provided file name and extension into your monochrome function which itself should be passed on as the first argument in your make ASCII function with the provided file name being the second argument there. Once the function has finished running print a small message informing the user that their image is complete and then close the program.
makeascii(monochrome(imgname, extension), imgname) print("Done!")
Further Suggestions
The default monochrome that I used here isn’t really a “true” monochrome since it only converts pixels into one of two shades, black or white, with no shades of grey. This can cause pictures with more complex colours to end up looking basically unrecognisable, to fix this you’ll either need to create your own method of making an image monochrome or search for a better one in a different imaging library. Other improvements that you could make to the project include: having the final project saved as an image instead of in a text file and adding the ability for the program to generate colour ASCII art.
Good Luck!
Download the Source code here
If you have any questions or comments email them to me at nick@crumbsofcode.com