Everyone, you know ** AA (ASCII art) **. It's a craftsmanship, isn't it? Once your favorite image, your favorite character, or yourself? Have you ever wanted to make AA? This time I will not go as far as AA craftsman, but I wrote a code that ** easily generates AA ** from my favorite image using my favorite characters, so I would like to introduce it. I will.
As an example, the code introduced in this article can be used for the following conversions. Image publisher: Irasutoya
AA made by AA craftsmen often expresses contours effectively with /
etc. and expresses illustrations with a small number of characters, but this time I want to do it with brute force without doing such complicated things. think.
The method is simple, ** Apply letters to the grayscale 256 levels of brightness in order of density **. Let's go.
First, read the conversion source image in grayscale and make it a Numpy array
.
AA.py
from PIL import Image
import numpy as np
file_path = "./hogehoge.png "
imag = Image.open(file_path).convert('L')
imarray = np.asarray(imag)
At this time, the value stored in ʻimarray` is an integer from 0 to 255 (2 ^ 8 steps). This is the only preparation on the image side.
Next, sort the characters to be used in order of density. I'm going to do it, but I don't know how to handle font files, and even if I do, I can't think of a way to measure the density of vector images. The method I came up with was to paste it on an image (raster) once and then calculate the sum or average of the images and compare them.
AA.py
from PIL import ImageDraw,ImageFont
def make_map(str_list):
l = []
font = ImageFont.truetype('msgothic.ttc', 20) #"C:\Windows\Fonts"In your favorite font (or write from the path)#If there is no error, it is OK as it is
for i in str_list:
im = Image.new("L",(20,20),"white") #Creating a white background image of the sky
draw = ImageDraw.Draw(im)
draw.text((0,0),i,font=font) #Character insertion
l.append(np.asarray(im).mean()) #Numpy array to calculate and store the average value
l_as = np.argsort(l) #Index of ascending sort of density index
lenl = len(l)
l2256 = np.r_[np.repeat(l_as[:-(256%lenl)],256//lenl),np.repeat(l_as[-(256%lenl):],256//lenl+1)] #Adjust the number of elements to 256
chr_map = np.array(str_list)[l2256] #Character list sorted by density
return chr_map
This function will sort by density and return it as a Nampy array
if you pass a list of characters.
By the way, there is a function that I personally like the Nampy array
that, if you pass an array that stores the index as an index, the specified element will be retrieved (fancy index
). To give an example
python
import numpy as np
arr=np.array(["Ah","I","U","e","O"])
print(arr[[1,1,4,3,2,0]])
output
['I' 'I' 'O' 'e' 'U' 'Ah']
** Hmm, I love Numpy. ** ** --Quiet talk-
This time, by using this function, you can ** fold it at once **.
AA.py
str_list = list("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +-*/%'"+'"!?#&()~^|@;:.,[]{}<>_0123456789') #Prepare the characters you want to use
chr_map = make_map(str_list) #Sort by density,
aa = chr_map[imarray] #Fancy sort! !! !!
Yes, this alone completes the data converted to AA in ** ʻaa` **.
After that, you can write it to a TXT file or output it as standard output so that it is easy to see.
AA.py
aa = aa.tolist() #Convert to list
out_path="./aa.txt"
with open(out_path,"w") as f:
for i in range(len(imarray)):f.write(''.join(aa[i])+"\n")
Or
AA.py
aa = aa.tolist()
for i in range(len(imarray)):print(''.join(aa[i]))
that's all. It was easy.
Change the output size and select full-width / half-width (Reference: [Python] Mutual conversion between full-width and half-width in one line (alphabet + number + symbol)) I will list the ones that are summarized in the function by adding detailed functions such as
AA.py
from PIL import Image, ImageDraw,ImageFont
import numpy as np
def make_map(str_list):
l=[]
font = ImageFont.truetype('msgothic.ttc', 20)
for i in str_list:
im = Image.new("L",(20,20),"white")
draw = ImageDraw.Draw(im)
draw.text((0,0),i,font=font)
l.append(np.asarray(im).mean())
l_as=np.argsort(l)
lenl=len(l)
l2256=np.r_[np.repeat(l_as[:-(256%lenl)],256//lenl),np.repeat(l_as[-(256%lenl):],256//lenl+1)]
chr_map=np.array(str_list)[l2256]
return chr_map
def output(chr_map,imarray,isOutText,out_path):
aa=chr_map[imarray].tolist()
if isOutText:
with open(out_path,"w") as f:
for i in range(len(imarray)):f.write(''.join(aa[i])+"\n")
else:
for i in range(len(imarray)):print(''.join(aa[i]))
def make_AA(file_path,str_list="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +-*/%'"+'"!?#&()~^|@;:.,[]{}<>_0123456789',width=150,isOutText=False,out_path="aa.txt",isFW=False):
imag=Image.open(file_path).convert('L')
if isFW:str_list=list(str_list.translate(str.maketrans({chr(0x0021 + i): chr(0xFF01 + i) for i in range(94)})))
else:str_list=list(str_list)
imarray=np.asarray(imag.resize((width,width*imag.height//imag.width//(2-int(isFW)))))
output(make_map(str_list),imarray,isOutText,out_path)
This is the function that make_AA ()
executes from here when used.
The arguments are file_path
: the path of the original image, str_list
: the character string to be used (character string instead of the list), width
: the number of horizontal characters (vertical is automatically adjusted), ʻisOutText : If True, it will be output to the TXT file. (False by default), ʻout_path
: Output destination and file name of TXT file when isOutText is True
, ʻisFW: Set to
Truewhen full-width characters are included or when you want to output in full-width Please (short for Full Width) It can be used without specifying anything other than
file_path` (in that case, the standard output is 150 characters wide).
Although it is easy, I introduced a program to make AA. By the way, if you raise the contrast of the original image as a pre-processing, the finish will be refreshing. I hope it will be helpful for you. I would be grateful if you could give us your impressions and advice.
Recommended Posts