目录

1. 介绍

ASCII 字符画 (ASCII Art) 是一种使用 ASCII 文本字符表示图像的创造性表达,可以追溯到图形界面受限的早期计算时代。 尽管技术在不断发展,但 ASCII 字符画经受住了时间的考验,在社交媒体平台、代码库和数字艺术社区中重新流行起来。 这篇文章将向您介绍使用 Python 生成 ASCII 字符画的方法和过程。

2. 准备工作

要使用 Python 制作 ASCII 艺术作品,您需要在计算机上设置 Python 环境。 如果您还没有,请从官方网站 (https://www.python.org/downloads/) 下载并安装 Python。 安装 Python 后,您需要安装 Pillow 库,用于打开、操作和保存图像文件:

要安装 Pillow 库,请打开终端或命令提示符并运行以下命令:

pip install pillow

3. 生成黑白的 ASCII 字符画

从图像创建 ASCII 艺术作品涉及几个基本步骤:调整图像大小、将其转换为灰度以及将灰度像素映射到 ASCII 字符。 在本节中,我们将使用 Python 和 Pillow 库完成这些步骤。

3.1 第 1 步:调整图像大小并将其转换为灰度

要使图像适合 ASCII 字符画,需要先将高分辨率图像调整为较小的分辨率,并将图像转换为灰度,因为 ASCII 字符画主要依靠灰色阴影来表示细节。

import PIL.Image as Image
import PIL.ImageDraw as ImageDraw
import PIL.ImageFont as ImageFont

# 将图像调整为指定大小
def resize_image(image_path, output_width, output_height):
    # 打开图像
    image = Image.open(image_path)

    # 调整图像大小
    image = image.resize((output_width, output_height))

    return image

# 将图像转换为灰度图像
def convert_to_gray(image):    
    return image.convert('L')

3.2 第 2 步:将灰度像素映射到 ASCII 字符

经过第1 步我们有了一个调整大小后的灰度图像,我们需要将每个像素的灰度值映射到一个 ASCII 字符。 为此,我们创建一串按视觉密度排序的 ASCII 字符,并将灰度值映射到这些字符。

# 将像素映射为 ASCII 字符
def map_pixels_to_ascii(gray_image, ascii_chars):
    ascii_image = []

    # 遍历输出图像的每一行
    for y in range(output_height):
        row = ""
        # 遍历输出图像的每一列
        for x in range(output_width):
            # 获取当前像素的灰度值
            gray_value = gray_image.getpixel((x, y))
            # 根据灰度值计算对应的 ASCII 字符的索引
            char_index = int(gray_value / 255 * (len(ascii_chars) - 1))
            # 将对应的 ASCII 字符添加到当前行中
            row += ascii_chars[char_index]
        # 将当前行添加到 ASCII 图像中
        ascii_image.append(row)

    # 将 ASCII 字符画转换为字符串并返回
    return "\n".join(ascii_image)

map_pixels_to_ascii 函数接受两个参数:

  • gray_image,输入的灰度图像。
  • ascii_chars,用于映射像素的 ASCII 字符集。ASCII 字符集的原则是按视觉密度排序,如@%#*+=-:.

3.3 第 3 步:将图像转换为 ASCII 字符画

通过组织第1步、第2步中的函数我们可以将图像转换为 ASCII 字符画。其基本流程为:调整图像大小 → 转换为灰度图像 → 用 ASCII 字符替换像素。

# 将图像转换为黑白 ASCII 字符画
def image_to_bw_ascii(image_path, output_width, output_height):
    # 调整图片大小
    resized_image = resize_image(image_path, output_width, output_height)

    # 转换为灰度图像
    gray_image = convert_to_gray(resized_image)    

    # 用 ASCII 字符替换像素
    ascii_image = map_pixels_to_ascii(gray_image, bw_ascii_chars)

    return ascii_image

image_to_bw_ascii函数接收三个参数:

  • image_path,原始图片。可以是绝对路径,也可以是相对路径。
  • output_width,输出字符画的宽度。对于文本形式的字符画,如显示在终端窗口的字符画,其单位为列;对于图像形式的字符画,如存储在图片文件中的字符画,其单位为像素。
  • output_height,输出字符画的高度。对于文本形式的字符画,如显示在终端窗口的字符画,其单位为行;对于图像形式的字符画,如存储在图片文件中的字符画,其单位为像素。

3.4 第 4 步:存储 ASCII 字符画到文本文件

ASCII 字符画是由字符组成的,因此可以将其存储到文本文件中。

# 存储黑白 ASCII 字符画到文本文件
def save_bw_ascii_image_as_text(ascii_image, output_path):
    with open(output_path, 'w') as file:
        file.write(ascii_image)

save_bw_ascii_image_as_text函数接收三个参数:

  • ascii_image,ASCII 字符画。
  • output_path,存储 ASCII 字符画的文本文件。

3.5 第 5 步:存储 ASCII 字符画到图像文件

使用 Pillow 库可以将 ASCII 字符画存储为 jpg、png 等图片文件。

# 存储黑白 ASCII 字符画到图片
def save_bw_ascii_image_as_picture(ascii_image, font_path, font_size, output_path):
    # 将输入的 ASCII 字符画按换行符拆分
    lines = ascii_image.split('\n')
    # 根据最长行的长度和行数计算出结果图像的宽度和高度
    width = max(len(line) for line in lines)
    height = len(lines)

    # 使用计算出的尺寸和白色背景颜色创建一个新的 RGB 图像
    image = Image.new('RGB', ((width * font_size), height * font_size), color='white')
    # 使用 ImageDraw.Draw 方法创建一个 Draw 对象
    draw = ImageDraw.Draw(image)
    # 使用 ImageFont.truetype 方法创建一个 Font 对象
    font = ImageFont.truetype(font_path, font_size)

    # 遍历 ASCII 艺术输入中的每个字符,并使用 Draw.text 方法将其绘制到图像上
    for y, line in enumerate(lines):
        for x, char in enumerate(line):
            # 根据其 x 和 y 坐标以及 font_size 参数计算每个字符的位置
            draw.text((x * font_size, y * font_size), char, font=font, fill='black')

    # 使用 Image.save 方法将生成的图像保存到 output_path 参数中
    image.save(output_path)

save_bw_ascii_image_as_picture函数接收四个参数:

  • ascii_image,ASCII 字符画。
  • font_path,绘图所用字体。
  • font_size,绘图字体大小。
  • output_path,图片文件。

3.6 效果展示

把上面的步骤串联起来就可以将一幅图片转换为 ASCII 字符画,并将它输出到终端、保存到文本文件、保存到图片文件中。

# 定义输出宽度和高度(行/像素)
output_width = 60
output_height = 35

# 定义黑白 ASCII 字符画绘图字体和字体大小
bw_ascii_image_font = "arial.ttf"
bw_ascii_image_font_size = 8

# 定义黑白 ASCII 字符画替换像素的 ASCII 字符
bw_ascii_chars = "@%#*+=-:. "

if __name__ == "__main__":
    image_path = "path/to/your/image.jpg"

    # 生成黑白 ASCII 字符画
    bw_ascii_image = image_to_bw_ascii(image_path, output_width, output_height)

    # 输出到终端
    print(bw_ascii_image)

    # 存储黑白 ASCII 字符画到文本文件
    save_bw_ascii_image_as_text(bw_ascii_image, "bw_ascii_image.txt")

    # 存储黑白 ASCII 字符画到图片文件
    save_bw_ascii_image_as_picture(bw_ascii_image, bw_ascii_image_font, bw_ascii_image_font_size, "bw_ascii_image.png")

注意将 "path/to/your/image.jpg" 替换为图像文件的路径。 您可以调整 output_widthoutput_height 变量来控制生成的 ASCII 字符画的大小。您还可以调整bw_ascii_chars变量来观察不同字符生成的 ASCII 字符画的效果。

下面是生成的 ASCII 字符画的效果示例。

原图:

终端输出效果:

生成的文本文件效果:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@###@@@@@@@@@@@@@@@@@@@@%####@@@@@@@@@@@@@@@
@@@@@@@@@@@@@##*######@@@@@@@@@@@@@@@@##*######@@@@@@@@@@@@@
@@@@@@@@@@@@@#+.:---*# @@@@@@@@@@@@@@ #+.::::*#@@@@@@@@@@@@@
@@@@@@@@@@@@@#*++***## @@@@@@@@@@@@@@ #*+++++*#@@@@@@@@@@@@@
@@@@@@@@@@@@@##+:::=##@@@@@@@@@@@@@@@@*##-:-###@@@@@@@@@@@@@
@@@@@@@@@@@@##=     =##@@@@@@@@@@@@@ ###*. .*###@@@@@@@@@@@@
@@@@@@@@@@@##-       -##@@@@@@@@@@@##*=:.   .:=*##@@@@@@@@@@
@@@@@@@@@@##-  .......-##@@@@@@@@@##=. .........=##@@@@@@@@@
@@@@@@@@@*#-  .:.......=#*@@@@@@@*#:  :::::::::::=#*@@@@@@@@
@@@@@@@@ #+. ...........*#:@@@@@-#+  .::::::::::::*# @@@@@@@
@@@@@@@@*#:  .:.........-#*@@@@@+#=  .::::::::::::+#-@@@@@@@
@@@@@@@@#*. ............:*#@@@@@+#=  .::::::::::::+#-@@@@@@@
@@@@@@@ #+  .:...........+#:@@@@+#=  .::::::::::::+#-@@@@@@@
@@@@@@@:#=  .............=#+@@@@+#=  .::::::::::::=#-@@@@@@@
@@@@@@@*#*+++++++++++++++*#*@@@@+#*+++*************#*@@@@@@@
@@@@@@@*#+::-============*#*@@@@+#+:::------------+#*@@@@@@@
@@@@@@@*#=  .------------+#*@@@@+#=  .::::::::::::+#*@@@@@@@
@@@@@@@*#=  :--------====*#*@@@@+#=  .::::::::----+#*@@@@@@@
@@@@@@@*#=  :--------****##*@@@@+#=  .:::::::-+**+##*@@@@@@@
@@@@@@@*#=  :------------+#*@@@@+#=  .::::::::::::+#*@@@@@@@
@@@@@@@*#=  :------------+#*@@@@+#=  .::::::::----+#*@@@@@@@
@@@@@@@*#=  :--------****##*@@@@+#=  .:::::::-****##*@@@@@@@
@@@@@@@*#=  :------------+#*@@@@+#=  .::::::::::::+#*@@@@@@@
@@@@@@@*#=  .------------+#*@@@@+#=  .::::::::::::+#*@@@@@@@
@@@@@@@*#*==+***********+*#*@@@@+#*==+++++++++++++*#*@@@@@@@
@@@@@@@=#+---------------+#*@@@@+#+---============+#-@@@@@@@
@@@@@@@-#-  .............=#*@@@@+#=  .::::::::::::=#-@@@@@@@
@@@@@@@-#=  .:...........=#*@@@@+#=  .::::::::::::+#-@@@@@@@
@@@@@@@-#=  .:...........=#+@@@@+#=  .::::::::::::+#:@@@@@@@
@@@@@@@ #*. ............:*# @@@@ #*. .::::::::::::*# @@@@@@@
@@@@@@@@*#*-..:::::::::-*#*@@@@@@*#*-..::::::::-=*#*@@@@@@@@
@@@@@@@@@@###*********###@@@@@@@@@@###********####@@@@@@@@@@
@@@@@@@@@@@@@*#######*@@@@@@@@@@@@@@@@@##******@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

生成的图片效果:

3.7 完整代码

# gen-bw-ascii-image.py

import PIL.Image as Image
import PIL.ImageDraw as ImageDraw
import PIL.ImageFont as ImageFont

# 定义输出宽度和高度(行/像素)
output_width = 60
output_height = 35

# 定义黑白 ASCII 字符画绘图字体和字体大小
bw_ascii_image_font = "arial.ttf"
bw_ascii_image_font_size = 8

# 定义黑白 ASCII 字符画替换像素的 ASCII 字符
bw_ascii_chars = "@%#*+=-:. "

# 将图像调整为指定大小
def resize_image(image_path, output_width, output_height):
    image = Image.open(image_path)
    image = image.resize((output_width, output_height))
    return image

# 将图像转换为灰度图像
def convert_to_gray(image):    
    return image.convert('L')

# 将像素映射为 ASCII 字符
def map_pixels_to_ascii(gray_image, ascii_chars):
    ascii_image = []

    for y in range(output_height):
        row = ""
        for x in range(output_width):
            gray_value = gray_image.getpixel((x, y))
            char_index = int(gray_value / 255 * (len(ascii_chars) - 1))
            row += ascii_chars[char_index]
        ascii_image.append(row)

    return "\n".join(ascii_image)


# 将图像转换为黑白 ASCII 字符画
def image_to_bw_ascii(image_path, output_width, output_height):
    resized_image = resize_image(image_path, output_width, output_height)

    gray_image = convert_to_gray(resized_image)    

    ascii_image = map_pixels_to_ascii(gray_image, bw_ascii_chars)

    return ascii_image


# 存储黑白 ASCII 字符画到文本文件
def save_bw_ascii_image_as_text(ascii_image, output_path):
    with open(output_path, 'w') as file:
        file.write(ascii_image)


# 存储黑白 ASCII 字符画到图片
def save_bw_ascii_image_as_picture(ascii_image, font_path, font_size, output_path):
    lines = ascii_image.split('\n')
    width = max(len(line) for line in lines)
    height = len(lines)

    image = Image.new('RGB', ((width * font_size), height * font_size), color='white')
    draw = ImageDraw.Draw(image)
    font = ImageFont.truetype(font_path, font_size)

    for y, line in enumerate(lines):
        for x, char in enumerate(line):
            draw.text((x * font_size, y * font_size-), char, font=font, fill='black')

    image.save(output_path)


if __name__ == "__main__":
    image_path = "path/to/your/image.jpg"

    # 生成黑白 ASCII 字符画
    bw_ascii_image = image_to_bw_ascii(image_path, output_width, output_height)

    # 存储黑白 ASCII 字符画到文本文件
    save_bw_ascii_image_as_text(bw_ascii_image, "bw_ascii_image.txt")

    # 存储黑白 ASCII 字符画到图片文件
    save_bw_ascii_image_as_picture(bw_ascii_image, bw_ascii_image_font, bw_ascii_image_font_size, "bw_ascii_image.png")

    # 在终端窗口打印彩色 ASCII 字符画
    print(bw_ascii_image)

4. 生成彩色的 ASCII 字符画

在色彩斑斓的现代社会 ASCII 字符画也要进步,下面我们探索如何生成彩色的 ASCII 字符画。

生成彩色 ASCII 字符画的步骤与生成黑白 ASCII 字符画的步骤基本相同,详述如下。

4.1 第 1 步:调整图像大小并将其转换为灰度

此步骤与生成黑白 ASCII 字符画的第 1 步相同,此处略过。

4.2 第 2 步:将图像转换为 ASCII 字符画

对于彩色字符画我们需要逐个像素地扫描图像,提取像素颜色,并完成像素到字符的映射,同时将扫描的信息存储起来,从而实现将图像转换为彩色的 ASCII 字符画。

# 将图像转换为彩色 ASCII 字符画
def image_to_colored_ascii(image_path, output_width, output_height):
    # 调整图片大小
    resized_image = resize_image(image_path, output_width, output_height)

    # 创建 ASCII 字符画
    ascii_image = []
    ascii_image_chars = []
    ascii_image_colors=[]
    for y in range(output_height):
        row = ""
        for x in range(output_width):
            # 获取像素颜色
            color = resized_image.getpixel((x, y))

            # 计算灰度值并选择相应的 ASCII 字符
            gray_value = sum(color) // 3
            char_index = int(gray_value / 255 * (len(colored_ascii_chars) - 1))
            if char_index >= len(colored_ascii_chars):
                char_index = len(colored_ascii_chars) - 1

            # 将 ASCII 字符和颜色添加到行中
            row += f"\033[38;2;{color[0]};{color[1]};{color[2]}m{colored_ascii_chars[char_index]}"

            # 存储字符和颜色
            ascii_image_chars.append(colored_ascii_chars[char_index])
            ascii_image_colors.append((color[0], color[1], color[2]))

        # 将行添加到 ASCII 字符画中
        ascii_image.append(row)

    return "\n".join(ascii_image), ascii_image_chars, ascii_image_colors

4.3 第 3 步:存储 ASCII 字符画到图像文件

下面的函数是将彩色 ASCII 字符画存储为图片。它通过创建一个新的 RGB 图像来实现,该图像的尺寸为 output_width * font_size和 output_height * font_size。然后使用 ImageDraw 模块将 ASCII 字符以及相应的颜色绘制到图像上。最后,将图像保存到指定的 output_path。

# 将彩色 ASCII 艺术图像存储为图片
def save_colored_ascii_image_as_picture(ascii_image_chars, ascii_image_colors, font_path, font_size, output_path):
    # 创建一个新的 RGB 图像
    image = Image.new('RGB', ((output_width * (font_size-3)), (output_height * (font_size-1))), color='black')
    draw = ImageDraw.Draw(image)
    font = ImageFont.truetype(font_path, font_size)

    # 绘制 ASCII 字符和颜色
    for i in range(output_height):
        for j in range(output_width):
            draw.text((j * (font_size-3), i * (font_size-1)), ascii_image_chars[i*output_width+j], font=font, fill=ascii_image_colors[i*output_width+j])

    # 保存图像
    image.save(output_path)

4.4 效果展示

将上面的代码组织一下就可以实现我们的目标了:将一幅图片转换为彩色 ASCII 字符画,并将它输出到终端、保存到图片文件中。

# 定义输出宽度和高度(行/像素)
output_width = 60
output_height = 35

# 定义彩色 ASCII 字符画绘图字体和字体大小
colored_ascii_image_font = "arial.ttf"
colored_ascii_image_font_size = 8

# 定义彩色 ASCII 字符画替换像素的 ASCII 字符
colored_ascii_chars = ["0", "1", "2", "3", "4", "5", "6", "8", "9"]

if __name__ == "__main__":
    image_path = "path/to/your/image.jpg"

    # 将图像转换为彩色 ASCII 字符画
    colored_ascii_image, colored_ascii_image_chars, colored_ascii_image_colors = image_to_colored_ascii(image_path, output_width, output_height)

    # 存储彩色 ASCII 字符画到图片文件
    save_colored_ascii_image_as_picture(colored_ascii_image_chars, colored_ascii_image_colors, colored_ascii_image_font, colored_ascii_image_font_size, "colored_ascii_image.png")

    # 在终端窗口打印彩色 ASCII 字符画
    print(colored_ascii_image)

注意将 "path/to/your/image.jpg" 替换为图像文件的路径。

下面是生成的彩色 ASCII 字符画的效果示例。

终端输出效果:

生成的图片效果:

4.5 更多效果展示

4.6 完整代码

# gen-colored-ascii-image.py

import PIL.Image as Image
import PIL.ImageDraw as ImageDraw
import PIL.ImageFont as ImageFont

# 定义输出宽度和高度(行/像素)
output_width = 60
output_height = 35

# 定义彩色 ASCII 字符画绘图字体和字体大小
colored_ascii_image_font = "arialbd.ttf"
colored_ascii_image_font_size = 8

# 定义彩色 ASCII 字符画替换像素的 ASCII 字符
colored_ascii_chars = ["0", "1", "2", "3", "4", "5", "6", "8", "9"]

# 将图像调整为指定大小
def resize_image(image_path, output_width, output_height):
    image = Image.open(image_path)
    image = image.resize((output_width, output_height))
    return image


# 将图像转换为灰度图像
def convert_to_gray(image):    
    return image.convert('L')


# 将图像转换为彩色 ASCII 字符画
def image_to_colored_ascii(image_path, output_width, output_height):
    resized_image = resize_image(image_path, output_width, output_height)

    ascii_image = []
    ascii_image_chars = []
    ascii_image_colors=[]
    for y in range(output_height):
        row = ""
        for x in range(output_width):
            color = resized_image.getpixel((x, y))

            gray_value = sum(color) // 3
            char_index = int(gray_value / 255 * (len(colored_ascii_chars) - 1))
            if char_index >= len(colored_ascii_chars):
                char_index = len(colored_ascii_chars) - 1

            row += f"\033[38;2;{color[0]};{color[1]};{color[2]}m{colored_ascii_chars[char_index]}"

            ascii_image_chars.append(colored_ascii_chars[char_index])
            ascii_image_colors.append((color[0], color[1], color[2]))

        ascii_image.append(row)

    return "\n".join(ascii_image), ascii_image_chars, ascii_image_colors


# 将彩色 ASCII 艺术图像存储为图片
def save_colored_ascii_image_as_picture(ascii_image_chars, ascii_image_colors, font_path, font_size, output_path):
    image = Image.new('RGB', ((output_width * font_size), (output_height * font_size)), color='black')
    draw = ImageDraw.Draw(image)
    font = ImageFont.truetype(font_path, font_size)

    for i in range(output_height):
        for j in range(output_width):
            draw.text((j * font_size, i * font_size), ascii_image_chars[i*output_width+j], font=font, fill=ascii_image_colors[i*output_width+j])

    image.save(output_path)

if __name__ == "__main__":
    image_path = "path/to/your/image.jpg"

    # 将图像转换为彩色 ASCII 字符画
    colored_ascii_image, colored_ascii_image_chars, colored_ascii_image_colors = image_to_colored_ascii(image_path, output_width, output_height)

    # 存储彩色 ASCII 字符画到图片文件
    save_colored_ascii_image_as_picture(colored_ascii_image_chars, colored_ascii_image_colors, colored_ascii_image_font, colored_ascii_image_font_size, "colored_ascii_image.png")

    # 在终端窗口打印彩色 ASCII 字符画
    print(colored_ascii_image)

5. 总结

本文向您介绍了 ASCII 字符画的迷人世界,并展示了 Python 如何成为制作您自己的字符画杰作的强大工具。 您可以尝试不同的图像、不同的设置来创建独特且令人着迷的 ASCII 艺术作品。您还可以在本文的基础上继续探索,创造更多有趣的功能。

6. 附录

以下是一些与 ASCII 字符画相关的资源,您可以在其中找到创建 ASCII 艺术的示例和灵感:


相关博客文章

相关书籍教程文章
官方公众号

💯本站文章同步发表在官方公众号 ReadingHere,关注公众号您将在第一时间了解本站最新文章和资讯。

❤️欢迎您关注本站官方公众号 ReadingHere


版权声明

本文由ReadingHere原创,未经ReadingHere授权不得转载、摘编。已经授权使用的,应在授权范围内使用,并注明来源: www.readinghere.com。违反上述声明者,ReadingHere将追究其相关法律责任。


交流合作

如需交流咨询或商务合作请扫描下图微信二维码联系。