Last active
June 5, 2022 17:55
-
-
Save BillyDoesDev/674bf416ba32d297334dacd57dea1849 to your computer and use it in GitHub Desktop.
A tool to convert images to Braille text art, written in Python3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
A tool to convert images into Braille art | |
Depends on: Pillow - Python Imaging Library | |
To install, do: | |
$ pip install Pillow | |
Usage: | |
$ python imgToBraille.py --help | |
""" | |
from PIL import Image | |
# Codepoint refrence: https://www.ssec.wisc.edu/~tomw/java/unicode.html#x2800 | |
def pixels_to_character(spam: list): | |
## Braille code is | |
## represented like so: ** # So, we index them like: | |
## 1 4 ** 0 3 | |
## 2 5 ** 1 4 | |
## 3 6 ** 2 5 | |
## 7 8 ** 6 7 | |
## So, for example, if we have a code- ⠳, | |
## We can represent it like [0, 0, 1, 1, 0, 0, 1, 1], | |
## where the 0 represents a dot (if invert mode is not selected that is...) | |
## Coincidentally, the base 10 (decimal) representation of the above binary sequence | |
## is the offset which we need to get the corresponding Braille Code | |
# codepoint_offset = sum([i << shift_value for shift_value, i in enumerate(spam)]) | |
codepoint_offset = int( | |
"".join(str(_) for _ in spam), base=2 | |
) # idk, easier to understand maybe? | |
return chr(10240 + codepoint_offset) | |
def img_to_braille(path_:str, width_=0, invert=False) -> str: | |
with Image.open(path_).convert("L") as im: # opening the image in BnW mode | |
width_ = im.width if not width_ else width_ # default img width if no args are passed | |
im = im.resize((width_, ((im.height * width_) // im.width))) | |
output = "" | |
## parse pixels in a 4 by 2 pattern, cuz one full braille character is of type -> ⣿ | |
for y in range(0, im.height - 4, 4): # looping till 4 pixels less | |
for x in range(0, im.width - 2, 2): # looping till 2 pixels less | |
braille_info = list([0] * 8) | |
dot_index = 0 | |
brightness = 0 | |
for y_ in range(4): | |
for x_ in range(2): | |
brightness += im.getpixel((x + x_, y + y_)) | |
if invert: # output will be inverted | |
if im.getpixel((x + x_, y + y_)) <= 128: # pixel is dark | |
braille_info[dot_index] = 1 | |
else: | |
if im.getpixel((x + x_, y + y_)) >= 128: # pixel is light | |
braille_info[dot_index] = 1 | |
dot_index += 1 | |
brightness //= 8 | |
output += pixels_to_character(braille_info) | |
## uncomment to use spaces for completely dark pixels instaed | |
# output = output.replace("⠀", " ") | |
output += "\n" | |
return output | |
def img_to_character(path_:str, width_=0, invert=False, outline=False) -> str: | |
density = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'. " | |
density = density[::-1] if invert else density | |
with Image.open(path_).convert("L") as im: # opening the image in BnW mode | |
width_ = im.width if not width_ else width_ # default img width if no args are passed | |
im = im.resize((width_, ((im.height * width_) // im.width))) | |
output = "" | |
# Looping by 4 pixels at a time.. just because... | |
for y in range(0, im.height - 4, 4): | |
for x in range(0, im.width - 4, 4): | |
brightness = 0 | |
for y_ in range(4): | |
for x_ in range(4): | |
brightness += im.getpixel((x + x_, y + y_)) | |
brightness //= 16 | |
output += density[len(density)*brightness//255-1] if outline else density[::-1][min(len(density)-1, len(density)*brightness//255)] | |
output += "\n" | |
return output | |
if __name__ == "__main__": | |
import argparse | |
import os | |
import sys | |
parser = argparse.ArgumentParser( | |
description="A tool to convert images into Braille art" | |
) | |
parser.version = "1.0 [beta]" | |
parser.add_argument("-v", "--version", action="version") | |
parser.add_argument("path", type=str, help="path to the desired image") | |
parser.add_argument( | |
"-w", metavar="width", type=int, help="specify the ouput width [in px]" | |
) | |
parser.add_argument("-i", action="store_true", help="invert output") | |
parser.add_argument("-o", action="store_true", help="just get the ouline if printing using characters") | |
args = parser.parse_args() | |
# BAD CODE. DO NOT TOUCH! | |
# well, in my defence, I was pretty shit at using argparse at the time | |
# and I'm super lazy now to go ahead and refactor it | |
# so yeah, deal with it. fuck you | |
if not os.path.isfile(args.path): | |
print("Invalid file path!") | |
sys.exit() | |
else: | |
path = os.path.join(args.path) | |
if args.w: | |
width = args.w | |
else: | |
width = 0 | |
print(img_to_braille(path, width, args.i if args.i else False)) | |
# print(img_to_character(path, width, args.i if args.i else False, outline=args.o if args.o else False)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment