Skip to content

Instantly share code, notes, and snippets.

@sooop
Last active August 30, 2022 06:44
Show Gist options
  • Save sooop/b69dd57af4a5d66490a8720efc57e5d1 to your computer and use it in GitHub Desktop.
Save sooop/b69dd57af4a5d66490a8720efc57e5d1 to your computer and use it in GitHub Desktop.
import argparse
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument("-w", "--width", dest="width", type=int, default=80)
parser.add_argument("-l", "--level", dest="level", type=int, default=16)
parser.add_argument("-i", "--invert", dest="invert", action="store_true")
parser.add_argument("filename")
return parser.parse_args()
def create_char_map(*, level: int = 16, inv: bool = False) -> str:
cmap: list[tuple[int | float, str]] = []
filepath = Path(__file__).parent / "CascadiaMono.ttf"
ft = ImageFont.truetype(filepath.as_posix(), size=12)
canvas = Image.new("L", (20, 20), color=0)
ctx = ImageDraw.Draw(canvas)
for i in range(128):
c = chr(i)
if c.isprintable():
ctx.rectangle((0, 0, 20, 20), 0)
ctx.text((0, 0), c, font=ft, fill=255)
val = sum(canvas.getdata())
cmap.append((val, c))
m, n = max(cmap)[0], min(cmap)[0]
cmap = [((v - n) / (m - n) * 255, c) for v, c in cmap]
result: list[str] = []
for i in range(level):
v = i / level * 255
result.append(min(cmap, key=lambda x: abs(x[0] - v))[1])
return "".join(result if inv else result[::-1])
def convert(
img: Image.Image, width: int = 80, level: int = 16, invert: bool = False
) -> str:
cmaps = create_char_map(level=level, inv=invert)
W, H = width, int(width / img.width * img.height)
im_res = img.convert("L").resize((W, H), resample=Image.Resampling.BOX)
res: list[str] = []
for y in range(im_res.height):
line: list[str] = []
for x in range(im_res.width):
p = im_res.getpixel((x, y))
line.append(cmaps[int(p / 256 * level)])
res.append("".join(line))
return "\n".join(res)
if __name__ == "__main__":
args = parse_args()
im = Image.open(args.filename)
print(convert(im, args.width, args.level, args.invert))
@sooop
Copy link
Author

sooop commented Aug 30, 2022

PIL.Image.BOX will be changed to PIL.Image.Resampling.BOX

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