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 = np.array(enhanced_edges) # Resize the image for ASCII aspect ratio width, height = image.size aspect_ratio = height / width * 0.45 # Adjusting for ASCII aspect ratio new_height = int(aspect_ratio * new_width) resized_image = Image.fromarray(enhanced_edges).resize((new_width, new_height)) return resized_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'
{ascii_art}'
return styled_ascii_art, ssim_formatted, ascii_txt_path
# Setup Gradio interface
with gr.Blocks() as interface:
# Add title and description
gr.Markdown("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.
") # 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()