raw data(バイナリ列や数値列)を画像として表示する

Pythonでraw data(バイナリ列や数値列)を画像として表示する. 今まではOpenCVを用いていたが, インストールされていない環境も多そうなので, 今回はPIL(Python Imaging Library)とPyGTKを用いた.

PILのみ用いる場合

数値のリストからnumpyのarrayを経由してPILのImage型を作成した.
arrayとImageの相互変換についてはNumPyのarrayとPILの変換を読めば詳しい.
とりあえずソースコード.

import Image
import numpy as np

data = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,17,17,17,17,81,180,180,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,139,253,253,253,253,253,253,253,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,228,253,253,253,253,253,253,253,207,197,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,213,253,253,253,253,253,253,253,253,253,253,223,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,231,253,253,253,108,40,40,115,244,253,253,134,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,114,114,114,37,0,0,0,205,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,95,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,205,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,61,99,96,0,0,45,224,253,253,195,10,0,0,0,0,0,0,0,0,0,0,0,11,25,105,83,189,189,228,253,251,189,189,218,253,253,210,27,0,0,0,0,0,0,0,0,0,0,42,116,173,253,253,253,253,253,253,253,253,253,253,253,253,253,221,116,7,0,0,0,0,0,0,0,0,0,118,253,253,253,253,245,212,222,253,253,253,253,253,253,253,253,253,253,160,15,0,0,0,0,0,0,0,0,254,253,253,253,189,99,0,32,202,253,253,253,240,122,122,190,253,253,253,174,0,0,0,0,0,0,0,0,255,253,253,253,238,222,222,222,241,253,253,230,70,0,0,17,175,229,253,253,0,0,0,0,0,0,0,0,158,253,253,253,253,253,253,253,253,205,106,65,0,0,0,0,0,62,244,157,0,0,0,0,0,0,0,0,6,26,179,179,179,179,179,30,15,10,0,0,0,0,0,0,0,0,14,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]

#文字列から作成する場合
#string_data = "".join([chr(c) for c in data])
#data = np.fromstring(string_data, dtype=np.uint8)
data = np.array(data, dtype=np.uint8)
data = data.reshape(28,28)
image = Image.fromarray(data)
image.show(command='display -quiet')

今回はグレースケール画像を扱っている. 注意点としては最後のshow関数のcommand引数だろう. これは画像をどのコマンドで表示するかを与えている. Linux環境だとデフォルトはxvを想定しているらしく, それがインストールされていないとshow関数は動作しない. 今, commandにdisplayコマンドを指定しているがdisplayコマンドをインストールしないとこれも動かない. このことはPILのshow()に詳しい.

文字列から作成する場合はnumpy.fromstring関数を使うようにコメントを残している. ただ, Imageクラスの方にfromstring関数があるのでそちらを使うべきだろう. バイナリ列のフォーマットは見ての通り, ただ各画素の値を並べているだけである.

カラー画像を数値列から作成するにはNumPyのarrayとPILの変換を読むに, おそらく3次元arrayを用いて一番上の次元をR,G,Bに対応させるのだろうが未検証.

PyGTKとの連携

もうちょっと豊かなインターフェイスで使いたいよねということで生データをPyGTKの画像として表示する方法を記載する.
今度は 数値列 -> array -> PILのImage -> str -> PixBuf -> GTKのImage と面倒な変換順序を辿っている. 一度, PILのImageを経由しているのは, PixBufがカラー画像のバイナリしか受け付けないためである. たぶん, もっとマシなやり方がある.

#!/usr/bin/python

import csv
import numpy as np
import Image

import pygtk
pygtk.require("2.0")
import gtk

src = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,17,17,17,17,81,180,180,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,139,253,253,253,253,253,253,253,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,228,253,253,253,253,253,253,253,207,197,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,213,253,253,253,253,253,253,253,253,253,253,223,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,231,253,253,253,108,40,40,115,244,253,253,134,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,114,114,114,37,0,0,0,205,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,95,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,205,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,61,99,96,0,0,45,224,253,253,195,10,0,0,0,0,0,0,0,0,0,0,0,11,25,105,83,189,189,228,253,251,189,189,218,253,253,210,27,0,0,0,0,0,0,0,0,0,0,42,116,173,253,253,253,253,253,253,253,253,253,253,253,253,253,221,116,7,0,0,0,0,0,0,0,0,0,118,253,253,253,253,245,212,222,253,253,253,253,253,253,253,253,253,253,160,15,0,0,0,0,0,0,0,0,254,253,253,253,189,99,0,32,202,253,253,253,240,122,122,190,253,253,253,174,0,0,0,0,0,0,0,0,255,253,253,253,238,222,222,222,241,253,253,230,70,0,0,17,175,229,253,253,0,0,0,0,0,0,0,0,158,253,253,253,253,253,253,253,253,205,106,65,0,0,0,0,0,62,244,157,0,0,0,0,0,0,0,0,6,26,179,179,179,179,179,30,15,10,0,0,0,0,0,0,0,0,14,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]


class ImageWindow:
    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.connect("delete_event", self.delete_event)
        
        data = np.array(src, dtype=np.uint8)
        data = data.reshape(28,28)
        image = Image.fromarray(data)
        image = image.convert("RGB")

        buf = gtk.gdk.pixbuf_new_from_data(image.tostring(),
                                           colorspace=gtk.gdk.COLORSPACE_RGB,
                                           has_alpha=False,
                                           bits_per_sample=8,
                                           width=28,
                                           height=28,
                                           rowstride=84)
        self.window.add(gtk.image_new_from_pixbuf(buf))
        self.window.show_all()

    def delete_event(self, widget, event, data=None):
        gtk.main_quit()
        return False

    
if __name__ == "__main__":
    window = ImageWindow()
    gtk.main()

PyGTKの使い方は他で調べてもらうとして変換についてのみ書く.
重要なのはpixbuf_new_from_dataがcolorspace引数にCOLORSPACE_RGBしか許さないため, 色空間を変換しなければならない点である. そのためPILのImage上で一度色空間の変換を行なっている. PILのImageに関する操作はこちらによくまとまっている. rowstride引数には画像の行と行の間のバイト数を指定する必要がある(画像処理ではお馴染み). ここでは3バイト * 28画素なので84としている.