Convertir una imagen a una imagen ASCII en Python

Introducción al arte ASCII 

El arte ASCII es una técnica de diseño gráfico que utiliza computadoras para la presentación y consiste en imágenes ensambladas a partir de los 95 caracteres imprimibles (de un total de 128) definidos por el estándar ASCII de 1963 y conjuntos de caracteres compatibles con ASCII con caracteres extendidos patentados (más allá de los 128 caracteres de ASCII estándar de 7 bits). El término también se usa libremente para referirse al arte visual basado en texto en general. El arte ASCII se puede crear con cualquier editor de texto y, a menudo, se usa con lenguajes de forma libre. La mayoría de los ejemplos de arte ASCII requieren una fuente de ancho fijo (fuentes no proporcionales, como en una máquina de escribir tradicional) como Courier para la presentación. Entre los ejemplos más antiguos conocidos de arte ASCII se encuentran las creaciones del pionero del arte por computadora Kenneth Knowlton de alrededor de 1966, que trabajaba para Bell Labs en ese momento. “Studies in Perception I” de Ken Knowlton y Leon Harmon de 1966 muestra algunos ejemplos de su arte ASCII temprano. El arte ASCII se inventó, en gran parte, porque los primeros impresores a menudo carecían de capacidad gráfica y, por lo tanto, se usaban caracteres en lugar de marcas gráficas. Además, para marcar divisiones entre diferentes trabajos de impresión de diferentes usuarios, las impresoras a granel a menudo usaban arte ASCII para imprimir pancartas grandes, lo que hacía que la división fuera más fácil de detectar para que un operador de computadora o un empleado pudiera separar más fácilmente los resultados. El arte ASCII también se usó en los primeros correos electrónicos cuando las imágenes no se podían incrustar. Puedes encontrar más sobre ellos. [Fuente : Para marcar divisiones entre diferentes trabajos de impresión de diferentes usuarios, las impresoras a granel a menudo usaban arte ASCII para imprimir pancartas grandes, lo que hacía que la división fuera más fácil de detectar para que un operador de computadora o un empleado pudiera separar más fácilmente los resultados. El arte ASCII también se usó en los primeros correos electrónicos cuando las imágenes no se podían incrustar. Puedes encontrar más sobre ellos. [Fuente : Para marcar divisiones entre diferentes trabajos de impresión de diferentes usuarios, las impresoras a granel a menudo usaban arte ASCII para imprimir pancartas grandes, lo que hacía que la división fuera más fácil de detectar para que un operador de computadora o un empleado pudiera separar más fácilmente los resultados. El arte ASCII también se usó en los primeros correos electrónicos cuando las imágenes no se podían incrustar. Puedes encontrar más sobre ellos. [Fuente :wiki .

Cómo funciona: 

Estos son los pasos que toma el programa para generar la 
imagen ASCII: 

  • Convierta la imagen de entrada a escala de grises.
  • Divide la imagen en mosaicos M×N.
  • Corrija M (el número de filas) para que coincida con la relación de aspecto de la imagen y la fuente.
  • Calcule el brillo promedio para cada mosaico de imagen y luego busque un carácter ASCII adecuado para cada uno.
  • Reúna filas de strings de caracteres ASCII e imprímalas en un archivo para formar la imagen final.

Requisitos  

Hará este programa en python y usaremos Pillow , que es la biblioteca de imágenes de Python para leer las imágenes, acceder a sus datos subyacentes y crearlos y modificarlos, y también el módulo científico Numpy para calcular los promedios.

El código 
Comenzará definiendo los niveles de escala de grises utilizados para generar el arte ASCII. Luego verá cómo se divide la imagen en mosaicos y cómo se calcula el brillo promedio para esos mosaicos. A continuación, trabajará para reemplazar los mosaicos con caracteres ASCII para generar el resultado final. Finalmente, configurará el análisis de línea de comando para el programa para permitir que un usuario especifique el tamaño de salida, el nombre de archivo de salida, etc.
 

Definición de los niveles de escala de grises y la cuadrícula  

Como primer paso para crear su programa, defina los dos niveles de escala de grises utilizados para convertir valores de brillo a caracteres ASCII como valores globales. 

>>>gscale1 = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~i!lI;:,\"^`". "    #70 levels of gray
>>>gscale2 = "@%#*+=-:. "         #10 levels of gray

El valor gscale1 en u es la rampa de escala de grises de 70 niveles, y gscale2 en v es la rampa de escala de grises de 10 niveles más simple. Ambos valores se almacenan como strings, con un rango de caracteres que progresan desde el más oscuro hasta el más claro.
Ahora que tiene sus rampas en escala de grises, puede configurar la imagen. El siguiente código abre la imagen y la divide en una cuadrícula:
 

    # open image and convert to grayscale
>>>    image = Image.open(fileName).convert('L')
    # store dimensions
>>>    W, H = image.size[0], image.size[1]
    # compute width of tile
>>>    w = W/cols
    # compute tile height based on aspect ratio and scale
>>>    h = w/scale
    # compute number of rows
>>>    rows = int(H/h)

Cálculo del brillo promedio 
A continuación, calcula el brillo promedio de un mosaico en la imagen en escala de grises. La función getAverageL() hace el trabajo.
 

#Given PIL Image, return average value of grayscale value
>>>def getAverageL(image):
    # get image as numpy array
...    im = np.array(image)
    # get shape
...    w,h = im.shape
    # get average
...    return np.average(im.reshape(w*h))

Primero, el mosaico de imagen se pasa como un objeto de imagen PIL. Convierta la imagen en una array numpy en el segundo paso, en cuyo punto ‘im’ se convierte en una array bidimensional de brillo para cada píxel. En el tercer paso, almacena las dimensiones (ancho y alto) de la imagen. En el cuarto paso, numpy.average() calcula el promedio de los valores de brillo en la imagen usando numpy.reshape() para convertir primero la array bidimensional de las dimensiones ancho y alto (ancho, alto) en una plana Array dimensional cuya longitud es el producto del ancho por la altura (ancho*alto). La llamada numpy.average() luego suma estos valores de array y calcula el promedio.
 

Generación del contenido ASCII a partir de la imagen 

    # ascii image is a list of character strings
>>>    aimg = []
    # generate list of dimensions
>>>    for j in range(rows):
...        y1 = int(j*h)
...        y2 = int((j+1)*h)
        # correct last tile
...        if j == rows-1:
...            y2 = H
        # append an empty string
...        aimg.append("")
...        for i in range(cols):
            # crop image to tile
...            x1 = int(i*w)
...            x2 = int((i+1)*w)
            # correct last tile
...            if i == cols-1:
...                x2 = W
            # crop image to extract tile
...            img = image.crop((x1, y1, x2, y2))
            # get average luminance
...            avg = int(getAverageL(img))
            # look up ascii char
...            if moreLevels:
...                gsval = gscale1[int((avg*69)/255)]
...            else:
...                gsval = gscale2[int((avg*9)/255)]
            # append ascii char to string
...            aimg[j] += gsval

En esta sección del programa, la imagen ASCII se almacena primero como una lista de strings, que se inicializa en el primer paso. A continuación, itera a través del número calculado de mosaicos de imagen de fila y, en el segundo paso y en la línea siguiente, calcula las coordenadas y iniciales y finales de cada mosaico de imagen. Aunque estos son cálculos de punto flotante, trunquelos a números enteros antes de pasarlos a un método de recorte de imágenes. A continuación, debido a que dividir la imagen en mosaicos crea mosaicos de borde del mismo tamaño solo cuando el ancho de la imagen es un múltiplo entero del número de columnas, corrija la coordenada y de los mosaicos en la última fila configurando la coordenada y en la altura real de la imagen. Al hacerlo, se asegura de que el borde superior de la imagen no se trunque. En el tercer paso, agrega una string vacía en el ASCII como una forma compacta de representar la fila de la imagen actual. A continuación, rellenará esta string. (Usted trata la string como una lista de caracteres). En el cuarto paso y en la línea siguiente, calcula las coordenadas x izquierda y derecha de cada mosaico, y en el quinto paso, corrige la coordenada x para el último mosaico para el mismas razones por las que corrigió la coordenada y. Use image.crop() en el sexto paso para extraer el mosaico de la imagen y luego pase ese mosaico a la función getAverageL() definida anteriormente, reduzca el valor de brillo promedio de [0, 255] a [0, 9] (el rango de valores para la rampa de escala de grises de 10 niveles predeterminada). Luego usa gscale2 (la string de rampa almacenada) como una tabla de búsqueda para ASCII Art 95 el valor ASCII relevante. La línea en el paso ocho es similar, excepto que se usa solo cuando la bandera de la línea de comando está configurada para usar la rampa con 70 niveles. Finalmente, agrega el valor ASCII buscado, gsval, a la fila de texto en el último paso, y el código se repite hasta que se procesan todas las filas.
Adición de la interfaz de línea de comandos y escritura de strings de arte ASCII en un archivo de texto
Para agregar una interfaz de línea de comandos, use el módulo integrado de Python argparse
Y ahora, finalmente, tome la lista generada de strings de caracteres ASCII y escríbalas en un archivo de texto.
 

# open a new text file
>>> f = open(outFile, 'w')
# write each string in the list to the new file
>>> for row in aimg:
...    f.write(row + '\n')
# clean up
>>> f.close()

Luego agregue estas partes para crear su programa. El código completo se da a continuación.
 

Python

# Python code to convert an image to ASCII image.
import sys, random, argparse
import numpy as np
import math
 
from PIL import Image
 
# gray scale level values from:
# http://paulbourke.net/dataformats/asciiart/
 
# 70 levels of gray
gscale1 = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. "
 
# 10 levels of gray
gscale2 = '@%#*+=-:. '
 
def getAverageL(image):
 
    """
    Given PIL Image, return average value of grayscale value
    """
    # get image as numpy array
    im = np.array(image)
 
    # get shape
    w,h = im.shape
 
    # get average
    return np.average(im.reshape(w*h))
 
def covertImageToAscii(fileName, cols, scale, moreLevels):
    """
    Given Image and dims (rows, cols) returns an m*n list of Images
    """
    # declare globals
    global gscale1, gscale2
 
    # open image and convert to grayscale
    image = Image.open(fileName).convert('L')
 
    # store dimensions
    W, H = image.size[0], image.size[1]
    print("input image dims: %d x %d" % (W, H))
 
    # compute width of tile
    w = W/cols
 
    # compute tile height based on aspect ratio and scale
    h = w/scale
 
    # compute number of rows
    rows = int(H/h)
     
    print("cols: %d, rows: %d" % (cols, rows))
    print("tile dims: %d x %d" % (w, h))
 
    # check if image size is too small
    if cols > W or rows > H:
        print("Image too small for specified cols!")
        exit(0)
 
    # ascii image is a list of character strings
    aimg = []
    # generate list of dimensions
    for j in range(rows):
        y1 = int(j*h)
        y2 = int((j+1)*h)
 
        # correct last tile
        if j == rows-1:
            y2 = H
 
        # append an empty string
        aimg.append("")
 
        for i in range(cols):
 
            # crop image to tile
            x1 = int(i*w)
            x2 = int((i+1)*w)
 
            # correct last tile
            if i == cols-1:
                x2 = W
 
            # crop image to extract tile
            img = image.crop((x1, y1, x2, y2))
 
            # get average luminance
            avg = int(getAverageL(img))
 
            # look up ascii char
            if moreLevels:
                gsval = gscale1[int((avg*69)/255)]
            else:
                gsval = gscale2[int((avg*9)/255)]
 
            # append ascii char to string
            aimg[j] += gsval
     
    # return txt image
    return aimg
 
# main() function
def main():
    # create parser
    descStr = "This program converts an image into ASCII art."
    parser = argparse.ArgumentParser(description=descStr)
    # add expected arguments
    parser.add_argument('--file', dest='imgFile', required=True)
    parser.add_argument('--scale', dest='scale', required=False)
    parser.add_argument('--out', dest='outFile', required=False)
    parser.add_argument('--cols', dest='cols', required=False)
    parser.add_argument('--morelevels',dest='moreLevels',action='store_true')
 
    # parse args
    args = parser.parse_args()
   
    imgFile = args.imgFile
 
    # set output file
    outFile = 'out.txt'
    if args.outFile:
        outFile = args.outFile
 
    # set scale default as 0.43 which suits
    # a Courier font
    scale = 0.43
    if args.scale:
        scale = float(args.scale)
 
    # set cols
    cols = 80
    if args.cols:
        cols = int(args.cols)
 
    print('generating ASCII art...')
    # convert image to ascii txt
    aimg = covertImageToAscii(imgFile, cols, scale, args.moreLevels)
 
    # open file
    f = open(outFile, 'w')
 
    # write to file
    for row in aimg:
        f.write(row + '\n')
 
    # cleanup
    f.close()
    print("ASCII art written to %s" % outFile)
 
# call main
if __name__ == '__main__':
    main()

Aporte: 
 

$python "ASCII_IMAGE_GENERATOR.py" --file data/11.jpg --cols 120

Recursos
1. Wikipedia: ASCII_ART 
2. Python Playground: Geeky Projects for the Curious Programmer por Mahesh Venkitachalam. 
3. Valores de nivel de escala de grises 
4. Código de Github para este artículo  Subhajit Saha
contribuye con este artículo . Si te gusta GeeksforGeeks y te gustaría contribuir, también puedes escribir un artículo usando write.geeksforgeeks.org o enviar tu artículo por correo a review-team@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks. Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente. 

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *