Skip to content

Instantly share code, notes, and snippets.

@JPLeBreton
Last active April 5, 2024 19:38
Show Gist options
  • Save JPLeBreton/6e1081803473b2e7792a00526a7c4d57 to your computer and use it in GitHub Desktop.
Save JPLeBreton/6e1081803473b2e7792a00526a7c4d57 to your computer and use it in GitHub Desktop.
colorblox.py - converts given image into emoji color blocks
import sys, os
from PIL import Image
MAX_SIZE = 16 # max output dimension in characters
RESAMPLE_TYPE = Image.NEAREST # alternately, Image.BICUBIC
DITHER_TYPE = Image.NONE # alternately, Image.FLOYDSTEINBERG
TRANSP = (0, 0, 0, 0)
RED = (221, 46, 68, 255)
ORANGE = (244, 144, 12, 255)
YELLOW = (253, 203, 88, 255)
GREEN = (120, 177, 89, 255)
BLUE = (85, 172, 238, 255)
PURPLE = (170, 142, 214, 255)
BROWN = (193, 105, 79, 255)
BLACK = (65, 65, 65, 255)
WHITE = (243, 243, 243, 255)
COLORS = [TRANSP, RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE, BROWN, BLACK, WHITE]
# different "blank" characters work better with different fonts :/
#TRANSP_CHAR = ' ' # regular ol' space
#TRANSP_CHAR = ' ' # U+2003 "em space"
#TRANSP_CHAR = '​' # U+200B "zero width sapce"
TRANSP_CHAR = ' ' # U+3000 "ideographic space" - still not the same size as a color block in many fonts :[
CHARS = [TRANSP_CHAR, '🟥', '🟧', '🟨', '🟩', '🟦', '🟪', '🟫', '⬛', '⬜']
if __name__ == '__main__':
if len(sys.argv) <= 1:
print('Please specify an image.')
sys.exit()
filename = ' '.join(sys.argv[1:])
if not os.path.exists(filename):
print("Couldn't find image file %s" % filename)
sys.exit()
# build palette
pal_img = Image.new('P', (1, 1))
# pal = [*(c[0], c[1], c[2]) for c in COLORS] # "iterable unpacking cannot be used in comprehension" :[
pal = []
for c in COLORS:
pal += [c[0], c[1], c[2], c[3]]
# PIL will fill out <256 color palettes with bogus values :/
while len(pal) < 256 * 4:
for i in range(4):
pal.append(0)
# palette for PIL must be exactly 256 colors
pal = pal[:256*4]
pal_img.putpalette(pal, rawmode='RGBA')
try:
src_img = Image.open(filename)
src_img = src_img.convert('RGB')
src_w, src_h = src_img.size
if src_w >= src_h:
aspect = src_h / src_w
w = MAX_SIZE
h = int(w * aspect)
else:
aspect = src_w / src_h
h = MAX_SIZE
w = int(h * aspect)
src_img = src_img.resize((w, h), resample=RESAMPLE_TYPE)
src_img = src_img.quantize(colors=len(COLORS), palette=pal_img, dither=DITHER_TYPE)
except:
print('Failed opening image %s' % filename)
sys.exit()
for y in range(h):
line = ''
for x in range(w):
p = src_img.getpixel((x, y))
line += CHARS[p]
print(line)
print(filename)
print('%s x %s -> %s x %s (%s chars total)' % (src_w, src_h, w, h, w * h))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment