【问题标题】:PPM image to ASCII art in PythonPPM图像到Python中的ASCII艺术
【发布时间】:2011-11-14 23:55:28
【问题描述】:

我必须编写一个程序,从命令行读取文件并将其转换为 ASCII 艺术。我使用的是 PPM 格式,这里有一个指向 the project 的链接。

这是我目前所拥有的:

import sys

def main(filename):
    image = open(filename)
    #reads through the first three lines
    color = image.readline().splitlines()
    size_width, size_height = image.readline().split()
    max_color = image.readline().splitlines()

    #reads the body of the file
    pixels = image.read().split()
    red = 0
    green = 0
    blue = 0
    r_g_b_value = []
    #pulls out the values of each tuple and coverts it to its grayscale value 
    for i in pixels:
        if i !=  "\n" or " ":
            if len(i) == 3:
                red = int(i[0]) * .3
                green = int(i[1]) * .59
                blue = int(i[2]) * .11
            elif len(i) == 2:
                red == int(i[0]) * .3
                green == int(i[1]) *.59
                blue == 0
            elif len(i) == 1:
                red == int(i[0]) * .3
                green == 0
                blue == 0

            r_g_b_value = [red + green + blue]

            character = []
        for j in len(r_g_b_value):
            if int(j) <= 16:
                character = " "
            elif int(j) > 16 and int(j) <= 32:
                character = "."
            elif int(j) > 32 and int(j) <= 48:
                character = ","
            elif int(j) > 48 and int(j) <= 64:
                charcter = ":"
            elif int(j) > 64 and int(j) <= 80:
                character = ";"
            elif int(j) > 80 and int(j) <= 96:
                character = "+"
            elif int(j) > 96 and int(j) <= 112:
                character = "="
            elif int(j) > 112 and int(j) <= 128:
                character = "o"
            elif int(j) > 128 and int(j) <= 144:
                character = "a"
            elif int(j) > 144 and int(j) <= 160:
                character = "e"
            elif int(j) > 160 and int(j) <= 176:
                character = "0"
            elif int(j) > 176 and int(j) <= 192:
                character = "$"
            elif int(j) > 192 and int(j) <= 208:
                character = "@"
            elif int(j) > 208 and int(j) <= 224:
                character = "A"
            elif int(j) > 224 and int(j) <= 240:
                character = "#"
            else:
                character = "M"

            grayscale = character
            print(grayscale)

main(sys.argv[1])

我收到一个错误,提示“int”对象不可迭代,是否有一种简单的方法可以解决此问题,以及如何建议在保留图像的同时将其打印出来。

最后我不确定的是如何保持图片的宽度和高度。

任何帮助将不胜感激。

【问题讨论】:

  • 我在这里迷路了,似乎当我打印 r_g_b_value 时,它​​每行打印一个值。
  • 我在想如果我为 j in range(r_g_b__values) 执行一系列 if/else 语句 int(j),将值设置为可用字符列表中的值,会这项工作以及保存角色的最佳方法是什么。会是新名单吗?
  • 如果您仍然收到错误,完整的回溯会很有帮助。
  • 我摆脱了这个错误,但是你有什么想法在保留图像的同时打印出这个字符

标签: python python-3.x ascii-art


【解决方案1】:

您可以使用image-to-ansi.py 进行转换。

首先,下载image-to-ansi.py

wget https://gist.githubusercontent.com/klange/1687427/raw/image-to-ansi.py

将此脚本另存为ppmimage.py:

# Parses a PPM file and loads it into image-to-ansi.py
import re, itertools

sep = re.compile("[ \t\r\n]+")

def chunks(iterable,size):
    """ http://stackoverflow.com/a/434314/309483 """
    it = iter(iterable)
    chunk = tuple(itertools.islice(it,size))
    while chunk:
        yield chunk
        chunk = tuple(itertools.islice(it,size))

""" Emulates the Image class from PIL and some member functions (`getpixel`, `size`). """
class Image:
    """ This class emulates the PIL Image class, and it can parse "plain" PPM's.
        You can use PIL instead. """
    @staticmethod
    def fromstdin():
        return Image()
    def __init__(self): # http://netpbm.sourceforge.net/doc/ppm.html
        self.entities = sep.split("\n".join(list(filter(lambda x: not x.startswith("#"), sys.stdin.read().strip().split("\n")))))
        self.size = tuple(list(map(int,self.entities[1:3])))
        self.high = int(self.entities[3]) # z.b. 255
        self.pixels = list(map(lambda x: tuple(map(lambda y: int(int(y) / self.high * 255), x)), list(chunks(self.entities[4:], 3))))
    def getpixel(self, tupl):
        x = tupl[0]
        y = tupl[1]
        pix = self.pixels[y*self.size[0]+x]
        return pix

