Terasannのチラ裏

「それはTeraさんに聞け」を目指すブログ

PythonでコマンドラインにGIFを表示させる(Python&乃木坂)

この前こんなのを書いた

コマンドラインで画像を表示させる(Python&乃木坂46) - Terasannのチラ裏

コンソール画面に画像を表示できるようになった!

ってことでこんどはGIFを表示できないか考えてみる。

で、実装してみた。

するとこんな感じで表示される。あらまぁ便利

f:id:kch-bone:20150222020026g:plain

読み込んだのはこれ 乃木坂46西野七瀬ちゃん

なーちゃんgif

かわいい。

アニメーションであるのでできればファイルダウンロードのプログレスバーのように標準出力を上書きしていきたいところ。

プログレスバーとかは標準出力のCR(キャリッジリターン)っていう先頭を指すポインタみたいのを使っているらしい。ってかキャリッジリターンていうの!? ちなみにLFはラインフィード!

pythonで実装した例が検索したら、ちらほら発見できた。

しかし複数行で行ってるという記事は見当たらなかった。

参考サイトからまずカウントダウンのコードを入手。

参考 : コンソールへの出力を上書きしてゆく方法

うむ。いい感じに動いている。

つづいて改行コードを忍ばせる。

失敗。

どうやら改行コードが入ってしまうとダメ。

CRで戻れるのはその行の先頭らしい。

これはスペースをコンソール目一杯に埋めて改行っぽくしてもダメだった。(試してみた)

どうしよう

ってことでclearコマンドを使うことにした。

python

   os.system("clear")

こんな感じでコンソールをクリアできる。 クリアって言っても消すわけではないみたい。 消したわけではないのでスクロールすると見えてきます笑

つづいてgifの見れるコードを。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
created by keiichi 
"""

from PIL import Image
from PIL import ImageOps
import os

import sys, time

def ascii(pmatrix, sx, sy, columns):

    tab = ""
    tablen = 0

    if columns <= sx:
        sx = columns
        columns = 0
    else:
        tablen = columns - sx*2
        for i in xrange(tablen):
            tab = tab + " "

    line = ""
    for y in xrange(sy):
        sentence = ""
        for x in xrange(sx*2):

            gray = pmatrix.getpixel((x,y))

            """            
            if gray > 250:
                character = " "
            elif gray > 230:
                character = "."
            elif gray > 200:
                character = ":"
            elif gray > 175:
                character = "+"
            elif gray > 150:
                character = "*"
            elif gray > 125:
                character = "#"
            elif gray > 50:
                character = "@"
            
           
            if gray > 250:
                character = " "
            elif gray > 230:
                character = "."
            elif gray > 210:
                character = ":"
            elif gray > 190:
                character = "|"
            elif gray > 170:
                character = "/"
            elif gray > 150:
                character = "+"
            elif gray > 130:
                character = "*"
            elif gray > 110:
                character = "¥"
            elif gray > 90:
                character = "&"
            elif gray > 70:
                character = "$"
            elif gray > 50:
                character = "@"
            """

            if gray > 250:
                character = "@"
            elif gray > 230:
                character = "$"
            elif gray > 210:
                character = "&"
            elif gray > 190:
                character = "¥"
            elif gray > 170:
                character = "*"
            elif gray > 150:
                character = "+"
            elif gray > 130:
                character = "/"
            elif gray > 110:
                character = "|"
            elif gray > 90:
                character = ":"
            elif gray > 70:
                character = "."
            elif gray > 50:
                character = " "


            sentence = sentence + character
        line = line + sentence + tab
        
    return line


if __name__ == '__main__':

    try:


        param = sys.argv

        filename = 'logo.gif'

        if param[1] != None:
            
            filename = param[1]
            
        try:
            src = Image.open(filename)
            
        except IOError:
            print "Cant load", filename
            sys.exit(1)

        rows, columns = os.popen('stty size', 'r').read().split()

        size = 60
        sx =  src.size[0]
        sy =  src.size[1]
    
        if param[2] != None:
            size  = int(param[2])

        gif = []

        if (src.format == 'GIF'):
            try:
                while 1:
                    
                    try :
                            image = src.resize((size*2, size), Image.ANTIALIAS)
                            output_image = ImageOps.grayscale(image)
                            gif.append(ascii(output_image, int(size) , int(size), int(columns)))
    
                    except ValueError,UnboundLocalError:
                        pass
                    src.seek(src.tell()+1)
                    # 各フレーム毎の処理をする
    
            except EOFError:
                pass

            print len(gif)
            count = 2
            for i in xrange(count):
                for tgif in gif:
                    #sys.stdout.write("\r"+tgif)
                    os.system("clear")
                    sys.stdout.write('\r%s' % str(tgif))
                    sys.stdout.flush()
                    time.sleep(0.1)
        else:

            image = src.resize((size*2, size), Image.ANTIALIAS)
            #image.show()
            output_image = ImageOps.grayscale(image)
            print ascii(output_image, int(size), int(size), int(columns))
        

    except Exception as e:
        print '=== エラー内容 ==='
        print 'type:' + str(type(e))
        print 'args:' + str(e.args)
        print 'message:' + e.message
        print 'e自身:' + str(e)

PILにはGIFを分解できる関数があるのでそれを使いました!

参考 : Pillow/PIL/GifImagePlugin.py

ところがぎっちょん!あまりに高速なgifだとうまく抽出できなかった! こういうGIFたち↓

mario

なんでかなーっておもってちょっと調べて見たんだけど、GIFってframeの速度の設定があるらしい。そりゃそうだ。

参考 : あなたは大丈夫?高速GIFアニメになってしまう症

Pillow/PIL/GifImagePlugin.pyの110行目のところで

if frame != self.__frame + 1:
        raise ValueError("cannot seek to frame %d" % frame)

が起こっているっぽい。よくわかんないけど! ついでにPILが今現在対応しているGIFはGIF87 and GIF89ってことなのでそれ以外のGIFは再生できない。

GIFってこんなに種類のあるものなのね。

ちなみにPNGが登場したことで静止画用途でのGIFはほぼ消滅したそうな。

なかなか勉強になりました!

Gifが表示できるようになったからTerminalの起動もかっこよくなるできるかも!

目指すはアイアンマン2のディスプレイにハッキングをするシーン 58秒辺りから!


Iron Man 2 Hacking Scene - YouTube

Arch Linuxだとこんなかんじの表示だよね。いいなぁ〜。

作ってみようかな〜なんて笑

今日も良いPythonライフを!