local5 commited on
Commit
d07527a
·
verified ·
1 Parent(s): eb01cac

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +188 -198
app.py CHANGED
@@ -1,199 +1,189 @@
1
- """
2
- CS5330 Fall 2024
3
- Author: Calvin Lo
4
- Lab 1: ASCII Art
5
-
6
- This program generates the ASCII art representation of
7
- an input image and compares the result to the original image
8
- using SSIM.
9
- """
10
-
11
- import gradio as gr
12
- import cv2
13
- import numpy as np
14
- from PIL import Image, ImageDraw, ImageFont, ImageFilter, ImageEnhance
15
- from skimage.metrics import structural_similarity as ssim
16
-
17
- # ASCII characters to map pixel intensity
18
- ASCII_CHARS = [
19
- ' ', '.', ',', ':', ';', 'i', 'l', '!', 'I', '1', 't', 'o', 'r', 'x', 'z', 'v',
20
- 'u', 'n', 'm', 'w', 'Q', 'B', 'N', 'M', '@'
21
- ]
22
-
23
-
24
- def preprocess_image(image, new_width=150):
25
- """
26
- This function performs the following operations on the input image:
27
- -enhance contrast
28
- -perform edge detection (Sobel)
29
- -denoise edges
30
- -enhance contrast
31
- -resize image
32
- """
33
- # Open the image and convert to grayscale
34
- image = Image.fromarray(image)
35
- image = image.convert("L")
36
-
37
- # Contrast limited adaptive histogram equalization
38
- opencv_image = np.array(image)
39
- clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
40
- equalized_image = clahe.apply(opencv_image)
41
-
42
- # Apply Sobel edge detection
43
- sobelx = cv2.Sobel(equalized_image, cv2.CV_64F, 1, 0, ksize=5)
44
- sobely = cv2.Sobel(equalized_image, cv2.CV_64F, 0, 1, ksize=5)
45
- edges = np.hypot(sobelx, sobely) # Combine gradients
46
-
47
- # Normalize the result to fit into the 0-255 grayscale range
48
- edges = np.uint8(255 * edges / np.max(edges))
49
-
50
- # Denoise the edges using Non-Local Means Denoising
51
- edges = cv2.fastNlMeansDenoising(edges, None, h=30, templateWindowSize=7, searchWindowSize=21)
52
-
53
- # Improve contrast of edges
54
- edges_image = Image.fromarray(edges)
55
- enhancer = ImageEnhance.Contrast(edges_image)
56
- enhanced_edges = enhancer.enhance(1.5)
57
- # enhanced_edges_array = np.array(enhanced_edges)
58
-
59
- # Invert the grayscale values of the original image
60
- inverted_image = 255 - np.array(equalized_image)
61
-
62
- # Convert sharpened edges to array for blending
63
- sharpened_edges_array = np.array(enhanced_edges )
64
-
65
- # Blend the inverted image with the sharpened edges using the blend_percentage
66
- blended_image = cv2.addWeighted(sharpened_edges_array, 1 - blend_percentage,
67
- inverted_image, blend_percentage, 0)
68
-
69
- # Resize the image for ASCII aspect ratio
70
- width, height = image.size
71
- aspect_ratio = height / width * 0.55 # Adjusting for ASCII aspect ratio
72
- new_height = int(aspect_ratio * new_width)
73
- resized_blended_image = Image.fromarray(blended_image).resize((new_width, new_height))
74
-
75
- return resized_blended_image
76
-
77
-
78
- def image_to_ascii(image):
79
- """
80
- This function generates an ASCII representation of images.
81
- """
82
- pixels = np.array(image)
83
- ascii_str = ""
84
- for row in pixels:
85
- for pixel in row:
86
- # Mapping pixel intensity to ASCII
87
- ascii_str += ASCII_CHARS[pixel // 10]
88
- ascii_str += "\n"
89
- return ascii_str
90
-
91
-
92
- def ascii_to_image(ascii_art, font_size=12, output_file='ascii_art.png'):
93
- """"
94
- This function converts ASCII text art to a png image.
95
- """
96
-
97
- # Specify font
98
- font_path = "DejaVuSansMono.ttf"
99
- font = ImageFont.truetype(font_path, font_size)
100
-
101
- # Get the required image dimensions
102
- lines = ascii_art.splitlines()
103
- max_width = max(font.getbbox(line)[2] for line in lines) # Get max width
104
- img_height = len(lines) * font_size
105
-
106
- # Generate image with white background
107
- image = Image.new("RGB", (max_width, img_height), "white")
108
- draw = ImageDraw.Draw(image)
109
-
110
- # Draw each character from the ASCII text on the image
111
- for y, line in enumerate(lines):
112
- draw.text((0, y * font_size), line, fill="black", font=font)
113
-
114
- image.save(output_file)
115
- return image
116
-
117
-
118
- def compare_ssim(image1, image2):
119
- """
120
- This function generates the structural similarity index
121
- measure for the two input images.
122
- """
123
- # Convert original color image to grayscale
124
- image1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
125
-
126
- # Convert ASCII image to grayscale and cv2 format
127
- image2 = np.array(image2.convert("L"))
128
-
129
- # Resize so both images have the same dimensions.
130
- image2 = cv2.resize(image2, (image1.shape[1], image1.shape[0]))
131
-
132
- score, _ = ssim(image1, image2, full=True)
133
- return score
134
-
135
-
136
- def process_image(image):
137
- """
138
- This function takes an image and generates:
139
- -ASCII art as a string
140
- -ASCII text file
141
- -Compute SSIM for the ASCII art image and the original image
142
- """
143
- # Get image edges and convert to ASCII
144
- edges = preprocess_image(image)
145
- ascii_art = image_to_ascii(edges)
146
-
147
- # Convert ASCII art back to an image for SSIM calculation
148
- ascii_image = ascii_to_image(ascii_art)
149
-
150
- # Calculate SSIM
151
- ssim_score = compare_ssim(image, ascii_image)
152
-
153
- # Write ASCII art to txt file
154
- ascii_txt_path = "ascii_art_output.txt"
155
- with open(ascii_txt_path, "w") as f:
156
- f.write(ascii_art)
157
-
158
- return ascii_art, ssim_score, ascii_txt_path
159
-
160
-
161
- def gradio_interface(image):
162
- """
163
- This function generates the interface outputs for a given input image.
164
- The outputs include the formatted SSIM, ASCII art, and ASCII art txt file path.
165
- """
166
- ascii_art, ssim_score, ascii_txt_path = process_image(image)
167
- ssim_formatted = f'<div style="font-size: 20px;">The structural similarity index measure is {ssim_score:.3f}</div>'
168
- # Wrap the ASCII art in HTML with custom font size
169
- styled_ascii_art = f'<pre style="font-family: monospace; font-size: 7px;">{ascii_art}</pre>'
170
-
171
- return styled_ascii_art, ssim_formatted, ascii_txt_path
172
-
173
-
174
- def main():
175
- # Setup Gradio interface
176
- with gr.Blocks() as interface:
177
- # Add title and description
178
- gr.Markdown("<h1 style='font-size: 40px;'>Image to ASCII Art Converter</h1>")
179
- 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>")
180
-
181
- # Image input and ASCII art + SSIM score output
182
- image_input = gr.Image(label="Upload an image")
183
- ascii_art_output = gr.HTML()
184
- ssim_score_output = gr.HTML()
185
-
186
- # File output for downloading, initially hidden
187
- ascii_file_output = gr.File(label="Download ASCII Art")
188
-
189
- # Update interface when image is uploaded
190
- image_input.change(fn=gradio_interface,
191
- inputs=image_input,
192
- outputs=[ascii_art_output, ssim_score_output, ascii_file_output])
193
-
194
- # Launch interface
195
- interface.launch()
196
-
197
-
198
- if __name__ == "__main__":
199
  main()
 
1
+ """
2
+ CS5330 Fall 2024
3
+ Author: Calvin Lo
4
+ Lab 1: ASCII Art
5
+
6
+ This program generates the ASCII art representation of
7
+ an input image and compares the result to the original image
8
+ using SSIM.
9
+ """
10
+
11
+ import gradio as gr
12
+ import cv2
13
+ import numpy as np
14
+ from PIL import Image, ImageDraw, ImageFont, ImageFilter, ImageEnhance
15
+ from skimage.metrics import structural_similarity as ssim
16
+
17
+ # ASCII characters to map pixel intensity
18
+ ASCII_CHARS = [
19
+ ' ', '.', ',', ':', ';', 'i', 'l', '!', 'I', '1', 't', 'o', 'r', 'x', 'z', 'v',
20
+ 'u', 'n', 'm', 'w', 'Q', 'B', 'N', 'M', '@'
21
+ ]
22
+
23
+
24
+ def preprocess_image(image, new_width=150):
25
+ """
26
+ This function performs the following operations on the input image:
27
+ -enhance contrast
28
+ -perform edge detection (Sobel)
29
+ -denoise edges
30
+ -enhance contrast
31
+ -resize image
32
+ """
33
+ # Open the image and convert to grayscale
34
+ image = Image.fromarray(image)
35
+ image = image.convert("L")
36
+
37
+ # Contrast limited adaptive histogram equalization
38
+ opencv_image = np.array(image)
39
+ clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
40
+ equalized_image = clahe.apply(opencv_image)
41
+
42
+ # Apply Sobel edge detection
43
+ sobelx = cv2.Sobel(equalized_image, cv2.CV_64F, 1, 0, ksize=5)
44
+ sobely = cv2.Sobel(equalized_image, cv2.CV_64F, 0, 1, ksize=5)
45
+ edges = np.hypot(sobelx, sobely) # Combine gradients
46
+
47
+ # Normalize the result to fit into the 0-255 grayscale range
48
+ edges = np.uint8(255 * edges / np.max(edges))
49
+
50
+ # Denoise the edges using Non-Local Means Denoising
51
+ edges = cv2.fastNlMeansDenoising(edges, None, h=30, templateWindowSize=7, searchWindowSize=21)
52
+
53
+ # Improve contrast of edges
54
+ edges_image = Image.fromarray(edges)
55
+ enhancer = ImageEnhance.Contrast(edges_image)
56
+ enhanced_edges = enhancer.enhance(1.5)
57
+ enhanced_edges = np.array(enhanced_edges)
58
+
59
+ # Resize the image for ASCII aspect ratio
60
+ width, height = image.size
61
+ aspect_ratio = height / width * 0.45 # Adjusting for ASCII aspect ratio
62
+ new_height = int(aspect_ratio * new_width)
63
+ resized_image = Image.fromarray(enhanced_edges).resize((new_width, new_height))
64
+
65
+ return resized_image
66
+
67
+
68
+ def image_to_ascii(image):
69
+ """
70
+ This function generates an ASCII representation of images.
71
+ """
72
+ pixels = np.array(image)
73
+ ascii_str = ""
74
+ for row in pixels:
75
+ for pixel in row:
76
+ # Mapping pixel intensity to ASCII
77
+ ascii_str += ASCII_CHARS[pixel // 10]
78
+ ascii_str += "\n"
79
+ return ascii_str
80
+
81
+
82
+ def ascii_to_image(ascii_art, font_size=12, output_file='ascii_art.png'):
83
+ """"
84
+ This function converts ASCII text art to a png image.
85
+ """
86
+
87
+ # Specify font
88
+ font_path = "DejaVuSansMono.ttf"
89
+ font = ImageFont.truetype(font_path, font_size)
90
+
91
+ # Get the required image dimensions
92
+ lines = ascii_art.splitlines()
93
+ max_width = max(font.getbbox(line)[2] for line in lines) # Get max width
94
+ img_height = len(lines) * font_size
95
+
96
+ # Generate image with white background
97
+ image = Image.new("RGB", (max_width, img_height), "white")
98
+ draw = ImageDraw.Draw(image)
99
+
100
+ # Draw each character from the ASCII text on the image
101
+ for y, line in enumerate(lines):
102
+ draw.text((0, y * font_size), line, fill="black", font=font)
103
+
104
+ image.save(output_file)
105
+ return image
106
+
107
+
108
+ def compare_ssim(image1, image2):
109
+ """
110
+ This function generates the structural similarity index
111
+ measure for the two input images.
112
+ """
113
+ # Convert original color image to grayscale
114
+ image1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
115
+
116
+ # Convert ASCII image to grayscale and cv2 format
117
+ image2 = np.array(image2.convert("L"))
118
+
119
+ # Resize so both images have the same dimensions.
120
+ image2 = cv2.resize(image2, (image1.shape[1], image1.shape[0]))
121
+
122
+ score, _ = ssim(image1, image2, full=True)
123
+ return score
124
+
125
+
126
+ def process_image(image):
127
+ """
128
+ This function takes an image and generates:
129
+ -ASCII art as a string
130
+ -ASCII text file
131
+ -Compute SSIM for the ASCII art image and the original image
132
+ """
133
+ # Get image edges and convert to ASCII
134
+ edges = preprocess_image(image)
135
+ ascii_art = image_to_ascii(edges)
136
+
137
+ # Convert ASCII art back to an image for SSIM calculation
138
+ ascii_image = ascii_to_image(ascii_art)
139
+
140
+ # Calculate SSIM
141
+ ssim_score = compare_ssim(image, ascii_image)
142
+
143
+ # Write ASCII art to txt file
144
+ ascii_txt_path = "ascii_art_output.txt"
145
+ with open(ascii_txt_path, "w") as f:
146
+ f.write(ascii_art)
147
+
148
+ return ascii_art, ssim_score, ascii_txt_path
149
+
150
+
151
+ def gradio_interface(image):
152
+ """
153
+ This function generates the interface outputs for a given input image.
154
+ The outputs include the formatted SSIM, ASCII art, and ASCII art txt file path.
155
+ """
156
+ ascii_art, ssim_score, ascii_txt_path = process_image(image)
157
+ ssim_formatted = f'<div style="font-size: 20px;">The structural similarity index measure is {ssim_score:.3f}</div>'
158
+ # Wrap the ASCII art in HTML with custom font size
159
+ styled_ascii_art = f'<pre style="font-family: monospace; font-size: 7px;">{ascii_art}</pre>'
160
+
161
+ return styled_ascii_art, ssim_formatted, ascii_txt_path
162
+
163
+
164
+ def main():
165
+ # Setup Gradio interface
166
+ with gr.Blocks() as interface:
167
+ # Add title and description
168
+ gr.Markdown("<h1 style='font-size: 40px;'>Image to ASCII Art Converter</h1>")
169
+ 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>")
170
+
171
+ # Image input and ASCII art + SSIM score output
172
+ image_input = gr.Image(label="Upload an image")
173
+ ascii_art_output = gr.HTML()
174
+ ssim_score_output = gr.HTML()
175
+
176
+ # File output for downloading, initially hidden
177
+ ascii_file_output = gr.File(label="Download ASCII Art")
178
+
179
+ # Update interface when image is uploaded
180
+ image_input.change(fn=gradio_interface,
181
+ inputs=image_input,
182
+ outputs=[ascii_art_output, ssim_score_output, ascii_file_output])
183
+
184
+ # Launch interface
185
+ interface.launch()
186
+
187
+
188
+ if __name__ == "__main__":
 
 
 
 
 
 
 
 
 
 
189
  main()