Python图片转字符画

用python实现图片转为字符画。

代码来源:python实现图片转字符画

突然好奇怎么把一张图片转为字符画,类似于下图所示效果:

1

上网搜了一下,找到了这么一段代码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系统下用记事本打开输出的字符画,可能会遇到这样的问题:

2

这是因为不同的字符大小不一致,导致无法对齐。解决方式很简单,更换字体(格式 - 字体)为Consolas即可,这是一种代码风格字体,效果如下:

3
  1. python实现图片转字符画 

  2. Grayscale - Wikipedia 

  3. python pillow模块用法 

  4. 可迭代对象(Iteratable Object)是能够一次返回其中一个成员的对象,通常使用for 循环来完成此操作,如字符串、列表、元组、集合、字典等等之类的对象都属于可迭代对象。简单来理解,任何你可以循环遍历的对象都是可迭代对象6 

  5. python 在列表,元组,字典变量前加*号 - CSDN 

  6. Python 中的可迭代对象、迭代器与生成器