ASCII_ART / app.py
RedBottle13's picture
update app.py
c84a261
raw
history blame
5.16 kB
from PIL import Image, ImageDraw, ImageOps, ImageFilter, ImageFont
import numpy as np
import math
import gradio as gr
##### CONSTANTS #####
# ASCII characters used to represent image pixels, reversed for better contrast in mapping
CHARS = ' .:-=+*#%@'[::-1]
# Convert the characters to a list for easier access
CHAR_ARRAY = list(CHARS)
# Number of available ASCII characters
CHAR_LEN = len(CHAR_ARRAY)
# Grayscale level for each ASCII character, determining how many shades of gray each character represents
GRAYSCALE_LEVEL = CHAR_LEN / 256
# Scaling factor to resize the image
SCALE = 0.15
# Character dimensions (width and height in pixels) used to match image aspect ratio to character aspect ratio
CHAR_W = 6
CHAR_H = 14
##### FUNCTIONS #####
def getChar(inputInt, gamma=1.8):
"""Map a grayscale pixel value to an ASCII character with gamma correction applied."""
# Adjust the input pixel intensity using gamma correction for perceptual brightness adjustment
inputInt = (inputInt / 255) ** gamma * 255
# Map the corrected pixel value to an appropriate ASCII character
return CHAR_ARRAY[math.floor(inputInt * GRAYSCALE_LEVEL)]
def load_and_preprocess_image(image):
"""Resize and preprocess the input image, adjusting contrast and blurring for better ASCII conversion."""
width, height = image.size
# Resize image, adjusting aspect ratio to fit ASCII character dimensions
im = image.resize((int(SCALE * width), int(SCALE * height * (CHAR_W / CHAR_H))))
# Enhance contrast to bring out more detail in the ASCII representation
im = ImageOps.equalize(im, mask=None)
# Apply a slight blur to reduce noise and simplify pixel values
im = im.filter(ImageFilter.GaussianBlur(radius=0.5))
return im
def create_ascii_art(im):
"""Convert a preprocessed image into ASCII art by mapping grayscale values to ASCII characters."""
# Convert the image to grayscale
grayscale_image = im.convert("L")
# Get the pixel values as a NumPy array for easier processing
pix = np.array(grayscale_image)
width, height = grayscale_image.size
# Create a string that holds the ASCII art, where each character represents a pixel
ascii_art = ""
for i in range(height):
for j in range(width):
# Append the corresponding ASCII character for each pixel
ascii_art += getChar(pix[i, j])
# Newline after each row to maintain image structure
ascii_art += '\n'
return ascii_art
def draw_ascii_image(ascii_art_string, char_width, char_height, font_size):
"""Draw the ASCII art string onto an image."""
# Split the ASCII art string into lines
lines = ascii_art_string.split('\n')
# Determine the dimensions of the image based on the number of characters
width = max(len(line) for line in lines) # Maximum line width
height = len(lines) # Number of lines (height)
# Create a blank white image based on the ASCII art size and font size
img_width = width * char_width
img_height = height * char_height
ascii_image = Image.new("RGB", (img_width, img_height), "white")
draw = ImageDraw.Draw(ascii_image)
# Use default or custom font (Pillow's default font in this case)
font = ImageFont.load_default()
# Draw the ASCII art on the image
for i, line in enumerate(lines):
# Draw each line of the ASCII art onto the image
draw.text((0, i * char_height), line, font=font, fill="black")
return ascii_image
def process_image(image):
"""Process the input image to generate both an ASCII art image and a downloadable text file."""
# Resize and preprocess the image
resized_image = load_and_preprocess_image(image)
# Generate the ASCII art as text
ascii_art = create_ascii_art(resized_image)
# Create an image from the ASCII art characters
output_image = draw_ascii_image(ascii_art, char_width=CHAR_W, char_height=CHAR_H, font_size=10)
# Save the ASCII art as a text file
ascii_txt_path = "ascii_art.txt"
with open(ascii_txt_path, "w") as text_file:
text_file.write(ascii_art)
return output_image, ascii_txt_path
##### GRADIO INTERFACE #####
def gradio_interface(image):
"""Gradio interface function to handle user input and return the ASCII art image and text file."""
ascii_image, txt_file = process_image(image)
return ascii_image, txt_file
# Set up the Gradio interface
demo = gr.Interface(
fn=gradio_interface,
inputs=gr.Image(type="pil", label="Upload an Image", height=300),
outputs=[
gr.Image(type="pil", label="ASCII Art Image", height=300),
gr.File(label="Download ASCII Art Text File", height=50),
],
title="ASCII Art Generator",
description="Upload an image, and this tool will generate ASCII art and provide a downloadable text file of the result.",
allow_flagging="never",
examples=[
['images/building.jpg'],
['images/cat.webp'],
['images/mountain.webp'],
['images/people.jpg'],
['images/Northeastern_seal.png'],
['images/einstein.jpg'],
],
)
demo.launch(share=True)