[Python] I wrote a simple code that automatically generates AA (ASCII art)

Introduction

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

Method

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.

Implementation

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.

Finally

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 thanfile_path` (in that case, the standard output is 150 characters wide).

Summary

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

[Python] I wrote a simple code that automatically generates AA (ASCII art)
A memo that I wrote a quicksort in Python
I made a simple blackjack with Python
I wrote a code to convert quaternions to z-y-x Euler angles in Python
Let's create a customer database that automatically issues a QR code in Python
Code reading of faker, a library that generates test data in Python
[Python] I made a web scraping code that automatically acquires the news title and URL of Nikkei Inc.
I wrote a class in Python3 and Java
I wrote an animation that degenerates a linear system with deadly dirty code
A Vim plugin that automatically formats Python styles
I made a program to convert images into ASCII art with Python and OpenCV
[Mac] I want to make a simple HTTP server that runs CGI with Python
mong --I tried porting the code that randomly generates Docker container names to Python -
I wrote Python code to create a table (view) dependency diagram (PlantUML) from SQL
Let's write python code that parses go code and generates go code
A simple Python HTTP server that supports Range Requests
I made a VM that runs OpenCV for Python
I tried to automatically generate a password with Python3
A memo that I touched the Datastore with python
I felt that I ported the Python code to C ++ 98.
[Python, ObsPy] I wrote a beach ball with Matplotlib + ObsPy
I made an action to automatically format python code
I wrote a Python script that exports all my posts using the Qiita API v2
I wrote a code that exceeds 100% recovery rate in horse racing prediction using LightGBM (Part 2)
I tried to make a generator that generates a C # container class from CSV with Python
[Python] I wrote a test of "Streamlit" that makes it easy to create visualization applications.
I made a simple typing game with tkinter in Python
A string permutation code that uses an algorithm that generates permutations.
I wrote a program quickly to study DI with Python ①
python Condition extraction from a list that I often forget
I made a simple book application with python + Flask ~ Introduction ~
I tried "a program that removes duplicate statements in Python"
Create code that outputs "A and pretending B" in python
I just wrote the original material for the python sample code
I wrote a script that splits the image in two
[Python] I tried to make a simple program that works on the command line using argparse.
I wrote python in Japanese
I made a python text
[Python] I made a Line bot that randomly asks English words.
I made a simple circuit with Python (AND, OR, NOR, etc.)
[Python3] I made a decorator that declares undefined functions and methods.
I made a package that can compare morphological analyzers with Python
I want to use a wildcard that I want to shell with Python remove
[Python] A memo that I tried to get started with asyncio
I wrote the code to write the code of Brainf * ck in python
Simple code to call a python program from Javascript on EC2
I wrote a function to load a Git extension script in Python
[Python] I made an image viewer with a simple sorting function.
I made a shuffle that can be reset (reverted) with Python
I wrote a script to extract a web page link in Python
I made a library that adds docstring to a Python stub file.
Create a simple Python development environment with VS Code and Docker
[Python] I wrote a REST API using AWS API Gateway and Lambda.
I made a program that automatically calculates the zodiac with tkinter
[python] I made a class that can write a file tree quickly
I wrote a tri-tree that can be used for high-speed dictionary implementation in D language and Python.