PythonでアスキーアートのGIFアニメーションを作る


![サンプル](https://49.media.tumblr.com/4a2238c9b81e2a331e6d4b2ca35306a0/tumblr_nzjgtpRlI31s0c5hro1_1280.gif)

特に意味なく、複数の画像からアスキーアートのGIFアニメーションを作るPythonを書いたんで、使い道も特に無いんでここに書いとく。

上のサンプル画像は、頭が回るように８枚の写真を使って、「polikej#」の文字を使って生成しました。
タイル状に並べる画像の数、フォントの色・サイズなどを、スクリプト冒頭の大文字変数で指定してます。
（サンプル画像は、下のコードを少しいじって作ってます。）

フォントのパスは、OSXだと`Font Book`とかで、等幅フォントを調べて、右クリックでFinder表示で分かる。

アスキーアート化に[Pillow](http://pillow.readthedocs.org/en/3.0.x/index.html)、GIFアニ生成に[ImageMagick](http://www.imagemagick.org/script/index.php)が必要です。
Mac使ってれんで、pipとbrewで、それぞれ、インストールしてます。

```py
from PIL import Image, ImageDraw, ImageFont
import os.path
import os
import commands


FONT_SIZE = 12
GRID_SIZE = (3, 2)
FONT_COLOR_SET = ("#ffffff", "#000000")
FONT_PATH = 'フォント（等幅）のパス'
IMAGE_NAMES = [
    'f0.png',
    'f1.png',
    'f2.png',
    'f3.png',
    'f4.png',
    'f5.png',
    'f6.png',
    'f7.png',
]

FONT_COLOR, FONT_BACKGROUND_COLOR = FONT_COLOR_SET
COLUMNS, ROWS = GRID_SIZE


def image2ascii(input_image):

    original_width, original_height = input_image.size

    width = original_width * COLUMNS
    height = original_height * ROWS

    character, line = "", []
    font = ImageFont.truetype(FONT_PATH, FONT_SIZE, encoding="utf-8")
    input_pix = input_image.load()
    output_image = Image.new("RGBA", (width, height), FONT_BACKGROUND_COLOR)
    draw = ImageDraw.Draw(output_image)

    font_width, font_height = font.getsize("#")

    margin_width = width % font_width
    margin_height = height % font_height

    offset_x = int(round(margin_width / 2))
    offset_y = int(round(margin_height / 2))

    for row in range(ROWS):
        for y in range(offset_y, original_height - offset_y, font_height):
            line = []
            for column in range(COLUMNS):
                for x in range(offset_x, original_width - offset_x, font_width):
                    r, g, b, _ = input_pix[x - offset_x, y - offset_y]
                    gray = r * 0.2126 + g * 0.7152 + b * 0.0722
                    "polikeiji"
                    if gray > 130:
                        character = " "
                    elif gray > 100:
                        character = "i"
                    elif gray > 90:
                        character = "l"
                    elif gray > 80:
                        character = "j"
                    elif gray > 60:
                        character = "o"
                    elif gray > 50:
                        character = "e"
                    elif gray > 40:
                        character = "p"
                    elif gray > 30:
                        character = "k"
                    else:
                        character = "#"
                    line.append(character)
            draw.text((offset_x, y + row * original_height), "".join(line), font = font, fill = FONT_COLOR)
    return output_image

if __name__ == "__main__":
    directory_name = os.path.dirname(IMAGE_NAMES[0])
    if directory_name != '':
        directory_name = directory_name + "/"
    ascii_image_directory = "{0}ascii_{1}_{2}x{3}_{4}_{5}".format(
        directory_name, FONT_SIZE, ROWS, COLUMNS, FONT_COLOR, FONT_BACKGROUND_COLOR)

    for image_name in IMAGE_NAMES:
        print "Input image: {0}".format(image_name)

        with Image.open(image_name) as input_image:
            output_image = image2ascii(input_image)
         
            file_name, extension = os.path.splitext(os.path.basename(image_name))
    
            ascii_image_name = "{0}/ascii_{1}_{2}x{3}_{4}{5}".format(
                ascii_image_directory,
                FONT_SIZE, ROWS, COLUMNS, file_name, extension)

            ascii_image_directory = os.path.dirname(ascii_image_name)
            if not os.path.exists(ascii_image_directory):
                os.makedirs(ascii_image_directory)
    
            output_image.save(ascii_image_name)

            print "Output image: {0}".format(ascii_image_name)

    make_gif_animation_command = "convert -delay 10 -loop 0 {0}/*.png {0}/{1}.gif".format(
        ascii_image_directory, os.path.basename(ascii_image_directory))

    print commands.getoutput(make_gif_animation_command)
```


# Pillowでアスキーアート生成

コードも含めて[こちら](http://d.hatena.ne.jp/xef/20120920/p1)を参考にしてます。
画像をピクセル単位で調べて、輝度に応じて文字を割り当てる感じ。

フォントによって、1文字のサイズが違うんで、事前に1文字のサイズを調べて、アスキーアートが中央寄せになるように調整してる。

あとで、ワイルドカードで画像を指定して、ImageMagickでGIFアニを作ってるんで、アスキーアート化した画像は一個のフォルダにまとめて保存してる。


# ImageMagickでGifアニ生成

単にconvertコマンドで生成。

```bash
convert -delay 10 -loop 0 {フォルダ}/*.png {フォルダ}/output.gif
```

delayオプションでなんとなくスピードは変わるけど、ビューワーによって速度が違うっぽく、厳密にFPSとかは指定できないっぽい。

PythonでGIFアニ作れないのかな、と思ったけど、ライブラリが無い or 面倒臭そうだった。
[stackoverflow](http://stackoverflow.com/questions/753190/programmatically-generate-video-or-animated-gif-in-python)。