Spaces:
Build error
Build error
File size: 6,891 Bytes
452e643 |
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 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
"""
CS5330 Fall 2024
Author: Calvin Lo
Lab 1: ASCII Art
This program generates the ASCII art representation of
an input image and compares the result to the original image
using SSIM.
"""
import gradio as gr
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont, ImageFilter, ImageEnhance
from skimage.metrics import structural_similarity as ssim
# ASCII characters to map pixel intensity
ASCII_CHARS = [
' ', '.', ',', ':', ';', 'i', 'l', '!', 'I', '1', 't', 'o', 'r', 'x', 'z', 'v',
'u', 'n', 'm', 'w', 'Q', 'B', 'N', 'M', '@'
]
def preprocess_image(image, new_width=150):
"""
This function performs the following operations on the input image:
-enhance contrast
-perform edge detection (Sobel)
-denoise edges
-enhance contrast
-resize image
"""
# Open the image and convert to grayscale
image = Image.fromarray(image)
image = image.convert("L")
# Contrast limited adaptive histogram equalization
opencv_image = np.array(image)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
equalized_image = clahe.apply(opencv_image)
# Apply Sobel edge detection
sobelx = cv2.Sobel(equalized_image, cv2.CV_64F, 1, 0, ksize=5)
sobely = cv2.Sobel(equalized_image, cv2.CV_64F, 0, 1, ksize=5)
edges = np.hypot(sobelx, sobely) # Combine gradients
# Normalize the result to fit into the 0-255 grayscale range
edges = np.uint8(255 * edges / np.max(edges))
# Denoise the edges using Non-Local Means Denoising
edges = cv2.fastNlMeansDenoising(edges, None, h=30, templateWindowSize=7, searchWindowSize=21)
# Improve contrast of edges
edges_image = Image.fromarray(edges)
enhancer = ImageEnhance.Contrast(edges_image)
enhanced_edges = enhancer.enhance(1.5)
# enhanced_edges_array = np.array(enhanced_edges)
# Invert the grayscale values of the original image
inverted_image = 255 - np.array(equalized_image)
# Convert sharpened edges to array for blending
sharpened_edges_array = np.array(enhanced_edges )
# Blend the inverted image with the sharpened edges using the blend_percentage
blended_image = cv2.addWeighted(sharpened_edges_array, 1 - blend_percentage,
inverted_image, blend_percentage, 0)
# Resize the image for ASCII aspect ratio
width, height = image.size
aspect_ratio = height / width * 0.55 # Adjusting for ASCII aspect ratio
new_height = int(aspect_ratio * new_width)
resized_blended_image = Image.fromarray(blended_image).resize((new_width, new_height))
return resized_blended_image
def image_to_ascii(image):
"""
This function generates an ASCII representation of images.
"""
pixels = np.array(image)
ascii_str = ""
for row in pixels:
for pixel in row:
# Mapping pixel intensity to ASCII
ascii_str += ASCII_CHARS[pixel // 10]
ascii_str += "\n"
return ascii_str
def ascii_to_image(ascii_art, font_size=12, output_file='ascii_art.png'):
""""
This function converts ASCII text art to a png image.
"""
# Specify font
font_path = "DejaVuSansMono.ttf"
font = ImageFont.truetype(font_path, font_size)
# Get the required image dimensions
lines = ascii_art.splitlines()
max_width = max(font.getbbox(line)[2] for line in lines) # Get max width
img_height = len(lines) * font_size
# Generate image with white background
image = Image.new("RGB", (max_width, img_height), "white")
draw = ImageDraw.Draw(image)
# Draw each character from the ASCII text on the image
for y, line in enumerate(lines):
draw.text((0, y * font_size), line, fill="black", font=font)
image.save(output_file)
return image
def compare_ssim(image1, image2):
"""
This function generates the structural similarity index
measure for the two input images.
"""
# Convert original color image to grayscale
image1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
# Convert ASCII image to grayscale and cv2 format
image2 = np.array(image2.convert("L"))
# Resize so both images have the same dimensions.
image2 = cv2.resize(image2, (image1.shape[1], image1.shape[0]))
score, _ = ssim(image1, image2, full=True)
return score
def process_image(image):
"""
This function takes an image and generates:
-ASCII art as a string
-ASCII text file
-Compute SSIM for the ASCII art image and the original image
"""
# Get image edges and convert to ASCII
edges = preprocess_image(image)
ascii_art = image_to_ascii(edges)
# Convert ASCII art back to an image for SSIM calculation
ascii_image = ascii_to_image(ascii_art)
# Calculate SSIM
ssim_score = compare_ssim(image, ascii_image)
# Write ASCII art to txt file
ascii_txt_path = "ascii_art_output.txt"
with open(ascii_txt_path, "w") as f:
f.write(ascii_art)
return ascii_art, ssim_score, ascii_txt_path
def gradio_interface(image):
"""
This function generates the interface outputs for a given input image.
The outputs include the formatted SSIM, ASCII art, and ASCII art txt file path.
"""
ascii_art, ssim_score, ascii_txt_path = process_image(image)
ssim_formatted = f'<div style="font-size: 20px;">The structural similarity index measure is {ssim_score:.3f}</div>'
# Wrap the ASCII art in HTML with custom font size
styled_ascii_art = f'<pre style="font-family: monospace; font-size: 7px;">{ascii_art}</pre>'
return styled_ascii_art, ssim_formatted, ascii_txt_path
def main():
# Setup Gradio interface
with gr.Blocks() as interface:
# Add title and description
gr.Markdown("<h1 style='font-size: 40px;'>Image to ASCII Art Converter</h1>")
gr.Markdown("<p style='font-size: 18px;'>Upload an image, and this tool will generate an ASCII art version of the image. It also calculates the Structural Similarity Index (SSIM) to evaluate the quality.</p>")
# Image input and ASCII art + SSIM score output
image_input = gr.Image(label="Upload an image")
ascii_art_output = gr.HTML()
ssim_score_output = gr.HTML()
# File output for downloading, initially hidden
ascii_file_output = gr.File(label="Download ASCII Art")
# Update interface when image is uploaded
image_input.change(fn=gradio_interface,
inputs=image_input,
outputs=[ascii_art_output, ssim_score_output, ascii_file_output])
# Launch interface
interface.launch()
if __name__ == "__main__":
main() |