Python使用PIL将图片或GIF转为字符画的方法详解

 更新时间:2025年03月20日 08:27:39   作者:李恩泽的博客  
PIL是专为Python语言设计的图像处理库,它涵盖了广泛的图像处理功能,如图像的加载、保存、编辑,以及执行多样化的图像处理任务,本文给大家介绍了Python使用PIL将图片或GIF转为字符画的方法,需要的朋友可以参考下

图片或GIF转字符画

红及一时的编程小玩具,将图片转为字符画

接下来通过Python实现,比较好玩

图片转换为黑白字符画

  • 安装pillow
pip3 install pillow

灰度值:黑白图像中点的颜色深度,范围一般从0~255,白色为255,黑色为0,所以黑白图片也被成为灰度图像

  • RBG映射灰度公式

我们要将一张图片的灰度处理,将其中的色彩处理为黑白灰亮度图像

越亮的地方用占位百分比越小的字符来替换处理,比如|

而越暗的地方用占位百分比越大的字符来替换处理,比如#

  • 创建一个不重复字符序列数据,灰度值越小(越暗)为序列数据开头,越大(越亮)到序列结尾,长度为90,用来映射256个灰度值,便捷的方法可以直接使用ascii码进行构建

  • 定义函数,用来处理RGB并得出灰度值,根据灰度值,得出一个字符

char_=list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?_+~<>i!lI;:,\"^`'. ")
def get_char(r,g,b, alpha=256):
	'''
		r(红),g(绿),b(蓝)
		total: 灰度值总大小 
		return -> str
	'''
	if alpha == 0:
		return ' '
	gray = 0.2126 * r  + 0.7152 * g + 0.0722 * b #得出灰度值
	char_length = len(char_) #字符序列长度
	index = (alpha+1) / char_length #总灰度值对应列表索引范围
	return char_[int(gray/index)] #返回当前灰度值所对应字符
  • pillow模块可以打卡图像,并重设图像大小,分析图像像素,定义如下函数,取出图像对应坐标像素
from PIL import Image
def convert_image_to_ascii(origin_path, output_path, width=150, height=80):
    """
    将图像转换为ASCII文字并保存到文本文件
    :param origin_path: 输入图像路径
    :param output_path: 输出文本文件路径
    :param width: 输出宽度
    :param height: 输出高度
    :return: None
    """
    # 打开图像,并将其转换为RGBA格式以确保包含alpha通道信息
    img = Image.open(origin_path).convert('RGBA')
    
    #Image.NEAREST 代表设置缩放图片的质量
    img = img.resize((width,height),Image.NEAREST)
 
    #遍历像素,获得灰度值对应字符
    content = ''
    for h in range(height):
        for w in range(width):
            char = get_char(*img.getpixel((w,h)))
            content += char
        content += '\n' #每一行像素换行追加\n
    with open(output_path,'w') as fp:
        fp.write(content)
    return content

  • 运行这段代码,调用analy_image函数,传入待处理图像路径path及保存之后的文件路径file

比如这样一张图片

  • 经过代码处理之后将会变为

  • 如果想把文本存储为图片也可以
from PIL import Image
from PIL import Image, ImageDraw, ImageFont

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


def get_char(r, g, b, alpha=256):
    '''
            r(红),g(绿),b(蓝)
            total: 灰度值总大小 
            return -> str
    '''
    if alpha == 0:
        return ' '
    gray = 0.2126 * r + 0.7152 * g + 0.0722 * b  # 得出灰度值
    char_length = len(char_)  # 字符序列长度
    index = (alpha+1) / char_length  # 总灰度值对应列表索引范围
    return char_[int(gray/index)]  # 返回当前灰度值所对应字符


def convert_image_to_ascii(origin_path, output_path, width=150, height=80):
    """
    将图像转换为ASCII文字并保存到文本文件
    :param origin_path: 输入图像路径
    :param output_path: 输出文本文件路径
    :param width: 输出宽度
    :param height: 输出高度
    :return: None
    """
    # 打开图像,并将其转换为RGBA格式以确保包含alpha通道信息
    img = Image.open(origin_path).convert('RGBA')
    
    #Image.NEAREST 代表设置缩放图片的质量
    img = img.resize((width,height),Image.NEAREST)
 
    #遍历像素,获得灰度值对应字符
    content = ''
    for h in range(height):
        for w in range(width):
            char = get_char(*img.getpixel((w,h)))
            content += char
        content += '\n' #每一行像素换行追加\n
    with open(output_path,'w') as fp:
        fp.write(content)
    return content


