1. 效果演示
字符画
是一系列字符组合成的文本,看起来就像一幅画一样,如图1所示。如果我们要手写一个字符画,首先要有扎实的美术基础,其次还要花费大量的时间和精力。对于零基础小白而言,困难可想而知。
但是不要发愁,我们可以使用Python
,只需要几行代码,就能够将一张图片轻而易举地转化为一个字符画。这张图片可以是黑白图片,转化效果如图2所示。也可以彩色图片,转化效果如图3所示。
2. 颜色处理
在完成这个案例之前,我们需要一些前置的知识。读者这里使用PS
随意打开一张本地的图片,然后放大,效果如图所示。
由图中我们可以看出一张图片实质是由一个个的小格子
组成的,这个小格子
就是一个像素,把这些小格子
描上点实际上就构成了我们的一幅画。那这个案例的本质就是用字符
去将一个个的小格子
给替换掉,相同像素就使用同一个字符替换,这样最后就形成我们的字符画
了。在了解案例的原理之后,为了方便去处理像素格,我们还需要了解一下其他的几个概念。
灰度值
:指黑白图像中点的颜色深度,范围一般从0到255
, 白色为255,黑色为0
,故黑白图片也称灰度图像。
RGB色彩
:RGB色彩模式是工业界的一种颜色标准。通过对红(R)
、绿(G)
、蓝(B)
三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的。
RGB
即是代表红、绿、蓝
三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。
灰度值和RGB色彩间产生映射:
答案:使用灰度值公式
,将像素的RGB值
映射到灰度值
# r代表红色 g代表绿色 b代表的蓝色
# gray: 代表的是灰度值
gray = 0.2126 * r + 0.7152 * g + 0.0722 * b
注意这个公式并不是一个真实的算法,而是简化的公式,真实的公式更复杂一些,不过在我们的这个应用场景下并没有必要使用真实的公式。
灰度值和字符间的映射:
(1) 首先我们需要有个字符串
# 字符串左边密集,会映射到黑色,黑色的灰度值为0
# 字符串右边稀疏,会映射到白色,白色的灰度值为255
color_str = "wmzвгдеёжзийклмнопрсoahkbdpqjftZO0QLCJUYX/\|()1{}[]?-_+~<>i!lI;:,\"^`'. "
(2) 然后我们将字符串转换成列表
# 通过list()函数进行转换
ascii_char = list(color_str)
这里因为数轴长度的原因,没有将所有的字符都列举出来,实际是有70个字符
去等分256个灰度值
的。
(3) 最后通过公式来,将灰度值映射到字符
# 灰度值范围为0-255,而字符集只有70
# 需要进行如下处理才能将灰度值映射到指定的字符上
# length代表的是字符的长度,即列表ascii_char的长度
unit = (256.0 + 0) / length # 得到的结果表示一个字符代表了灰度值中的3个值
# 返回灰度值对应的字符
arg_str = ascii_char[int(gray / unit)] # ascii_char[index]
# 可以使用整除
# arg_str = ascii_char[gray // unit]
3. 图像处理
刚才在上一节,我们已经学习了如何将彩色值
处理成黑白色值
。那么,我们的彩色值如何来获取呢?
3.1 使用PIL(pillow)处理图像
(1) 首先介绍PIL的使用
PIL是一个Python图像处理库,是本案例使用的重要工具,使用下面的命令来安装pillow(PIL)库
:一定要联网安装
pip install -i http://pypi.douban.com/simple --trusted-host pypi.douban.com pillow
(2) 获取图片对象
from PIL import Image
# 使用open()来获取图片
img = Image.open(path) # img图片对象
# path: 图片地址,字符串类型
(3) 获取图片大小
# size是属性,没有括号
WIDTH, HEIGHT = img.size
(4) 重设图片大小
# 使用resize()重新设置图片大小
new_img = img.resize((width, height),Image.ANTIALIAS)
# width: 图片宽度,数字类型
# height: 图片高度,数字类型
# Image.ANTIALIAS: 高质量
# new_img: 新图片
(5) 获取某个位置的色彩值
rgb_s = img.getpixel((列, 行))
# (列, 行): 座标(列, 行)位置
# rgb_s: RGB像素值(有的时候会包含alpha值),返回的结果是一个元组,例如 (1,2,3)或者(1,2,3,0)。
(6) 修改图片名称和类型
# 使用save()函数进行图片的保存
new_img.save(fileout, type)
# fileout: 图片名称,字符串类型,如: hello.png
# type: 图片类型,字符串类型,如: png/gif/jpg
3.2 完整代码
from PIL import Image # 导入
def resize_image(pic_in, pic_out, width, height, pic_type):
img = Image.open(pic_in)
new_img = img.resize((width, height), Image.ANTIALIAS)
new_img.save(pic_out, pic_type)
if __name__ == '__main__':
pic_in = r"timg.jpeg" # 输入图片
pic_out = r"out.png" # 输出图片
width = 600 # 输出图片宽度
height = 600 # 输出图片高度
pic_type = "png" # 输出图片类型
resize_image(pic_in, pic_out, width, height, pic_type)
4. 案例代码
# -*- coding: utf-8 -*-
# @Time : 2020/4/6 20:19
# @Author : 我就是任性-Amo
# @FileName: test.py
# @Software: PyCharm
# @Blog :https://blog.csdn.net/xw1680
from PIL import Image # 导入
def get_char(r, g, b, alpha=256):
if alpha == 0:
return " "
length = len(ascii_char) # 获取字符集的长度
gray = 0.2126 * r + 0.7152 * g + 0.0722 * b # 将RGB值转为灰度值
unit = 256 / length # 灰度值有256个 而字符集只有70个
return ascii_char[int(gray / unit)]
def get_new_img(img_name=""):
im = Image.open(img_name) # 打开并调整图片的宽和高
WIDTH, HEIGHT = im.size # 获取图片的宽和高
print(f"图片原始的宽和高为:{WIDTH},{HEIGHT}")
if not WIDTH or not HEIGHT:
pass # 防止获取到的图片是损坏的图片,因此进行判断
# 如果是损坏的,宽或者高就没有数据
# 如果没有损坏,宽或高就有数据
else:
if 299 >= WIDTH or 299 >= HEIGHT:
WIDTH = int(WIDTH / 1.4)
HEIGHT = int(HEIGHT / 1.4)
if 500 >= WIDTH >= 300 or 500 >= HEIGHT >= 300:
WIDTH = int(WIDTH / 2)
HEIGHT = int(HEIGHT / 2)
if 1000 > WIDTH > 500 or 1000 > HEIGHT > 500:
WIDTH = int(WIDTH / 3.8)
HEIGHT = int(HEIGHT / 3.8)
if WIDTH >= 1000 or HEIGHT >= 1000:
WIDTH = int(WIDTH / 10)
HEIGHT = int(HEIGHT / 10)
print(f"修改后图片的宽和高为:{WIDTH},{HEIGHT}")
"""
Image.ANTIALIAS: 高质量
Image.NEAREST: 低质量
Image.BILINEAR: 双线性
Image.BICUBIC: 三次样条插值
"""
im = im.resize((WIDTH, HEIGHT), Image.ANTIALIAS)
return im
def get_str_img(im, output=None):
WIDTH, HEIGHT = im.size # 获取图片的宽和高
txt = "" # 初始化输出的字符串
# 遍历图片的中的每一行
for i in range(HEIGHT):
# 遍历图片中的每一列
for j in range(WIDTH):
txt += get_char(*im.getpixel((j, i)))
# 遍历完一行后需要增加换行符
txt += "\n"
# 字幅画输出到文件
if output:
with open(output, "w") as file:
file.write(txt)
else:
with open("output.txt", "w") as file:
file.write(txt)
if __name__ == '__main__':
# 定义字符
ascii_char = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ")
img = get_new_img("timg.jpeg")
get_str_img(img)
运行效果如图所示: