converted windows newline characters to unix

This commit is contained in:
iou1name 2017-11-27 11:34:49 -05:00
parent 72211f6688
commit 646e876528

524
ascii.py
View File

@ -1,262 +1,262 @@
#! /usr/bin/env python3 #! /usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
ASCII ASCII
""" """
from io import BytesIO from io import BytesIO
import requests import requests
from PIL import Image, ImageFont, ImageDraw from PIL import Image, ImageFont, ImageDraw
#import imageio #import imageio
import numpy as np import numpy as np
import numpngw import numpngw
ASCII_CHARS = "$@%#*+=-:. " ASCII_CHARS = "$@%#*+=-:. "
HEADERS = {'User-Agent': 'Gimme the ascii.'} HEADERS = {'User-Agent': 'Gimme the ascii.'}
def scale_image(image, size=(100,100)): def scale_image(image, size=(100,100)):
""" """
Resizes an image while preserving the aspect ratio. Chooses the Resizes an image while preserving the aspect ratio. Chooses the
dimension to scale by based on whichever is larger, ensuring that dimension to scale by based on whichever is larger, ensuring that
neither width or height is ever larger than the accepted size tuple. neither width or height is ever larger than the accepted size tuple.
Because text characters are typically pretty close to a 1:2 rectangle, Because text characters are typically pretty close to a 1:2 rectangle,
we weight the width twice as much. we weight the width twice as much.
""" """
original_width, original_height = image.size original_width, original_height = image.size
original_width = original_width * 2 original_width = original_width * 2
if original_width > original_height: if original_width > original_height:
if original_width > size[0]: if original_width > size[0]:
new_width = size[0] new_width = size[0]
aspect_ratio = original_height/float(original_width) aspect_ratio = original_height/float(original_width)
new_height = int(aspect_ratio * new_width) new_height = int(aspect_ratio * new_width)
else: else:
new_width, new_height = image.size new_width, new_height = image.size
else: else:
if original_height > size[1]: if original_height > size[1]:
new_height = size[1] new_height = size[1]
aspect_ratio = original_width/float(original_height) aspect_ratio = original_width/float(original_height)
new_width = int(aspect_ratio * new_height) new_width = int(aspect_ratio * new_height)
else: else:
new_width, new_height = image.size new_width, new_height = image.size
image = image.resize((new_width, new_height)) image = image.resize((new_width, new_height))
return image return image
def pixels_to_chars(image, reverse=False): def pixels_to_chars(image, reverse=False):
""" """
Maps each pixel to an ascii char based on where it falls in the range Maps each pixel to an ascii char based on where it falls in the range
0-255 normalized to the length of ASCII_CHARS. 0-255 normalized to the length of ASCII_CHARS.
""" """
range_width = int(255 / len(ASCII_CHARS)) + (255 % len(ASCII_CHARS) > 0) range_width = int(255 / len(ASCII_CHARS)) + (255 % len(ASCII_CHARS) > 0)
pixels = list(image.getdata()) pixels = list(image.getdata())
chars = [] chars = []
for pixel in pixels: for pixel in pixels:
if reverse: if reverse:
index = -int(pixel/range_width)-1 index = -int(pixel/range_width)-1
else: else:
index = int(pixel/range_width) index = int(pixel/range_width)
chars.append(ASCII_CHARS[index]) chars.append(ASCII_CHARS[index])
chars = "".join(chars) chars = "".join(chars)
chars = [chars[i:i + image.size[0]] for i in range(0, len(chars), chars = [chars[i:i + image.size[0]] for i in range(0, len(chars),
image.size[0])] image.size[0])]
return "\n".join(chars) return "\n".join(chars)
def char_color(pixel, code="irc"): def char_color(pixel, code="irc"):
""" """
Maps a color to a character based on the original pixel color. Maps a color to a character based on the original pixel color.
Calculates the distance from the provided color to each of the 16 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 in the IRC and ANSI color codes and selects the closest match.
""" """
colors_index = ["white", "black", "blue", "green", "red", "brown", "purple", colors_index = ["white", "black", "blue", "green", "red", "brown", "purple",
"orange", "yellow", "light green", "teal", "cyan", "light blue", "pink", "orange", "yellow", "light green", "teal", "cyan", "light blue", "pink",
"grey", "silver"] "grey", "silver"]
colors_rgb = {"white": (0,0,0), "black": (255,255,255), "blue": (0,0,255), 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), "green": (0,255,0), "red": (255,0,0), "brown": (150,75,0),
"purple": (128,0,128), "orange": (255,128,0), "yellow": (255,255,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 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), "light blue": (65,105,225), "pink":(255,192,203), "grey": (128,128,128),
"silver": (192,192,192)} "silver": (192,192,192)}
colors_irc = {"white": "0", "black": "1", "blue": "2", "green": "3", "red": "4", colors_irc = {"white": "0", "black": "1", "blue": "2", "green": "3", "red": "4",
"brown": "5", "purple": "6", "orange": "7", "yellow": "8", "light green": "9", "brown": "5", "purple": "6", "orange": "7", "yellow": "8", "light green": "9",
"teal": "10", "cyan": "11", "light blue": "12", "pink": "13", "grey": "14", "teal": "10", "cyan": "11", "light blue": "12", "pink": "13", "grey": "14",
"silver": "15"} "silver": "15"}
colors_ansi = {"white": "[1;37m", "black": "[0;30m", "blue": "[0;34m", colors_ansi = {"white": "[1;37m", "black": "[0;30m", "blue": "[0;34m",
"green": "[0;32m", "red": "[0;31m", "brown": "[0;33m", "green": "[0;32m", "red": "[0;31m", "brown": "[0;33m",
"purple": "[0;35m", "orange": "[1;31m", "yellow": "[1;33m", "purple": "[0;35m", "orange": "[1;31m", "yellow": "[1;33m",
"light green": "[1;32m", "teal": "[0;36m", "cyan": "[1;36m", "light green": "[1;32m", "teal": "[0;36m", "cyan": "[1;36m",
"light blue": "[1;34m", "pink": "[1;35m", "grey": "[1;30m", "light blue": "[1;34m", "pink": "[1;35m", "grey": "[1;30m",
"silver": "[0;37m"} "silver": "[0;37m"}
dist = [(abs(pixel[0] - colors_rgb[color][0])**2 dist = [(abs(pixel[0] - colors_rgb[color][0])**2
+ abs(pixel[1] - colors_rgb[color][1])**2 + abs(pixel[1] - colors_rgb[color][1])**2
+ abs(pixel[2] - colors_rgb[color][2])**2)**0.5 + abs(pixel[2] - colors_rgb[color][2])**2)**0.5
for color in colors_index] for color in colors_index]
color = colors_index[dist.index(min(dist))] color = colors_index[dist.index(min(dist))]
if code == "irc": if code == "irc":
return colors_irc[color] return colors_irc[color]
elif code == "ansi": elif code == "ansi":
return colors_ansi[color] return colors_ansi[color]
def open_image(imagePath): def open_image(imagePath):
""" """
Opens the image at the supplied file path in PIL. If an internet URL 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. Returns a is supplied, it will download the image and then open it. Returns a
PIL image object. PIL image object.
""" """
try: try:
if imagePath.startswith("http"): if imagePath.startswith("http"):
res = requests.get(imagePath, headers=HEADERS, verify=True, res = requests.get(imagePath, headers=HEADERS, verify=True,
timeout=20) timeout=20)
res.raise_for_status() res.raise_for_status()
image = Image.open(BytesIO(res.content)) image = Image.open(BytesIO(res.content))
else: else:
image = Image.open(imagePath) image = Image.open(imagePath)
except FileNotFoundError as e: except FileNotFoundError as e:
return f"File not found: {imagePath}" return f"File not found: {imagePath}"
except Exception as e: except Exception as e:
return(f"Error opening image: {imagePath}\n{e}") return(f"Error opening image: {imagePath}\n{e}")
return image return image
def colorize(chars, image, code): def colorize(chars, image, code):
""" """
Colorizes the ascii matrix. Colorizes the ascii matrix.
""" """
prefix = {"irc": "\03", "ansi":"\033"} prefix = {"irc": "\03", "ansi":"\033"}
chars = chars.split("\n") chars = chars.split("\n")
for j in range(0, image.size[1]): for j in range(0, image.size[1]):
new_row = "" new_row = ""
for k in range(0, image.size[0]): for k in range(0, image.size[0]):
new_row += prefix[code] + char_color(image.getpixel((k,j)), code) new_row += prefix[code] + char_color(image.getpixel((k,j)), code)
new_row += chars[j][k] new_row += chars[j][k]
chars[j] = new_row chars[j] = new_row
return "\n".join(chars) return "\n".join(chars)
def image_to_ascii(image, reverse=False, colors=None): def image_to_ascii(image, reverse=False, colors=None):
""" """
Reads an image file and converts it to ascii art. Returns a Reads an image file and converts it to ascii art. Returns a
newline-delineated string. If reverse is True, the ascii scale is newline-delineated string. If reverse is True, the ascii scale is
reversed. reversed.
""" """
image = scale_image(image) image = scale_image(image)
image_grey = image.convert('L') # convert to grayscale image_grey = image.convert('L') # convert to grayscale
chars = pixels_to_chars(image_grey, reverse) chars = pixels_to_chars(image_grey, reverse)
if colors: if colors:
chars = colorize(chars, image, colors) chars = colorize(chars, image, colors)
image.close() image.close()
image_grey.close() image_grey.close()
del(image) del(image)
del(image_grey) del(image_grey)
return chars return chars
def ascii_to_image(image_ascii): def ascii_to_image(image_ascii):
""" """
Creates a plain image and draws text on it. Creates a plain image and draws text on it.
""" """
# TODO: make font type and size non-fixed # TODO: make font type and size non-fixed
width = len(image_ascii[:image_ascii.index("\n")]) * 8 width = len(image_ascii[:image_ascii.index("\n")]) * 8
height = (image_ascii.count("\n")+1) * 12 + 4 height = (image_ascii.count("\n")+1) * 12 + 4
font = ImageFont.truetype("LiberationMono-Regular.ttf", 14) font = ImageFont.truetype("LiberationMono-Regular.ttf", 14)
image = Image.new("RGB", (width, height), (255,255,255)) image = Image.new("RGB", (width, height), (255,255,255))
draw = ImageDraw.Draw(image) draw = ImageDraw.Draw(image)
draw.text((0,0), image_ascii, (0,0,0), font=font, spacing=0) draw.text((0,0), image_ascii, (0,0,0), font=font, spacing=0)
return image return image
def handle_gif(image, output, reverse=False): def handle_gif(image, output, reverse=False):
""" """
Handle gifs seperately. Handle gifs seperately.
""" """
image = open_image(args.imagePath) image = open_image(args.imagePath)
ascii_seq = [] ascii_seq = []
new_image = ascii_to_image(image_to_ascii(image, reverse)) new_image = ascii_to_image(image_to_ascii(image, reverse))
image.seek(1) image.seek(1)
while True: while True:
try: try:
im = ascii_to_image(image_to_ascii(image, reverse)) im = ascii_to_image(image_to_ascii(image, reverse))
ascii_seq.append(im) ascii_seq.append(im)
image.seek(image.tell()+1) image.seek(image.tell()+1)
except EOFError: except EOFError:
break # end of sequence break # end of sequence
#new_image.save(output, save_all=True, append_images=ascii_seq, #new_image.save(output, save_all=True, append_images=ascii_seq,
# duration=60, loop=0, optimize=True) # duration=60, loop=0, optimize=True)
ascii_seq = [new_image] + ascii_seq ascii_seq = [new_image] + ascii_seq
np_ascii_seq = [np.array(im) for im in ascii_seq] np_ascii_seq = [np.array(im) for im in ascii_seq]
with open(output, "wb") as file: with open(output, "wb") as file:
numpngw.write_apng(file, np_ascii_seq) numpngw.write_apng(file, np_ascii_seq)
if __name__=='__main__': if __name__=='__main__':
import argparse import argparse
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Converts an image file to ascii art.") description="Converts an image file to ascii art.")
parser.add_argument( parser.add_argument(
"imagePath", "imagePath",
help="The full path to the image file.") help="The full path to the image file.")
parser.add_argument( parser.add_argument(
"-r", "-r",
"--reverse", "--reverse",
action="store_true", action="store_true",
help="Reverses the ascii scale.") help="Reverses the ascii scale.")
parser.add_argument( parser.add_argument(
"-o", "-o",
"--output", "--output",
help="Outputs the ascii art into a file at the specified path.") help="Outputs the ascii art into a file at the specified path.")
parser.add_argument( parser.add_argument(
"-i", "-i",
"--image", "--image",
action="store_true", action="store_true",
help="Outputs the ascii art as an image rather than plain text. \ help="Outputs the ascii art as an image rather than plain text. \
Requires --output.") Requires --output.")
parser.add_argument( parser.add_argument(
"-a", "-a",
"--animated", "--animated",
action="store_true", action="store_true",
help="Handles animated GIFs. Includes --image.") help="Handles animated GIFs. Includes --image.")
parser.add_argument( parser.add_argument(
"-c", "-c",
"--color", "--color",
type=str, type=str,
help="Colorizes the ascii matrix.") help="Colorizes the ascii matrix.")
parser.set_defaults(reverse=False, image=False, animated=False) parser.set_defaults(reverse=False, image=False, animated=False)
args = parser.parse_args() args = parser.parse_args()
if args.animated: # --animated includes --image if args.animated: # --animated includes --image
args.image = True args.image = True
if args.image: # --image requires --output if args.image: # --image requires --output
if not args.output: if not args.output:
parser.error("--image requires --output") parser.error("--image requires --output")
image = open_image(args.imagePath) image = open_image(args.imagePath)
if args.animated: if args.animated:
handle_gif(image, args.output, args.reverse) handle_gif(image, args.output, args.reverse)
exit() exit()
image_ascii = image_to_ascii(image, args.reverse, args.color) image_ascii = image_to_ascii(image, args.reverse, args.color)
if args.image: if args.image:
image = ascii_to_image(image_ascii) image = ascii_to_image(image_ascii)
image.save(args.output, "PNG") image.save(args.output, "PNG")
elif args.output: elif args.output:
with open(args.output, "w+") as file: with open(args.output, "w+") as file:
file.write(image_ascii) file.write(image_ascii)
else: else:
print(image_ascii) print(image_ascii)