如何使用 Python 将图片变为字符的模样

我们先来看一下,小帅b原本的头像是这样的:

用 Python 写的脚本转一下,就变成这样了:

也就是说,将图片中的轮廓,用你喜欢的字符代替,是不是有点装逼了:

这是如何实现呢?接下来就是:

学习 Python 的正确姿势

我们知道,每张图片都是由一个个像素组成的,而你在手机上之所以可以看到五颜六色的图片,是因为有红绿蓝三原色,它们可以组合成无数种颜色,而我们常见的显示器用的就是 RGB 的颜色标准,不同的 RGB 值代表不同的颜色强度,这个值在 0 到 255 之间。

其实我们想要让图片使用字符替换,并不需要那么多颜色,所以我们可以把图片转化为灰度图像,这样它就只剩下强度不同的黑与白了,这时候,我们只需要通过颜色的灰度值去区分就可以得到需要替换字符的地方。

也就是说我们可以获取一张图片的所有像素,而每个像素都有它特有的灰度值,从 0—–>255 表示从最黑到最白,那么我们只要准备 256 个字符,把字符替换成灰度值,接着再输出就行了。

因为灰度图像只是黑白强度不同,所以我们并不需要细致到每一个具体的数值,可以把 0 —-> 255 拆分一下,比如每 25 个灰度值代表一种强度:

0—>25        白      对应字符a
25—>50      浅灰    对应字符b
50—>75      灰       对应字符c
…           越来越灰   对应字符d
225—>250   黑       对应字符e
我们可以在这每个区间里定义一个字符,这时候只要判断像素的灰度值,看看这个值在什么区间,对应替换相应的字符就可以了。
比如 a 这个字符对应的是 0—>25 的灰度值区间,如果我们获取到图片的某一个像素的灰度值为 24 ,那么就将这个像素替换为a。

事就是这么个事,我们还是来用代码体现一下会比较清晰。

0x00 | 定义字符串列表


定义一个字符串列表,我们将灰度值以 25 为单位进行拆分,那么这个列表就需要有 11 个字符:

0x01 | 获取图片
想要通过命令直接获取图片的路径,可以用 sys 模块中的 argv 属性,从 argv[1] 中可以获取到使用 Python 命令携带的参数:

0x02 | 图片等比缩放


我们可以自己定义一下要输出的字符图片尺寸。通过 Pillow 库打开图片,等比例缩放:

比如我们希望输出的字符图片宽度是 100 ,那么就先获取下原图的宽高,计算下宽高比,然后得出新图的宽高,生成新的图片:

0x03 | 图片转成灰度


将图片转化为灰度很简单,调用 convert 方法就可以了:

0x04 | 获取图片的像素

可以通过 image.getdata 来获取图片的所有像素灰度值:

可以打印看看这些像素值:

运行一下是这样子的:

(密集恐惧的不要点看)

是的,接下来我们就根据这些值替换字符。

0x05 | 替换字符


我们刚说了,将灰度值以 25 为单位区分,所以我们可以通过像素的值除以 25 ,得到的就是我们刚刚定义 chars 列表的 index ,将 index 对应的这些字符拼起来就可以了:

将刚刚获取的像素替换成字符,打印一下:

我们运行下:

我们刚刚定义的列表是这样的:


而刚刚获取的图片像素值 255 是最多的,所以替换得到的 chars[10] 也就最多,不过为啥现在得到的结果乱七八糟呢?

0x06 | 结构化字符


这是因为我们刚刚缩放了图片的宽高,输出的字符应该要对应相应的宽高,所以需要先获取一下刚刚的字符长度,接着循环它,以缩放后的宽作为步长,这样得到的列表中的每个元素都是固定宽度的字符,接着就可以通过 join 将列表转化为字符串,每一行都铺上相应的字符:

现在再来运行一波:

是不是有点内味了,当然,图片的缩放比可以根据图片再调调,比如我们在计算缩放后的高度给它 × 个 0.66 看看:

再运行一下:

是不是稍微舒服点了?

ok,以上。

部分代码参考至:

https://github.com/RameshAditya/asciify/blob/master/asciify.py

本篇所涉及的完整代码可在公众号【学习python的正确姿势】后台发送 2 获取,主要还是给你分享下思路,希望对你有帮助,那么我们下回见,peace!

发表回复