Spaces:
Running
Running
File size: 5,053 Bytes
1d9074b |
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 |
"""
Jialu Bi
CS5330 Lab 1
Generator for ASCII art
"""
import cv2
import numpy as np
import matplotlib.pyplot as plt
import gradio as gr
from skimage.metrics import structural_similarity as ssim
from skimage.metrics import mean_squared_error
# A class for ASCII genrator related functions
class ASCIIGenerator:
def __init__(self):
self.ASCIIChars = ['#', '@', '%', '*', '+', '-', '.', ' ']
# self.ASCIIChars = ['█', '▓', '▒', '░', ' ']
# Resize the image to make sure consistent result
def resizeImg(self, img):
h, w, c = img.shape
aspectRatio = w / h
imgResized = cv2.resize(img, (int(160 * aspectRatio), 160))
return imgResized
# Convert to gray scale
# The input should be the resized image
def cvtGrayScale(self, imgResized):
R, G, B = cv2.split(imgResized)
R, G, B = R.astype(np.float32), G.astype(np.float32), B.astype(np.float32)
# Apply weights for more refined control
imgGray = 0.299 * R + 0.587 * G + 0.114 * B
imgGray = np.clip(imgGray, 0, 255).astype(np.uint8)
return imgGray
# Preprocess the image
# The input should be the gray scale image
def preprocess(self, imgGray):
# Histogram equalization
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
imgEqualized = clahe.apply(imgGray)
# Increase contrast
imgEqualized = cv2.convertScaleAbs(imgEqualized, alpha=1.6, beta=0)
# Detect the edges and emphasize them
edges = cv2.Canny(imgEqualized, threshold1=50, threshold2=150)
invertedEdges = cv2.bitwise_not(edges)
imgEnhanced = cv2.addWeighted(imgGray, 0.7, invertedEdges, 0.3, 0)
return imgEnhanced
# Map to ASCII Characters
# The input should be the preprocessed image
def mapASCII(self, imgEnhanced):
ASCIIArt = ''
h, w = imgEnhanced.shape
# Iterate through the pixels to create the ASCII art
for i in range(h):
for j in range(w):
pixelVal = imgEnhanced[i, j]
# Assign a proper ASCII character according to the pixel's intensity value
ASCIIArt += self.ASCIIChars[pixelVal * len(self.ASCIIChars) // 256]
ASCIIArt += '\n'
return ASCIIArt
# Main function for generating ASCII art
def generateASCIIArt(self, img):
imgResized = self.resizeImg(img)
imgGray = self.cvtGrayScale(imgResized)
imgEnhanced = self.preprocess(imgGray)
ASCIIArt = self.mapASCII(imgEnhanced)
return ASCIIArt
# Convert ASCII art back to grayscale for performance evaluation
def ASCIItoGray(self, ASCIIArt):
# Create a dictionary to store the corresponding intensity of ASCII characters
ASCIICharsVal = {}
offset = 0
for char in self.ASCIIChars:
ASCIICharsVal[char] = offset
offset += 256 // len(self.ASCIIChars)
# Map ASCII characters back to pixels
ASCIIGray = []
lines = ASCIIArt.split('\n')[:-1]
for line in lines:
row = []
for char in line:
row.append(ASCIICharsVal[char])
ASCIIGray.append(row)
ASCIIGray = np.array(ASCIIGray, dtype=np.uint8)
return ASCIIGray
# Evaluate the similarity between the grayscale version of the original image and its ASCII art version
def compare(self, ASCIIGray, imgGray):
# Compute SSIM
ssimVal, _ = ssim(imgGray, ASCIIGray, full=True)
# Compute MSE
mseVal = mean_squared_error(imgGray, ASCIIGray)
# Compute PSNR
psnrVal = cv2.PSNR(imgGray, ASCIIGray)
return ssimVal, mseVal, psnrVal
# Gradio interface function
def generateArt(img):
if img is None:
return None, None, None
# Generate the ASCII art
generator = ASCIIGenerator()
ASCIIArt = generator.generateASCIIArt(img)
# Evaluate the performance of the algorithm
imgResized = generator.resizeImg(img)
imgGray = generator.cvtGrayScale(imgResized)
ASCIIGray = generator.ASCIItoGray(ASCIIArt)
scores = generator.compare(ASCIIGray, imgGray)
report = f'SSIM: {round(scores[0], 2)}, MSE: {round(scores[1], 2)}, PSNR: {round(scores[2], 2)}'
# Save the ASCII art to a text file
outputPath = 'ascii_art.txt'
with open(outputPath, 'w', encoding='utf-8') as file:
file.write(ASCIIArt)
return f'<pre style="font-size: 8px; line-height: 0.62;">{ASCIIArt}</pre>', outputPath, report
# Create a Gradio interface
with gr.Blocks() as demo:
gr.Markdown('## ASCII Art Generator\nUpload an image to generate ASCII art.')
imgInput = gr.Image(type='numpy', label='Upload Image')
ASCIIOutput = gr.HTML(label='ASCII Art Output')
fileOutput = gr.File(label='Download ASCII Art')
scoreOutput = gr.Textbox(label='ASCII Conversion Performance')
imgInput.change(fn=generateArt, inputs=imgInput, outputs=[ASCIIOutput, fileOutput, scoreOutput])
demo.launch(share=True) |