blog

リア向け。

ssh接続先で作った図をターミナル上に表示

困っていること。

皆さんsshでサーバとかに入って、データの解析して
図を作って、その図を見るのにどうしてるでしょうか?
自分のPCにscpとかでダウンロードしますか?
その転送作業、面倒くさくありませんか?

端末。sshクライアントを変えよう。

通常、Windowsからのssh接続では
TeraTerm, Putty, Poderosa等を使います。この辺のsshクライアントがメジャーです。
最近私が使い始めたのはRLoginというクライアントですね。
http://nanno.dip.jp/softlib/man/rlogin/
タブ型クライアントとしてはPoderosaがありましたが、もう開発が止まっていて
乗り換えたいと考えてました。そこでRLoginです。詳しい説明はリンク先で。

で、どうやって図を表示させるの?

このRLoginですが、さらさらっと機能一覧を見ていると、なんと
Sixel Graphicsに対応しているではありませんか。
Sixelというのはターミナル上で画像とかを表示させるための規格です。
つまり画像をターミナル上に表示させることができるのです!

例えばmadoka.jpgとかいうファイルがあれば

$ jpegtopnm madoka.jpg| pnmquant 256 | ppmtosixel

と一度pnm/ppm形式に変換して、さらにsixelへ変換させれば
ターミナル上に画像が表示されるわけです。
簡単ですね。これだけです。
ssh接続先のサーバでグラフの図をjpgでもpngでもいいのでファイルを作ってやれば
jpegtopnmとかpngtopnmで図が出せるというわけです。

応用編

私は普段PythonでデータをゴニョゴニョしてPythonのmatplotlibでそのまま図を作ってしまいます。もちろんmatplotlibはpng/eps/pdfとか主要な形式はサポートしているので、ファイルに出力させてしまえばOKです。

が、すべてファイルとして保存しておきたいわけでもないので、Pythonスクリプトから直接sixelを吐けないかと考えるわけです。もちろんできます。

Pysixelというライブラリが公開されているようなので、これを使わさせて頂きます。
https://pypi.python.org/pypi/PySixel
インストール方法はリンク先に載ってるので省略。
PythonでのCode Exampleもリンク先に載ってるので使い方はすぐにわかりますね。
特に難しくもないのでライブラリの紹介だけですべてが終わってしまいそうです。
それではこれを書いてる意味がほとんどなくなるのでmatplotlibと連携する方法を書きましょう。そうしましょう。

まず、Windowとかは表示させないので、matplotlibのbackendをAggに変えてしまいます。

import matplotlib
matplotlib.rcParams['backend'] = "Agg"

そして何らかの図をplotします。

import pylab as pl
import numpy as np
x = np.linspace(0,2*np.pi)
y = np.sin(x)
pl.plot(x,y)

次がポイントです。グラフの図を保存するときはsavefigでOKなのですが、今回は図を保存せずに直接sixel形式として標準出力に出します。
Pysixelではファイルっぽいものを読んで表示させるので、そのファイルっぽいものを作ってそこにデータを流します。

from StringIO import StringIO
buf = StringIO()  # ファイルっぽいもの
pl.savefig(buf)   # bufに保存。ファイルじゃないよ!
buf.seek(0)       # ここポイント。無いとダメ。

seek(0)しないと図の形式がわかりませんとかいうエラーがでます。
あとはPysixelで表示させるだけですよ。簡単。

import sixel
writer = sixel.SixelWriter()
writer.draw(buf)

ただ問題として、このSixel Graphicsの表示機能とscreenを両立させることができないんですよね。。。
2013/11/20追記
kefir_/saitohaさんから解決方法を教えてもらいました。ありがたや。
> https://gist.github.com/saitoha/7546579 今週末にでも改良版を載せられたらいいのですが。。


使い方をまとめた全ソース貼っときます。

#!/usr/bin/env python2
#vim:fileencoding=utf8
import matplotlib
matplotlib.rcParams['backend'] = "Agg"
 
from StringIO import StringIO
import pylab as pl
import numpy as np
import sixel
 
def main():
    pl.figure(figsize=(6,4))
    x = np.linspace(0,2*np.pi)
    y = np.sin(x)
    pl.plot(x,y)
 
    buf = StringIO() # ファイルっぽいもの
    pl.savefig(buf) # bufに保存。ファイルじゃないよ!
    buf.seek(0) # ここポイント。無いとダメ。
 
    writer = sixel.SixelWriter()
    writer.draw(buf)
 
if __name__=="__main__":
    main()

スクリーンショット
f:id:nasing-i:20131114234956p:plain