def ascii_art_to_image(ascii_art, output_path, font_path=None, font_size=20):
    """
    将ASCII字符艺术转换为图片并保存
    :param ascii_art: ASCII字符艺术字符串
    :param output_path: 输出图片路径
    :param font_path: 字体文件路径,默认为None,表示使用默认字体
    :param font_size: 字体大小
    :return: None
    """
    # 分割ASCII艺术字符串为行列表
    lines = ascii_art.split('\n')

    # 获取最长行的长度和总行数,用于确定图片尺寸
    max_line_length = max(len(line) for line in lines)
    num_lines = len(lines)

    # 设置图片的基本参数
    char_width, char_height = font_size, font_size  # 假设每个字符宽度和高度相等
    margin = 10  # 图片边缘留白

    # 创建白色背景的新图像
    image = Image.new('RGB',
                      (max_line_length * char_width + 2 * margin,
                       num_lines * char_height + 2 * margin),
                      color='white')
    draw = ImageDraw.Draw(image)

    try:
        # 加载字体
        if font_path:
            font = ImageFont.truetype(font_path, font_size)
        else:
            font = ImageFont.load_default()
    except IOError:
        print("加载字体失败,使用默认字体")
        font = ImageFont.load_default()

    # 在图像上绘制ASCII艺术文字
    for i, line in enumerate(lines):
        draw.text((margin, margin + i * char_height),
                  line, fill='black', font=font)

    # 保存图像
    image.save(output_path)
    print(f"已保存图片至 {output_path}")


path = '1.jpg'
file = '1.txt'
content_str = convert_image_to_ascii(path, '1.txt')
ascii_art_to_image(content_str, '1_ASCII.jpg')
  • 完整代码
from PIL import Image

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


def get_char(r, g, b, alpha=256):
    '''
    r(红),g(绿),b(蓝)
    total: 灰度值总大小 
    return -> str
    '''
    if alpha == 0:
        return ' '
    gray = 0.2126 * r + 0.7152 * g + 0.0722 * b  # 得出灰度值
    char_length = len(char_)  # 字符序列长度
    index = (alpha+1) / char_length  # 总灰度值对应列表索引范围
    return char_[int(gray/index)]  # 返回当前灰度值所对应字符


def analy_image(path, file):
    """
    path: 处理图像路径
    file: 处理后的保存文件
    return -> None
    """
    img = Image.open(path)
    width = 80
    height = 80
    img = img.resize((width, height), Image.NEAREST)
    # Image.NEAREST 代表设置缩放图片的质量

    # 遍历像素,获得灰度值对应字符
    content = ''
    for h in range(height):
        for w in range(width):
            char = get_char(*img.getpixel((w, h)))
            # img.getpixel(w,h) 获取对应坐标像素
            content += char
        content += '\n'  # 每一行像素换行追加\n
    print(content)
    with open(file, 'w') as fp:
        fp.write(content)

将GIF转换为动态彩色字符画

思路很简单,将GIF图片处理为一帧一帧的单独图片,再将单独图片灰度处理搞成字符图片,像上面这样,最终再上色并且把一阵一阵的字符图片组合成一个字符GIF

from PIL import Image,ImageDraw,ImageFont
import os
from time import sleep
import imageio

char_ = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ")
#"MNHQ$OC67+>!:-. "
def get_char(r,g,b,alpha=256):
    '''
    r(红),g(绿),b(蓝)
    total: 灰度值总大小 
    return -> str
    '''
    if alpha == 0:
            return ''
    gray = 0.2126 * r  + 0.7152 * g + 0.0722 * b #得出灰度值
    char_length = len(char_) #字符序列长度
    index = (alpha+1) / char_length #总灰度值对应列表索引范围
    return char_[int(gray/index)] #返回当前灰度值所对应字符

def gif2png(path='test.gif'):
    '''
    path: GIF 图像路径
    该函数拆分GIF为单独的每一张PNG图片
    '''
    img = Image.open(path)
    work_path = os.getcwd() #当前工作路径
    cache_dir = os.path.join(work_path,'cache')
    if not os.path.exists(cache_dir):
        #如果不存在保存单独每一帧图片的目录,则创建该目录
        os.mkdir(cache_dir)
    while True:
        try:
            current = img.tell() #获取当前帧位置
            file_name = os.path.join(cache_dir,str(current)+'.png')
            img.save(file_name)
            img.seek(current+1) #向下一帧读取
        except EOFError:
            #GIF读取完毕
            break
    return current

def analy_image(pic_id):
    """
    path: 处理图像路径
    file: 处理后的保存文件
    return -> None
    """
    cache_dir = os.path.join(os.getcwd(),'cache')
    path = os.path.join(cache_dir,'%d.png' % (pic_id))
    img = Image.open(path).convert('RGB')
    #GIF处理后的单帧图片需要转换为RGB格式,否则会报错
    pic_width,pic_height = img.width,img.height
    width = int(pic_width / 6)
    height = int(pic_height / 12)
    img = img.resize((width,height),Image.NEAREST)
    #Image.NEAREST 代表设置缩放图片的质量

    #遍历像素,获得灰度值对应字符
    content = ''
    colors = []
    for h in range(height):
        for w in range(width):
            px = img.getpixel((w,h))
            char = get_char(px[0],px[1],px[2], px[3] if len(px) > 3 else 256)
            colors.append( (px[0],px[1],px[2]) )
            #img.getpixel(w,h) 获取对应坐标像素
            content += char
        content += '\n' #每一行像素换行追加\n
        colors.append( (255,255,255) ) 
    print(content)
    return content,colors,pic_width,pic_height

