Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit b22e11f

Browse files
Merge pull request avinashkranjan#1916 from aryanrai2001/Dynamic-Resolution-In-Photo-To-ASCII
Added dynamic resolution to Photo-to-ASCII
2 parents 1c1dbaa + b584547 commit b22e11f

File tree

1 file changed

+175
-78
lines changed

1 file changed

+175
-78
lines changed

‎Photo To Ascii/photo_to_ascii.py‎

Lines changed: 175 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,58 @@
1+
"""
2+
Script that converts any Image into its ASCII representation.
3+
"""
4+
5+
from sys import exit as sysexit
16
from PIL import Image, ImageEnhance
27

3-
# ASCII characters used to build the output text
4-
char_ramp = []
58

6-
# Choose character sequence for mapping
7-
def set_char_ramp():
8-
global char_ramp
9+
def pixels_to_ascii(image, char_ramp):
10+
"""
11+
Function that takes in an image and a character sequence.
12+
And returns a string containing the ASCII representation of
13+
the image based on the character sequence provided.
14+
"""
15+
16+
pixels = image.convert("L").getdata()
17+
characters = " ".join(
18+
[char_ramp[int((pixel / 256) * len(char_ramp))] for pixel in pixels]
19+
)
20+
pixel_count = len(characters)
21+
scanline_width = image.width * 2
22+
23+
return "\n".join(
24+
[
25+
characters[index: (index + scanline_width)]
26+
for index in range(0, pixel_count, scanline_width)
27+
]
28+
)
29+
30+
31+
def input_image():
32+
"""
33+
Function that asks user for a path to an image file
34+
and checks for validity. Then it loads and returns the
35+
image and the path as a tuple(image, path).
36+
"""
37+
38+
path = input("Enter a valid pathname to an image:\n")
39+
40+
try:
41+
image = Image.open(path)
42+
except IOError:
43+
print("Invalid Path!")
44+
sysexit()
45+
46+
return image, path
47+
48+
49+
def input_ramp():
50+
"""
51+
Function that asks the user to choose from a set
52+
of character sequences or to specify their own
53+
custom sequence and then returns that as a string.
54+
"""
55+
956
print("Choose a Character Sequence!")
1057
print("1 - Basic")
1158
print("2 - Standard (Short)")
@@ -17,90 +64,140 @@ def set_char_ramp():
1764

1865
try:
1966
choice = int(choice)
20-
except Exception:
67+
except ValueError:
2168
print("Invalid Input!")
22-
exit()
69+
sysexit()
2370

2471
if choice == 1:
25-
char_ramp = list("00011111...")
26-
elif choice == 2:
27-
char_ramp = list("@%#*+=-:. ")
28-
elif choice == 3:
29-
char_ramp = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1}{[]?-_+~<>i!lI;:,\"^`'. ")
30-
elif choice == 4:
31-
char_ramp = ["█", "▉", "▊", "▋", "▌", "▍", "▎", "▏"]
32-
elif choice == 5:
33-
char_ramp = ["█", "▓", "▒", "░", " "]
34-
elif choice == 6:
35-
custom_ramp = input("Enter your Character Sequence from High density to Low: ")
72+
return list("00011111...")
73+
if choice == 2:
74+
return list("@%#*+=-:. ")
75+
if choice == 3:
76+
return list(
77+
"$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft"
78+
+ "/\\|()1}{[]?-_+~<>i!lI;:,\"^`'. "
79+
)
80+
if choice == 4:
81+
return ["█", "▉", "▊", "▋", "▌", "▍", "▎", "▏"]
82+
if choice == 5:
83+
return ["█", "▓", "▒", "░", " "]
84+
if choice == 6:
85+
while True:
86+
custom_ramp = input("Enter character sequence ['?' for info]: ")
87+
if custom_ramp == "?":
88+
print(
89+
"The character sequence must start with characters",
90+
"that represent high pixel density and end with",
91+
"characters that represent low pixel density.",
92+
)
93+
else:
94+
break
3695
if len(custom_ramp) == 0:
3796
print("Invalid Input!")
38-
exit()
39-
char_ramp = list(custom_ramp)
40-
else:
41-
print("Invalid Input!")
42-
exit()
97+
sysexit()
98+
return list(custom_ramp)
4399

100+
print("Invalid Input!")
101+
sysexit()
44102

45-
# Convert pixels to a string of ASCII characters
46-
def pixels_to_ascii(image, contrast_factor):
47-
image = image.convert("L")
48-
enhancer = ImageEnhance.Contrast(image)
49-
image = enhancer.enhance(contrast_factor)
50-
pixels = image.getdata()
51-
characters = " ".join([char_ramp[int((pixel/256)*len(char_ramp))] for pixel in pixels])
52-
return(characters)
53103

