Spaces:
Running
Running
File size: 6,386 Bytes
81e3815 6861f2c 81e3815 6861f2c 81e3815 |
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 |
import cv2
import numpy as np
from PIL import Image, ImageEnhance, ImageFilter
from skimage.metrics import structural_similarity as ssim
# Constants
DEFAULT_WIDTH = 150
CONTRAST_FACTOR = 2.0
ASCII_CHARS = "@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|)(1}{][?-_+~i!lI;:,\"^`'. "
ASCII_CHARS = list(ASCII_CHARS)
def resize_image(image, new_width=DEFAULT_WIDTH, resampling_filter=Image.LANCZOS):
"""Resize the image while maintaining the aspect ratio."""
width, height = image.size
aspect_ratio = height / width
new_height = int(new_width * aspect_ratio)
resized_image = image.resize((new_width, new_height), resampling_filter)
# Apply a sharpening filter after resizing
sharpened_image = resized_image.filter(ImageFilter.SHARPEN)
return sharpened_image
def convert_to_grayscale(image):
"""Convert the image to grayscale using weighted sum of RGB channels."""
# Get the image data
pixels = image.load()
width, height = image.size
# Check if the image is already in grayscale mode
if image.mode == 'L':
return image
# Create a new grayscale image
grayscale_image = Image.new("L", (width, height))
grayscale_pixels = grayscale_image.load()
# Define the weights for each channel
weights = (0.299, 0.587, 0.114)
# Process each pixel
for x in range(width):
for y in range(height):
r, g, b = pixels[x, y][:3] # Get RGB values
# Calculate the grayscale value using the weighted sum
gray = int(weights[0] * r + weights[1] * g + weights[2] * b)
grayscale_pixels[x, y] = gray
return grayscale_image
def adjust_contrast(image, factor=CONTRAST_FACTOR):
"""Adjust the contrast of the image."""
enhancer = ImageEnhance.Contrast(image)
return enhancer.enhance(factor)
def apply_edge_detection(image):
"""Apply edge detection to the image using Canny algorithm with preprocessing and automatic threshold detection."""
# Convert image to numpy array
image_np = np.array(image)
# Print an error message if the image is not in grayscale
if len(image_np.shape) == 3:
raise ValueError("Edge detection requires a grayscale image.")
# Apply Gaussian blur to reduce noise
blurred_image = cv2.GaussianBlur(image_np, (5, 5), 1.4)
# Compute the median of the pixel intensities
median_intensity = np.median(blurred_image)
# Set the lower and upper thresholds for Canny edge detection
lower_threshold = int(max(0, (1.0 - 0.2) * median_intensity))
upper_threshold = int(min(255, (1.0 + 0.2) * median_intensity))
# Apply Canny edge detection with the computed thresholds
edges = cv2.Canny(blurred_image, threshold1=lower_threshold, threshold2=upper_threshold)
# Convert the result back to PIL Image and return
edge_image = Image.fromarray(edges)
return edge_image
def map_pixels_to_ascii(image):
"""Map the pixel values of the image to ASCII characters."""
pixels = image.getdata()
ascii_str = ""
num_chars = len(ASCII_CHARS)
for pixel in pixels:
index = pixel * (num_chars - 1) // 255
ascii_str += ASCII_CHARS[index]
return ascii_str
def format_ascii_art(ascii_str, width):
"""Format the ASCII string into a structured ASCII art."""
ascii_art = ""
for i in range(0, len(ascii_str), width):
ascii_art += ascii_str[i:i+width] + "\n"
return ascii_art
def process_image(image):
"""Process the input image to prepare it for ASCII art generation."""
if not isinstance(image, Image.Image):
image = Image.fromarray(image)
grayscale_image = convert_to_grayscale(image)
adjusted_image = adjust_contrast(grayscale_image)
adjusted_image = resize_image(adjusted_image)
return adjusted_image
def generate_ascii_art(image):
"""Generate ASCII art from the given image."""
# Ensure the input is a PIL Image object
adjusted_image = process_image(image)
ascii_str = map_pixels_to_ascii(adjusted_image)
ascii_art = format_ascii_art(ascii_str, adjusted_image.width)
return ascii_art
def ascii_to_image(ascii_art, width):
"""Convert ASCII art back to a grayscale image."""
ascii_chars = ASCII_CHARS
num_chars = len(ascii_chars)
# Split the ASCII art into lines
ascii_lines = ascii_art.split('\n')
height = len(ascii_lines)
# Create a blank image
image = np.zeros((height, width), dtype=np.uint8)
for y, line in enumerate(ascii_lines):
for x, char in enumerate(line):
if char in ascii_chars:
index = ascii_chars.index(char)
pixel_value = index * 255 // (num_chars - 1)
image[y, x] = pixel_value
# Convert the numpy array to a PIL Image
image = Image.fromarray(image)
image = adjust_contrast(image)
return image
def calculate_similarity(original_image, ascii_art):
# Convert ASCII art back to image
ascii_image = ascii_to_image(ascii_art, DEFAULT_WIDTH)
# Ensure ascii_image is a PIL Image object
if not isinstance(ascii_image, Image.Image):
ascii_image = Image.fromarray(ascii_image)
# convert original image to grayscale
original_image = convert_to_grayscale(original_image)
# Resize the original image to the desired width
original_resize = original_image.resize((ascii_image.width, ascii_image.height), Image.LANCZOS)
original_resize = original_resize.filter(ImageFilter.SHARPEN)
# Apply edge detection
original_image_edge = apply_edge_detection(original_resize)
# convert the original image to binary
threshold = 0
original_image_edge = original_image_edge.point(lambda p: p > threshold and 255)
ascii_image_edge = apply_edge_detection(ascii_image)
ascii_image_edge = ascii_image_edge.point(lambda p: p > threshold and 255)
# Convert images to numpy arrays
original_array = np.array(original_image_edge)
ascii_array = np.array(ascii_image_edge)
# Check if the dimensions match
if original_array.shape != ascii_array.shape:
raise ValueError(f"Dimension mismatch: Original Image {original_array.shape}, ASCII Image {ascii_array.shape}")
# Calculate SSIM
ssim_value = ssim(original_array, ascii_array)
# Calculate MSE
mse = np.mean((original_array - ascii_array) ** 2)
return ssim_value, mse
|