def text2png(content,colors,pic_width,pic_height,pic_id):
    '''
    将输出的图片字符文本转换为png
    '''
    work_path = os.getcwd() #当前工作路径
    content_dir = os.path.join(work_path,'content')
    if not os.path.exists(content_dir):
        #如果不存在保存单独每一帧图片的目录,则创建该目录
        os.mkdir(content_dir)

    txt_img = Image.new("RGB", (pic_width,pic_height), (255,255,255))
    canvas = ImageDraw.Draw(txt_img) #创建一个可以在给定图像上绘图的对象
    font = ImageFont.load_default().font

    x = 0
    y = 0

    font_w,font_h =  font.getsize(content[1]) #字体的宽高

    for i in range(len(content)):
        if content[i] == '\n':
            x = -font_w #每次初始化横纵坐标
            y += font_h
        canvas.text( (x,y), content[i], colors[i])
        x += font_w # 追加一个字体的像素
    txt_img.save(os.path.join(content_dir,'%d.png' % (pic_id)))

def png2gif(_id,dir_name='content',duration=5 / 130):
    '''
    将之前处理好的字符png图片组合成GIF图像
    通过imageio模块处理合并
    '''
    path = os.path.join(os.getcwd(),dir_name)
    images = []
    for pic_id in range(_id):
        #遍历取出每一张处理后的字符图片id值
        images.append(imageio.imread(os.path.join(path,'%d.png' % pic_id) ) )
        #从文件中读入数据
    imageio.mimsave(os.path.join(os.getcwd(),'fin.gif'), images, duration=duration)
    #保存路径、png图片数据列表、合并帧频率
            
def main():
    path = input(':')
    _id = gif2png(path)
    for pic_id in range(_id+1):
        content,colors,pic_width,pic_height = analy_image(pic_id)
        text2png(content,colors,pic_width,pic_height,pic_id)
        sleep(5 / 130)
        os.system('cls')
    png2gif(_id)

if __name__ == '__main__':
	main()
  • 来看这样一张GIF图片

  • 他会变成这样

以上就是Python使用PIL将图片或GIF转为字符画的方法详解的详细内容,更多关于Python PIL图片或GIF转字符画的资料请关注脚本之家其它相关文章!

相关文章

  • python 实现体质指数BMI计算

    python 实现体质指数BMI计算

    这篇文章主要介绍了python 实现体质指数BMI计算操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-05-05
  • python如何在一个py文件中获取另一个py文件中的值(一个或多个)

    python如何在一个py文件中获取另一个py文件中的值(一个或多个)

    这篇文章主要介绍了python如何在一个py文件中获取另一个py文件中的值(一个或多个),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • python时间序列数据相减的实现

    python时间序列数据相减的实现

    本文主要介绍了python时间序列数据相减的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • 解决每次打开pycharm直接进入项目的问题

    解决每次打开pycharm直接进入项目的问题

    今天小编就为大家分享一篇解决每次打开pycharm直接进入项目的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-10-10
  • Python实现制作透明背景的电子印章

    Python实现制作透明背景的电子印章

    这篇文章主要为大家详细介绍了如何利用Python语言实现制作透明背景的电子印章,文中的示例代码讲解详细,感兴趣的小伙伴可以尝试一下
    2022-09-09
  • Python中的全局变量如何理解

    Python中的全局变量如何理解

    在本篇文章里小编给大家整理的是关于Python中全局变量详解内容,需要的朋友们可以参考下。
    2020-06-06
  • 用python做一个搜索引擎(Pylucene)的实例代码

    用python做一个搜索引擎(Pylucene)的实例代码

    下面小编就为大家带来一篇用python做一个搜索引擎(Pylucene)的实例代码。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • 基于python分享一款地理数据可视化神器keplergl

    基于python分享一款地理数据可视化神器keplergl

    这篇文章主要介绍了分享一款地理数据可视化神器keplergl,keplergl是由Uber开源的一款地理数据可视化工具,通过keplergl我们可以在Jupyter notebook中使用,下文分享需要的小伙伴可以参考一下
    2022-02-02
  • Python利用os模块实现自动删除磁盘文件

    Python利用os模块实现自动删除磁盘文件

    你们一定想不到os模块还可以这样玩,本文就将利用Python中的os模块实现自动删除磁盘文件功能,文中的示例代码讲解详细,感兴趣的可以尝试一下
    2022-11-11
  • python多线程中获取函数返回值的三种方法

    python多线程中获取函数返回值的三种方法

    本文主要介绍了python多线程中获取函数返回值的三种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03

最新评论