用python实现图片转为字符画。
代码来源:python实现图片转字符画
突然好奇怎么把一张图片转为字符画,类似于下图所示效果:
上网搜了一下,找到了这么一段代码1:
#-*- coding:utf-8 -*-
from PIL import Image
IMG='D:\Code\python\\test1\\ascii_dora.png'
WIDTH=60
HEIGHT=45
ascii_char = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ")
#将256灰度映射到70个字符上
def get_char(r,g,b,alpha=256):#alpha透明度
if alpha==0:
return ' '
length=len(ascii_char)
gray=int(0.2126*r+0.7152*g+0.0722*b)#计算灰度
unit=(256.0+1)/length # 字符对应的灰度区间宽度
return ascii_char[int(gray/unit)]#不同的灰度对应着不同的字符
#通过灰度来区分色块
if __name__=='__main__':
im=Image.open(IMG)
im=im.resize((WIDTH,HEIGHT),Image.NEAREST)
txt=""
for i in range(HEIGHT):
for j in range(WIDTH):
txt+=get_char(*im.getpixel((j,i)))
txt+='\n'
print (txt)
#写入文件
with open("output.txt",'w') as f:
f.write(txt)
核心挺容易理解的,就是先把彩色图像转换为灰度图像,再将不同的灰度对应到不同的字符上,然后输出。
有些挺有趣的小知识点的,在这篇博客里记录一下。
有关图像处理的一些概念
灰度(grayscale)
In digital photography, computer-generated imagery, and colorimetry, a grayscale image is one in which the value of each pixel is a single sample representing only an amount of light; that is, it carries only intensity information.(灰度图像指的是每一个像素点仅有一个描述亮度的参数的图像。)2
RGB图像灰度计算:0.2126*r+0.7152*g+0.0722*b
。(字符画核心步骤之一)
图像重采样(resampling)
对图像进行缩放,实际上就是旧图像到新图像的映射,有多种映射算法,如Nearest Neighbour Resampling, Bicubic Resampling, Lanczos Resampling 等。这些不同的映射方式被称为图像重采样。
python的pillow库
Pillow是PIL的一个派生分支,但如今已经发展成为比PIL本身更具活力的图像处理库。pillow可以说已经取代了PIL,将其封装成python的库(pip即可安装),且支持python2和python3,目前最新版本是3.0.0。3使用命令pip install pillow
即可安装。
用pillow库可以更改、裁剪图片,获得图片的RGB值,进行一些图像处理(如模糊、浮雕、获得轮廓)等等。使用说明手册:Handbook — Pillow (PIL Fork) 9.0.1 documentation
resize()函数
pillow库里的resize()函数定义
def resize(self, size, resample=None, box=None, reducing_gap=None)
作用:缩放图像。
参数:size - 希望缩放到的大小,是一个二元组(width, height)
resample - 可选参数,图像重采样方式。PIL库提供了多种filter,可参阅此处。
box - 可选参数,表示欲缩放的区域,是一个四元组。
reducing_gap - 没看明白,暂时也不关心。
返回值:缩放后的图像。
if __name__=='__main__'
有什么用?
可以看这篇文章:Python if __name__ == __main__ Explained with Code Examples。但是它写的有点啰嗦,扼要地解释一下:
__name__
是什么变量?
Python解释器(interpreter)读取一个Python文件时,会首先声明一些特殊的变量,然后再去执行文件中的代码。__name__
就是其中一个特殊的变量。
__name__
的值
Python文件被称为模块(module),以.py
作为文件后缀。模块中可以定义方法(函数)、类和变量。一个模块中可以导入其它模块,就是import语句。
当解释器正在执行的是本模块时,__name__
的值为__main__
;当解释器正在执行的是导入的模块时,__name__
的值为该模块的模块名。
例如,编写一个模块file_one.py
:
# Python file one module
print("File one __name__ is set to: {}" .format(__name__))
再编写一个模块file_two.py
:
# Python file two module
import file_one
print("File two __name__ is set to: {}" .format(__name__))
运行模块file_two.py
,运行结果为:
File one __name__ is set to: file_one
File two __name__ is set to: __main__
if __name__=='__main__'
有什么用?
当我们只想在运行本模块时执行某些语句,就可以将这些语句编写到if __name__=='__main__'
分支中。
这样,其他模块导入这个模块时,并不会运行这些语句。
在可迭代对象前加*
号
unpacking operator *
注意到程序中有这么一行:
txt += get_char(*im.getpixel((j,i)))
这里的*
被称作unpacking operator,它的作用是解包(unpack)可迭代对象(iteratable object)4,通俗地说,就是将列表、字典等拆分成多个参数。5例如,下面的程序
a = [1, 2, 4]
print(a)
print(*a)
print('------------------')
a = {'a': 1, 'b': 2}
print(a)
print(*a)
print(*a.values())
print('------------------')
a = "hello"
print(a)
print(*a)
运行结果为
[1, 2, 4]
1 2 4
------------------
{'a': 1, 'b': 2}
a b
1 2
------------------
hello
h e l l o
注意,字典解包后,如果不使用values()函数,得到的是键,而不是值。
转字符画代码中,im.getpixel((j, i))
得到了(j, i)位置像素点的RGB参数,并以元组的形式返回,前面加*
后将元组分裂成三个变量,再用get_char()
函数接受,事实上就是将这三个参数分别对应传给了get_char()
函数的r, g, b
三个参数。
*args
与**kwargs
这篇文章作了详细的解释:Python args and kwargs: Demystified。
简单地说,二者作为函数的参数,用于向函数中传递任意数量的参数。其中
*args
将得到的参数打包成元组,**kwargs
将得到的参数打包成字典(当然你也可以起其它的名字,比如*numbers
,**dic
等,习惯上使用*args
与**kwargs
)。下面的例子使用了
*args
:# sum_integers_args.py def my_sum(*args): result = 0 # Iterating over the Python args tuple for x in args: result += x return result print(my_sum(1, 2, 3))
运行结果:
6
下面的例子使用了
**kwargs
:# concatenate.py def concatenate(**kwargs): result = "" # Iterating over the Python kwargs dictionary for arg in kwargs.values(): result += arg return result print(concatenate(a="Real", b="Python", c="Is", d="Great", e="!"))
运行结果:
RealPythonIsGreat!
更多介绍(如用
*
来分裂(split)列表、传参顺序等)请参看节首文章。
记事本字符大小不一致导致字符画无法对齐
Windows系统下用记事本打开输出的字符画,可能会遇到这样的问题:
这是因为不同的字符大小不一致,导致无法对齐。解决方式很简单,更换字体(格式 - 字体)为Consolas即可,这是一种代码风格字体,效果如下: