2012年4月5日木曜日

グラフの軸の色だけを変更する Python スクリプト

お久しぶりです。目標を掲げて毎日着実に進もうという決意とともに新年度を迎えております。とりあえず TOEIC 800 点取るのと、C++ への習熟度を高めることを研究以外のがんばりどころとしたいです。

さて突然ですが、研究の成果などをグラフにして表示するとき、軸の色を黒から白に変更したいと思ったことはないでしょうか?僕は研究室のミーティングで専ら黒背景のスライドを使ってトークするのですが、こういうときは図の背景を透明にして軸や凡例文字を白くし、グラフ自体は何か意味のある色を使って表示するのが素敵だとずっと思ってきました。このエントリではこれを叶えてくれる Python スクリプトの説明をします。

右図は gnuplot で適当に出力した sin 関数で、元々は eps 形式だったのを convert -background none -density 300 sin.eps sin.png として png 形式にしたものです。-background オプションをつけることで背景を透明にしています。今やろうとしているのは、赤い線はそのままにした状態で軸と sin(x) という凡例の文字だけを白くすることです。これは PIL を使うと以下に示す通り簡単に出来ます。注意点は、PIL のバージョン 1.1.6 以上を使うこと。詳しくはこのページの load メソッドについてのコメントを見てください。
# b2w.py by hidehideta

from sys import argv
from os import wait
import Image

def black2white(ori):
    if ori.endswith(".eps"):
        import subprocess
        command = "convert -background none -density 300 " \
                  + ori + " " + ori[:-4] + ".png"
        subprocess.Popen(command.split()); wait()
        ori = ori[:-4] + ".png"

    UP = 20 # threshold value to judge "black" pixels
    im = Image.open(ori)
    h, w = im.size
    jm = Image.new(im.mode, im.size)

    # pixel access objects
    pi = im.load()
    pj = jm.load()

    for i in range(h):
        for j in range(w):
            p = pi[i, j]
            if p[0] < UP and p[1] < UP and p[2] < UP:
                pj[i, j] = (255, 255, 255, 255)
            else: pj[i, j] = p

    modified = ori[:-4] + "_b2w.png"
    jm.save(modified, format="png")

if __name__ == "__main__":
    if len(argv) != 2:
        print "Usage: python b2w.py < image file to convert >"
    else:
        ori = argv[1]
        black2white(ori)
ポイントは 21, 22 行目で、load() メソッドを呼ぶことで各ピクセルの値を取得、変更できるようにしています(図は RGBA モードで読み込まれているので各ピクセルごとに4つの要素を持つタプルが割り当てられている)。肝心の黒色は RGB で (0, 0, 0) に対応するので、UP で決まる値程度までは遊びを持たせて 27 行目で判別しています。以下、軸や凡例に対応すると思われるピクセルには白を表す (255, 255, 255, 255) を与え、他はオリジナルのピクセルをコピーして終了。最終的に右図が得られるはずです。入力する画像のフォーマットは PIL がサポートしているものなら何でもよいと思いますが、png 以外試してないので断言できません。なお、eps ファイルとか png ファイルがたくさんあるのを一括処理するには glob.glob() を使うとすごく楽です。

今回はかなり原始的な方法を使いましたが、Scipy や OpenCV を利用したらもっと簡単かつ複雑なことも出来るのかもしれません(もしかして ImageMagick だけで何とかなるのだろうか)。また何か分かったら書きます。

ちなみにこの記事が Syntax Highlighter 初挑戦だったんですけど、これ、どうだろう?幅の細かい調整とかどうやるのか分からなくて困る。。。(今回はこことかのおかげで何とか使えるようにはなりました)

0 件のコメント:

コメントを投稿