Home / Tools / ASCII art

Image to ASCII art

An old trick and still a good way to understand what greyscale conversion actually does.

Nothing is uploaded.

Why the height is halved

Characters in a terminal are about twice as tall as they are wide. Resize to the width you want and the same number of rows, and the picture comes out stretched vertically. Multiplying the height by roughly 0.5 puts it back.

Why the character order matters

The string runs from the character that covers the most ink to the one that covers the least: @ through to a space. Each grey value picks a character by how dark it is. Reverse the string and you get a negative, which is what the invert option does, and it is what you want when the art will be shown as light text on a dark background.

Doing it properly in Pillow

from PIL import Image

chars = "@%#*+=-:. "

im = Image.open("photo.jpg").convert("L")
wide = 100
tall = int(im.height * (wide / im.width) * 0.5)
im = im.resize((wide, tall))

for y in range(tall):
    print("".join(chars[im.getpixel((x, y)) * len(chars) // 256] for x in range(wide)))

Converting to L first is doing the real work. It applies the standard luminance weighting, which counts green far more than blue because that is how human vision works. Averaging the three channels instead gives noticeably worse art.