added color
This commit is contained in:
parent
2eb6dd716c
commit
72211f6688
137
ascii.py
Normal file → Executable file
137
ascii.py
Normal file → Executable file
|
@ -12,7 +12,7 @@ import numpy as np
|
|||
import numpngw
|
||||
|
||||
ASCII_CHARS = "$@%#*+=-:. "
|
||||
HEADERS = {'User-Agent': 'bix nood where dah ascii tiddies at'}
|
||||
HEADERS = {'User-Agent': 'Gimme the ascii.'}
|
||||
|
||||
|
||||
def scale_image(image, size=(100,100)):
|
||||
|
@ -51,22 +51,68 @@ def pixels_to_chars(image, reverse=False):
|
|||
"""
|
||||
range_width = int(255 / len(ASCII_CHARS)) + (255 % len(ASCII_CHARS) > 0)
|
||||
|
||||
pixels_in_image = list(image.getdata())
|
||||
pixels_to_chars = []
|
||||
for pixel_value in pixels_in_image:
|
||||
pixels = list(image.getdata())
|
||||
chars = []
|
||||
for pixel in pixels:
|
||||
if reverse:
|
||||
index = -int(pixel_value/range_width)-1
|
||||
index = -int(pixel/range_width)-1
|
||||
else:
|
||||
index = int(pixel_value/range_width)
|
||||
pixels_to_chars.append(ASCII_CHARS[index])
|
||||
index = int(pixel/range_width)
|
||||
chars.append(ASCII_CHARS[index])
|
||||
|
||||
return "".join(pixels_to_chars)
|
||||
chars = "".join(chars)
|
||||
chars = [chars[i:i + image.size[0]] for i in range(0, len(chars),
|
||||
image.size[0])]
|
||||
return "\n".join(chars)
|
||||
|
||||
|
||||
def char_color(pixel, code="irc"):
|
||||
"""
|
||||
Maps a color to a character based on the original pixel color.
|
||||
Calculates the distance from the provided color to each of the 16
|
||||
colors in the IRC and ANSI color codes and selects the closest match.
|
||||
"""
|
||||
colors_index = ["white", "black", "blue", "green", "red", "brown", "purple",
|
||||
"orange", "yellow", "light green", "teal", "cyan", "light blue", "pink",
|
||||
"grey", "silver"]
|
||||
|
||||
colors_rgb = {"white": (0,0,0), "black": (255,255,255), "blue": (0,0,255),
|
||||
"green": (0,255,0), "red": (255,0,0), "brown": (150,75,0),
|
||||
"purple": (128,0,128), "orange": (255,128,0), "yellow": (255,255,0),
|
||||
"light green": (191,255,0), "teal": (0,128,128), "cyan": (0,255,255),
|
||||
"light blue": (65,105,225), "pink":(255,192,203), "grey": (128,128,128),
|
||||
"silver": (192,192,192)}
|
||||
|
||||
colors_irc = {"white": "0", "black": "1", "blue": "2", "green": "3", "red": "4",
|
||||
"brown": "5", "purple": "6", "orange": "7", "yellow": "8", "light green": "9",
|
||||
"teal": "10", "cyan": "11", "light blue": "12", "pink": "13", "grey": "14",
|
||||
"silver": "15"}
|
||||
|
||||
colors_ansi = {"white": "[1;37m", "black": "[0;30m", "blue": "[0;34m",
|
||||
"green": "[0;32m", "red": "[0;31m", "brown": "[0;33m",
|
||||
"purple": "[0;35m", "orange": "[1;31m", "yellow": "[1;33m",
|
||||
"light green": "[1;32m", "teal": "[0;36m", "cyan": "[1;36m",
|
||||
"light blue": "[1;34m", "pink": "[1;35m", "grey": "[1;30m",
|
||||
"silver": "[0;37m"}
|
||||
|
||||
dist = [(abs(pixel[0] - colors_rgb[color][0])**2
|
||||
+ abs(pixel[1] - colors_rgb[color][1])**2
|
||||
+ abs(pixel[2] - colors_rgb[color][2])**2)**0.5
|
||||
for color in colors_index]
|
||||
|
||||
color = colors_index[dist.index(min(dist))]
|
||||
|
||||
if code == "irc":
|
||||
return colors_irc[color]
|
||||
elif code == "ansi":
|
||||
return colors_ansi[color]
|
||||
|
||||
|
||||
def open_image(imagePath):
|
||||
"""
|
||||
Opens the image at the supplied file path in PIL. If an internet URL
|
||||
is supplied, it will download the image and then open it.
|
||||
is supplied, it will download the image and then open it. Returns a
|
||||
PIL image object.
|
||||
"""
|
||||
try:
|
||||
if imagePath.startswith("http"):
|
||||
|
@ -77,39 +123,53 @@ def open_image(imagePath):
|
|||
else:
|
||||
image = Image.open(imagePath)
|
||||
except FileNotFoundError as e:
|
||||
return e
|
||||
except OSError:
|
||||
return e
|
||||
return f"File not found: {imagePath}"
|
||||
except Exception as e:
|
||||
return("Error opening image file: " + imagePath)
|
||||
return(f"Error opening image: {imagePath}\n{e}")
|
||||
|
||||
return image
|
||||
|
||||
|
||||
def image_to_ascii(image, reverse=False):
|
||||
def colorize(chars, image, code):
|
||||
"""
|
||||
Colorizes the ascii matrix.
|
||||
"""
|
||||
prefix = {"irc": "\03", "ansi":"\033"}
|
||||
chars = chars.split("\n")
|
||||
for j in range(0, image.size[1]):
|
||||
new_row = ""
|
||||
for k in range(0, image.size[0]):
|
||||
new_row += prefix[code] + char_color(image.getpixel((k,j)), code)
|
||||
new_row += chars[j][k]
|
||||
chars[j] = new_row
|
||||
return "\n".join(chars)
|
||||
|
||||
|
||||
def image_to_ascii(image, reverse=False, colors=None):
|
||||
"""
|
||||
Reads an image file and converts it to ascii art. Returns a
|
||||
newline-delineated string. If reverse is True, the ascii scale is
|
||||
reversed.
|
||||
"""
|
||||
image = scale_image(image)
|
||||
image = image.convert('L') # convert to grayscale
|
||||
image_grey = image.convert('L') # convert to grayscale
|
||||
|
||||
chars = pixels_to_chars(image, reverse)
|
||||
chars = pixels_to_chars(image_grey, reverse)
|
||||
|
||||
image_ascii = []
|
||||
for index in range(0, len(chars), image.size[0]):
|
||||
image_ascii.append(chars[index: index + image.size[0]])
|
||||
if colors:
|
||||
chars = colorize(chars, image, colors)
|
||||
image.close()
|
||||
image_grey.close()
|
||||
del(image)
|
||||
return "\n".join(image_ascii)
|
||||
del(image_grey)
|
||||
return chars
|
||||
|
||||
|
||||
def ascii_to_image(image_ascii):
|
||||
"""
|
||||
Creates a plain image and draws text on it.
|
||||
"""
|
||||
# TODO: make font type, size, and image size non-fixed
|
||||
# TODO: make font type and size non-fixed
|
||||
width = len(image_ascii[:image_ascii.index("\n")]) * 8
|
||||
height = (image_ascii.count("\n")+1) * 12 + 4
|
||||
|
||||
|
@ -120,7 +180,10 @@ def ascii_to_image(image_ascii):
|
|||
return image
|
||||
|
||||
|
||||
def handle_gif(output, reverse=False):
|
||||
def handle_gif(image, output, reverse=False):
|
||||
"""
|
||||
Handle gifs seperately.
|
||||
"""
|
||||
image = open_image(args.imagePath)
|
||||
ascii_seq = []
|
||||
new_image = ascii_to_image(image_to_ascii(image, reverse))
|
||||
|
@ -137,8 +200,6 @@ def handle_gif(output, reverse=False):
|
|||
# duration=60, loop=0, optimize=True)
|
||||
ascii_seq = [new_image] + ascii_seq
|
||||
np_ascii_seq = [np.array(im) for im in ascii_seq]
|
||||
#images2gif.writeGif(output, ascii_seq, nq=10, subRectangles=False)
|
||||
#imageio.mimsave(output, np_ascii_seq)
|
||||
with open(output, "wb") as file:
|
||||
numpngw.write_apng(file, np_ascii_seq)
|
||||
|
||||
|
@ -171,6 +232,11 @@ if __name__=='__main__':
|
|||
"--animated",
|
||||
action="store_true",
|
||||
help="Handles animated GIFs. Includes --image.")
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--color",
|
||||
type=str,
|
||||
help="Colorizes the ascii matrix.")
|
||||
parser.set_defaults(reverse=False, image=False, animated=False)
|
||||
args = parser.parse_args()
|
||||
|
||||
|
@ -180,16 +246,17 @@ if __name__=='__main__':
|
|||
if not args.output:
|
||||
parser.error("--image requires --output")
|
||||
|
||||
image = open_image(args.imagePath)
|
||||
if args.animated:
|
||||
handle_gif(args.output, args.reverse)
|
||||
handle_gif(image, args.output, args.reverse)
|
||||
exit()
|
||||
|
||||
image_ascii = image_to_ascii(image, args.reverse, args.color)
|
||||
if args.image:
|
||||
image = ascii_to_image(image_ascii)
|
||||
image.save(args.output, "PNG")
|
||||
elif args.output:
|
||||
with open(args.output, "w+") as file:
|
||||
file.write(image_ascii)
|
||||
else:
|
||||
image = open_image(args.imagePath)
|
||||
image_ascii = image_to_ascii(image, args.reverse)
|
||||
if args.image:
|
||||
image = ascii_to_image(image_ascii)
|
||||
image.save(args.output, "PNG")
|
||||
elif args.output:
|
||||
with open(args.output, "w+") as file:
|
||||
file.write(image_ascii)
|
||||
else:
|
||||
print(image_ascii)
|
||||
print(image_ascii)
|
||||
|
|
Loading…
Reference in New Issue
Block a user