Skip to content

Instantly share code, notes, and snippets.

@drscotthawley
Created April 24, 2026 14:14
Show Gist options
  • Select an option

  • Save drscotthawley/cbd2383b7de420d8986c894cf1b95333 to your computer and use it in GitHub Desktop.

Select an option

Save drscotthawley/cbd2383b7de420d8986c894cf1b95333 to your computer and use it in GitHub Desktop.
Prints an ASCII-character version of an image.
#! /usr/bin/env python3
# Usage: img2ascii <img_file> <col_width>
from PIL import Image
import numpy as np
ASCII_CHARS = np.array(list(
" .:-=+*#%@"
))
# simpler ramp often looks better than huge ones
def rescale(pixels, low=2, high=98):
# percentile-based contrast stretch (prevents blowout)
lo = np.percentile(pixels, low)
hi = np.percentile(pixels, high)
pixels = np.clip(pixels, lo, hi)
return (pixels - lo) / (hi - lo) * 255
def image_to_ascii(path, width=140, gamma=1.2, invert=False):
img = Image.open(path).convert("L")
aspect_ratio = img.height / img.width
height = int(aspect_ratio * width * 0.55)
img = img.resize((width, height))
pixels = np.array(img).astype(float)
# 🔑 key: percentile stretch instead of min/max
pixels = rescale(pixels, 3, 97)
# gamma correction (controls brightness distribution)
pixels = 255 * ((pixels / 255) ** gamma)
if invert:
pixels = 255 - pixels
indices = (pixels / 255 * (len(ASCII_CHARS) - 1)).astype(int)
ascii_img = ASCII_CHARS[indices]
return "\n".join("".join(row) for row in ascii_img)
if __name__ == "__main__":
import sys
print(image_to_ascii(sys.argv[1], width=int(sys.argv[2]), gamma=1.3))
@drscotthawley

drscotthawley commented Apr 24, 2026

Copy link
Copy Markdown
Author

Totally generated by ChatGPT. Not actually "mine". Though we did iterate over a few versions to get it the way I liked it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment