Image2ASCII

Inspiration

I've never built a Python program that works with images before, and I thought it would be a great fit for my first hackathon as I'm under a time crunch (big test on Monday, and my timezone is weird, I have to submit by 4 am?) and it's not too complicated to pull off after doing some research, because the Pillow library in Python is so powerful.

Also I think ASCII art is really cool, which is why I built this project.

What it does

It's a simple command line application that converts an image to ASCII art by mapping each pixel's luminosity to an ASCII character using the Luma relative luminosity formula.

It's pretty cool, see the GitHub repo for an example of the Mona Lisa in ASCII art, and there are a few more examples on the project page here as well.

The program needs the file path to the image, run as follows:

python main.py -f filepath

How I built it

I've built it using the Pillow library for going over each pixel and Python's argparse for parsing command line arguments.

Converting images to ASCII is surprisingly simple, it can be done in 3 steps:

  • Computing the pixel matrix (a fancy way of saying go through each pixel in the image)
  • Convert the RGB values(the color of the pixel) using some formula to ASCII characters
  • Print the ASCII characters

I've followed these steps in order by breaking each step into a function. The research links:

Project Files

main.py

def calculate_pixel_luminosity(red: int, green: int, blue: int) -> int:

Calculates and returns the pixel luminosity by forming a weighted average to account for human perception, using this formula:

L = 0.2126 R + 0.7152 G + 0.0722 B

Since humans are more sensitive to green light, it is weighted the most heavily, followed by red, then blue.

def compute_ascii_character_map(pixel_luminosity: int, maximum_luminosity: int) -> str:

Computes ASCII character based on the luminosity percentage, multiplied the length of the ASCII matrix, subtracted by 1 (to account for the index) to get the index of the character to map to.

map_index = ( (pixel_luminosity / maximum_luminosity) * length of ASCII matrix ) - 1

def compute_pixel_matrix(image) -> list[list[str]]:

Takes a PIL.Image object, goes through each of the pixels, maps the pixel's RGB values to an ASCII character, storing in a 2D array.

def main() -> None:

Prints out the image in ASCII

Challenges I ran into

The image was squashed originally, and didn't exactly fit the terminal window. After a lot of experimentation and googling, I learned that although pixels are perfect squares, the terminal characters are not, which is why the image was squished.

I had to do a lot of trial and error with the pillow library as well to resize the image so it would fit a full screen terminal window.

Accomplishments

The project took longer than expected but I'm happy I was able to do the project without many headaches and debugging, under the time I alloted myself, and I'm grateful I could easily understand the pillow library documentation.

Real World Usage

ASCII art is quite popular on the internet, and it's a great way to get retweets on Twitter, designing cool graphics for games, as well as to share with family and friends.

What I learned

  1. Terminal characters are roughly 3 times long as they are wide compared to pixels, which are perfect square
  2. You need to resize images before being able to print them on the terminal
  3. The luminositiy for a pixel is calculated by:
round((0.2126 * red) + (0.7152 * green) + (0.0722 * blue))

What's next for Image2ASCII

  • A GUI interface!

  • Turning it into a simple Flask or Django app.

  • Adding more formulae for luminosity which can be specified by command line arguments.

Built With

Share this project:

Updates