[Python入门项目]字符画(PIL)

发布于 2022-06-13  729 次阅读


字符画就是将一幅图片用字符来表示,保存为一个txt文本文档,主要的效果如图。

效果图

安装PIL / install Pillow

我们需要用到Pthon的PIL第三方库,PIL是一个python的强大的图形库。

PIL在Python2.7的时候就停止维护了,在Python3中维护使用的PIL叫作Pillow,可以将这俩看作同一个库。

我这里使用的Pycharm平台,在左上角设置 - 文件 - Python解释器 - 加号 - 搜索pillow - 选择第一个并点击左下角安装。

Python解释器界面
安装Pillow第三方库

在安装完成后引入PIL库并命名为Image(这只是一个编程习惯,并不一定要重新命名)。

#引入第三方库
from PIL import Image

读取图片并转换成灰度图片 / open & convert

准备一张图片(这张图片用了超分辨率算法进行增强)。

首先用一个变量来存下原图片,这里用im来表示,将其按照一定比例缩小,因为字符普遍高度比宽度更大,所以注意缩小比例时高度应该缩小更多以保证字符画的比例正常,这个需要根据图片原本比例和字符宽高比例调整,这里就不那么严谨了,写一个差不多的比例就好了。

im = Image.open('src.png')
k = 3 # 缩小倍数,需要保证 k >= 1
im = im.resize((im.width // k,im.height // (k + 2)), Image.Resampling.LANCZOS)# 最高精度转换
im = im.convert('L')    # 转为黑白图, 每个像素都一个灰度值,从0到255, 0是黑色, 255是白色

open参数为一个字符串表示图片地址。这里需要提前将图片放到和py文件同一个目录下保证相对路径正确。

resize参数第一个是一个二元组(宽度,高度),第二参数为转换模式,Image.Resampling.LANCZOS表示最高精度转换。

convert参数一般有'1'(二值图,非黑即白), 'L'(灰度图片,每一个像素的灰度0 ~ 255), 'P'(8位彩色), 'RGBA'(32位彩色), 'CMYK'等。

设定字符集 / char_set

字符集的作用是,将不同灰度的像素点用不同的字符去代替。比如灰度越高(越黑)的像素点就用@$等单位覆盖率高的字符,而越白的像素点就用. / ( ) 空格等单位覆盖率低的字符去表示。

这里这个字符集出处请见文末参考文档。

# 创建字符集
char_set = '''$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. '''

遍历图片并构造出字符文本 / construct the text

通过双重循环来遍历图片的每一个像素点,再根据像素点的灰度值来选择一个字符加入到字符串后面即可。

for i in range(im.height):
    for j in range(im.width):
        gray = im.getpixel((j, i)) # 一个二元组,这里的坐标(x, y)和遍历的顺序相反,所以要用 (j ,i)
        if isinstance(gray, tuple): # 如果这个灰度值不是一个整数而是一个三元组,就算出一个整型来表示灰度值
            gray = int(0.2 * gray[0] + 0.6 * gray[1] * 0.2 * gray[2])
        buf += get_char(gray)
    buf += '\n'

写入文件 / wirte to file

# 将buf内容写入pic.txt
open('pic.txt', 'w').write(buf)

完整代码 / complete code

from PIL import Image

#设定字符集
char_set = '''$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. '''

#打开图片
im = Image.open('src.png')
k = 2 # 缩小倍数,需要保证 k >= 1,如果倍数为1,需要调整下一行的 k + 2 改成 k 即可
im = im.resize((im.width // k,im.height // (k + 2)), Image.Resampling.LANCZOS)# 最高精度转换
im = im.convert('L')    # 转为黑白图, 每个像素都一个灰度值,从0到255, 0是黑色, 255是白色


def get_char(gray: int)->str: # 将灰度值对应到字符集中
    return ' ' if gray > 240 else char_set[int(gray / 257.0 * len(char_set))]


buf = '' # 最终结果将存入 buf 再保存到 pic.txt 中

#遍历图片
for i in range(im.height):
    for j in range(im.width):
        gray = im.getpixel((j, i)) # 一个二元组,这里的坐标(x, y)和遍历的顺序相反,所以要用 (j ,i)
        if isinstance(gray, tuple): # 如果这个灰度值不是一个整数而是一个三元组,就算出一个整型来表示灰度值
            gray = int(0.2 * gray[0] + 0.6 * gray[1] * 0.2 * gray[2])
        buf += get_char(gray)
    buf += '\n'

# 将buf内容写入pic.txt
open('pic.txt', 'w').write(buf)

注意最后要用浏览器打开txt文件,否则显示比例会不协调。

参考文档:python实战练手项目---字符画及其原理 | 酷python (coolpython.net)