Ascii art is a graphic style composed of alphabets and symboles.
You use Python Image Library. If you installed Python with Anaconda, it alredy installed the library, otherwise execute pip install Pillow
.
We use the sample input image Lenna.png
. Locate the image in the same folder as .py file.
First, You write very simple code. you load an image, print its size and show the image.
from PIL import Image
img = Image.open('Lenna.png')
print(img.size)
img.show()
(512, 512)
Each pixel is defined by the value of red, green and blue, (r, g, b), 0 <= r <= 255, 0<= g <= 255, 0 <= b <= 255. (r, g, b) is converted into the intensity (or Brightness) by
gray = red * 0.2126 + green * 0.7152 + b * 0.0722
You can see pixel data by load(). Also, convert image type by .convert('RGB')
for when an input image's mode is not RGB.
from PIL import Image
img = Image.open('Lenna.png').convert('RGB')
pixels = img.load()
print(pixels)
Bunch of (r, g, b) is shown now. Each pixel is accessed by pixels[x,y], 0 <= x < width, 0 <= y < height
. xy Coordination in a image is below.
O ---------> x
|
|
| Image
|
|/
y
convert them into grayscale because we are making grayscale ascii art.
from PIL import Image
img = Image.open('Lenna.png').convert('RGB')
w, h = img.size
pixels = img.load()
for y in range(h):
row = []
for x in range(w):
r, g, b = pixels[x, y]
gray = r * 0.2326 + g * 0.7152 + b * 0.0722
print(gray)
You create a white canvas to output. import other libraries.
from PIL import Image, ImageFont, ImageDraw
img = Image.open('Lenna.png').convert('RGB')
w, h = img.size
pixels = img.load()
fontsize = 24
font = ImageFont.truetype("path/to/font.ttc", fontsize, encoding='utf-8')
# On windows, fontpath = 'C://Windows/Fonts/msgothic.ttc'
# On Mac, fontpath = '/System/Library/Fonts/Menlo.ttc'
# On Linux, fontpath = '/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf'
output_img = Image.new(mode='RGBA', size=(w,h), color=(255,255,255)) # white canvas. Its size is the same as the input image's size.
draw = ImageDraw.Draw(output_img)
for y in range(h):
for x in range(w):
r, g, b = pixels[x, y]
gray = r * 0.2326 + g * 0.7152 + b * 0.0722
output_img.show()
Put a character corresponding to the grayscale in the same pixel of the input image. x and y should step by fontsize. Monospaced font is recommended. We divide 0-255 grayscale into ten levels. Edit for-loops.
from PIL import Image, ImageFont, ImageDraw
img = Image.open('Lenna.png').convert('RGB')
w, h = img.size
pixels = img.load()
fontsize = 24
fontpath = 'path/to/your/font'
font = ImageFont.truetype(fontpath, fontsize, encoding='utf-8')
output_img = Image.new(mode='RGBA', size=(w,h), color=(255,255,255))
draw = ImageDraw.Draw(output_img)
for y in range(0, h, fontsize): # The third parameter in range is a step
for x in range(0, w, fontsize):
r, g, b = pixels[x, y]
gray = r * 0.2326 + g * 0.7152 + b * 0.0722
r, g, b = pixels[x, y]
gray = r * 0.2326 + g * 0.7152 + b * 0.0722
if gray > 225:
character = ' '
elif gray > 200:
character = '.'
elif gray > 175:
character = ','
elif gray > 150:
character = ':'
elif gray > 125:
character = ';'
elif gray > 100:
character = '+'
elif gray > 75:
character = '*'
elif gray > 50:
character = '%'
elif gray > 25:
character = '#'
else:
character = 'W'
draw.text((x, y), character, font=font, fill = '#000000') # #000000 corresponds black
output_img.show()
It is too low image quality. Change fontsize to 12 by fontsize = 12
and re-run.
from PIL import Image, ImageFont, ImageDraw
img = Image.open('Lenna.png').convert('RGB')
w, h = img.size
pixels = img.load()
fontsize = 12
fontpath = 'path/to/your/font'
# On windows, fontpath = 'C://Windows/Fonts/msgothic.ttc'
# On Mac, fontpath ='/System/Library/Fonts/Menlo.ttc'
font = ImageFont.truetype(fontpath, fontsize, encoding='utf-8')
output_img = Image.new(mode='RGBA', size=(w,h), color=(255,255,255))
draw = ImageDraw.Draw(output_img)
for y in range(0, h, fontsize): # The third parameter in range is a step
for x in range(0, w, fontsize):
r, g, b = pixels[x, y]
gray = r * 0.2326 + g * 0.7152 + b * 0.0722
if gray > 225:
character = ' '
elif gray > 200:
character = '.'
elif gray > 175:
character = ','
elif gray > 150:
character = ':'
elif gray > 125:
character = ';'
elif gray > 100:
character = '+'
elif gray > 75:
character = '*'
elif gray > 50:
character = '%'
elif gray > 25:
character = '#'
else:
character = 'W'
draw.text((x, y), character, font=font, fill = '#000000') # #000000 corresponds black
output_img.show()
It lookes better. The input image size is 512 x 512. When fontsize = 24, as 512 / 24 = 21.3... so 22 characters are written in each row, and when fontsize = 12, as 512 / 12 = 42.6..., so 43 characters are done likewise.
Add the last line to save an image.
from PIL import Image, ImageFont, ImageDraw
img = Image.open('Lenna.png').convert('RGB')
w, h = img.size
pixels = img.load()
fontsize = 12
fontpath = 'path/to/your/font'
font = ImageFont.truetype(fontpath, fontsize, encoding='utf-8')
output_img = Image.new(mode='RGBA', size=(w,h), color=(255,255,255))
draw = ImageDraw.Draw(output_img)
for y in range(0, h, fontsize):
for x in range(0, w, fontsize):
r, g, b = pixels[x, y]
gray = r * 0.2326 + g * 0.7152 + b * 0.0722
if gray > 225:
character = ' '
elif gray > 200:
character = '.'
elif gray > 175:
character = ','
elif gray > 150:
character = ':'
elif gray > 125:
character = ';'
elif gray > 100:
character = '+'
elif gray > 75:
character = '*'
elif gray > 50:
character = '%'
elif gray > 25:
character = '#'
else:
character = 'W'
draw.text((x, y), character, font=font, fill = '#000000') # #000000 means black
output_img.save('Lenna_AA.png')