Spaces:
Runtime error
Runtime error
File size: 5,159 Bytes
83fd0ba c84a261 83fd0ba |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
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)
|