image_to_ansi = __import__("image-to-ansi") # __import__ because of minuses in filename. From https://gist.github.com/1687427

if __name__ == '__main__':
    import sys
    #import Image
    im = Image.fromstdin() # use Image.open from PIL if using PIL
    for y in range(im.size[1]):
        for x in range(im.size[0]):
            p = im.getpixel((x,y))
            h = "%2x%2x%2x" % (p[0],p[1],p[2])
            short, rgb = image_to_ansi.rgb2short(h)
            sys.stdout.write("\033[48;5;%sm " % short)
        sys.stdout.write("\033[0m\n")
    sys.stdout.write("\n")

您可以像这样测试脚本(假设您安装了netpbmimagemagick):

convert -resize $(($COLUMNS*2))x$(($LINES*2)) logo: pnm:- | pnmtoplainpnm | python3 ppmimage.py

在我的机器上是这样的:

【讨论】:

    【解决方案2】:

    在这里,您的代码已修改并可以正常工作。
    它不是最优的,它没有考虑到头部中是否存在或多或少的 cmets,也没有异常处理,但这只是一个开始。

    import sys
    import numpy as np
    
    RGBS = range(16, 255, 16)
    CHARS = [' ', '.', ',', ':', ';', '+', '=', 'o',
             'a', 'e', '0', '$', '@', 'A', '#']
    FACTORS = [.3, .59, .11]
    
    def main(filename):
        image = open(filename)
        #reads header lines
        color = image.readline()
        _ = image.readline()
        size_width, size_height = image.readline().split()
        max_color = image.readline()
    
        size_width = int(size_width)
        max_color = int(max_color)
    
        #reads the body of the file
        data = [int(p) for p in image.read().split()]
        #converts to array and reshape
        data = np.array(data)
        pixels = data.reshape((len(data)/3, 3))
        #calculate rgb value per pixel
        rgbs = pixels * FACTORS
        sum_rgbs = rgbs.sum(axis=1)
        rgb_values = [item * 255 / max_color for item in sum_rgbs]
    
        grayscales = []
        #pulls out the value of each pixel and coverts it to its grayscale value 
        for indx, rgb_val in enumerate(rgb_values):
            #if max width, new line
            if (indx % size_width) == 0 : grayscales.append('\n')    
    
            for achar, rgb in zip(CHARS, RGBS):
                if rgb_val <= rgb:
                    character = achar
                    break
                else:
                    character = 'M'
    
            grayscales.append(character)
    
        print ''.join(grayscales)
    
    main('test.ppm')
    

    这些是 ppm 数字和 ASCII 艺术结果

    以及我用于示例的 micro ppm 文件:

    P3
    # test.ppm
    4 4
    15
     0  0  0    0  0  0    0  0  0   15  0 15
     0  0  0    0 15  7    0  0  0    0  0  0
     0  0  0    0  0  0    0 15  7    0  0  0
    15  0 15    0  0  0    0  0  0    0  0  0
    

    【讨论】:

    • 此答案中提供的此解决方案不提供颜色支持。请参阅我的回答,了解如何牺牲对糟糕的 Windows 控制台的支持并获得颜色支持。
    • @JanusTroelsen 谢谢,但请注意 OP 询问的是 ASCII 艺术,而不是 ANSI 艺术
    【解决方案3】:

    我不久前在 C# 中编写了其中一个,我计算了要与这个公式一起使用的字符:

    index_into_array = (int)(r_g_b_value * (chars_array_length / (255.0)));
    

    对于宽度和高度,你可以平均每两行垂直像素将高度减半。

    编辑1:回应评论: 基本思想是,它将 RGB 值从 0 到 255 缩放到 0 到数组的长度,并将其用作索引。

    编辑 2:更新以纠正我忽略了您的灰度标准化。

    【讨论】:

    • 我仍然不太擅长编码,所以我不确定我是否完全理解,我将可用列表设置为 chars_array_length,然后使用您发布的函数,然后将该值打印到得到角色还是我需要做其他事情。再次感谢您的帮助
    • @asmith 更新了答案以进一步解释。
    • 我再次为我缺乏技能而道歉,但我在 main(sys.argv[1]) 文件“other.py”中收到此错误文件“other.py”,第 40 行", line 36, in main current_character = character_choices[int((r_g_b_value * (len(character_choices) /(255.0*3))))] TypeError: can't multiply sequence by non-int of type 'float'
    • 是的,我不知道 python,你必须自己将我发布的算法转换为 python 代码或更新你/问另一个问题。
    猜你喜欢
    • 1970-01-01
    • 2016-01-04
    • 1970-01-01
    • 1970-01-01
    • 2013-06-27
    • 2012-07-08
    • 2016-10-12
    • 2010-12-14
    • 1970-01-01
    相关资源
    最近更新 更多