54-
# Driver function
55-
def photoascii():
56-
# Attempt to open image file from user-input
57-
path = input("Enter a valid pathname to an image:\n")
58-
try:
59-
image = Image.open(path)
60-
except Exception:
61-
print("Invalid Path!")
62-
exit()
63-
64-
contrast_factor = input("Enter contrast factor (1 = Original Contrast) [Note: Enter negative value to invert output] : ")
104+
def input_contrast():
105+
"""
106+
Function that asks user for the contrast factor that is to be applied
107+
on the image before conversion. And returns it.
108+
"""
109+
110+
while True:
111+
contrast_factor = input("Enter contrast factor ['?' for info] : ")
112+
if contrast_factor == "?":
113+
print(
114+
"Contrast factor is a value that is used to controle",
115+
"the contrast of the output. Default value of the contrast",
116+
"factor is 1. Negative value will invert the output.",
117+
)
118+
else:
119+
break
120+
65121
try:
66122
contrast_factor = float(contrast_factor)
67-
except Exception:
123+
except ValueError:
68124
print("Invalid Input!")
69-
exit()
70-
71-
set_char_ramp()
72-
125+
sysexit()
126+
127+
return contrast_factor
128+
129+
130+
def resize_image(image):
131+
"""
132+
Function that takes in an image and asks the user for a sample size.
133+
Then returns a resized image with each pixel representing the sample grid.
134+
"""
135+
136+
while True:
137+
sample_size = input("Enter the sample size ['?' for info] : ")
138+
if sample_size == "?":
139+
print(
140+
"Sample size refers to the number of pixels",
141+
"that will be sampled for one character.",
142+
"Default value of sample size is 4.",
143+
"Its value must be greater than or equal to 1.",
144+
)
145+
else:
146+
break
147+
148+
try:
149+
sample_size = int(sample_size)
150+
except ValueError:
151+
print("Invalid Input!")
152+
sysexit()
153+
154+
if sample_size <= 0:
155+
print("Invalid Input!")
156+
sysexit()
157+
158+
width, height = image.size
159+
new_width = width // sample_size
160+
new_height = (new_width * height) // width
161+
162+
return image.resize((new_width, new_height))
163+
164+
165+
def get_output_path(path):
166+
"""
167+
Function that takes in the path of the input image file and returns the
168+
path of a text file that the ouput will be saved to.
169+
"""
170+
171+
dot_index = path.rfind(".")
172+
slash_index = path.rfind("\\")
173+
174+
if slash_index == -1:
175+
slash_index = path.rfind("/")
176+
177+
image_name = path[slash_index + 1: dot_index] + "_" + path[dot_index + 1:]
178+
179+
return path[:slash_index] + f"/{image_name}_ASCII.txt"
180+
181+
182+
def main():
183+
"""
184+
The main function.
185+
"""
186+
187+
image, path = input_image()
188+
char_ramp = input_ramp()
189+
190+
contrast_factor = input_contrast()
73191
if contrast_factor < 0:
74-
global char_ramp
75192
char_ramp.reverse()
76193
contrast_factor = -contrast_factor
77-
78-
# Fetch the name of the image file
79-
dot_index = path.rfind('.')
80-
slash_index = path.rfind('\\')
81-
if slash_index == -1:
82-
slash_index = path.rfind('/')
83-
image_name = path[slash_index+1:dot_index] + "_" + path[dot_index+1:]
84-
85-
# Resize image
86-
new_width = 100
87-
width, height = image.size
88-
ratio = height/width
89-
new_height = int(new_width * ratio)
90-
resized_image = image.resize((new_width, new_height))
91-
92-
# Convert image to ASCII
93-
new_image_data = pixels_to_ascii(resized_image, contrast_factor)
94-
pixel_count = len(new_image_data)
95-
scanline_width = new_width * 2;
96-
ascii_image = "\n".join([new_image_data[index:(index+scanline_width)]
97-
for index in range(0, pixel_count, scanline_width)])
98-
99-
# Save result to text file
100-
with open(path[:slash_index] + "/{}_ASCII.txt".format(image_name), "w", encoding='utf8') as f:
101-
f.write(ascii_image)
102-
103-
104-
# Run Program
105-
if __name__ == '__main__':
106-
photoascii()
194+
195+
image = resize_image(ImageEnhance.Contrast(image).enhance(contrast_factor))
196+
ascii_image = pixels_to_ascii(image, char_ramp)
197+
198+
with open(get_output_path(path), "w", encoding="utf8") as file:
199+
file.write(ascii_image)
200+
201+
202+
if __name__ == "__main__":
203+
main()

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /