2012年7月26日木曜日

PyQtでグラフ 完了

PyQwtを使ってグラフを描く。今回はマウスでデータを動かすプログラムを作成。


マウスの座標を取得するにはQwtPlotのinvTransformを使えば良いことが分かった。
ただ、この関数にはキャンパス(QwtPlot.canvas())の座標を与えなければならないので注意。
僕はずっとQwtPlotのウィジットの座標を与えていたようで、少し値がずれていました。

イベントの取得方法はPyQwtのHPの例を参考にしました。
イベントフィルタについてはここを読むとわかる。
今回のようにクラス外部にフィルタを持つことで、柔軟にcanvas()でのイベントを取得できます。
(ただ原理をきちんと理解していないので冗長さはありますが…どうなんでしょう?)

クリックした座標に一番近いデータはQwt.PlotCurve.closestPointで取得できます。
あとはドラッグしている間にそのデータを書き換えるだけです。




ソースも載せときます。
とりあえずグラフでやりたいことはここまでなので、あとはQtでUIを作成していきます。

import sys

from PyQt4.QtGui import QApplication, QWidget, QHBoxLayout 
from PyQt4.Qt import Qt, QPen, QSize, QBrush, QObject, SIGNAL, QEvent
from PyQt4.Qwt5.Qwt import QwtPlot, QwtPlotGrid, QwtPlotCurve, QwtSymbol

import numpy as np
from numpy import pi

class Spy(QObject):
    
    def __init__(self, parent):
        QObject.__init__(self, parent)
        parent.setMouseTracking(True)
        parent.installEventFilter(self)

    # __init__()

    def eventFilter(self, _, event):
        if event.type() == QEvent.MouseMove:
            self.emit(SIGNAL("MouseMove"), event.pos())
        if event.type() == QEvent.MouseButtonPress:
            self.emit(SIGNAL("MouseButtonPress"), event.pos())
        if event.type() == QEvent.MouseButtonRelease:
            self.emit(SIGNAL("MouseButtonRelease"), event.pos())
        return False

    # eventFilter()

# class Spy

class PlotWidget(QwtPlot):
    def __init__(self, title, *args):
        QwtPlot.__init__(self, *args)
        self.setTitle(title)
        self.setCanvasBackground(Qt.white)
        self.setMouseTracking(True)
        
        #grid
        grid = QwtPlotGrid()
        grid.attach(self)
        grid.setPen(QPen(Qt.black, 0, Qt.DotLine)) #make grid dotted-line
        
        self.curve = None
        self.picked_id = None   #picked index by mouse
        self.pick_tol = 5.0     #picking tollerence        
        
        #connect signal and slot
        self.connect(Spy(self.canvas()), SIGNAL("MouseMove"),
                     self.on_move)
        self.connect(Spy(self.canvas()), SIGNAL("MouseButtonPress"),
                     self.on_click)
        self.connect(Spy(self.canvas()), SIGNAL("MouseButtonRelease"),
                     self.on_release)
    
    def on_move(self, pos):
        if self.picked_id is not None:
            #drag the picked data by mouse
            x = self.invTransform(QwtPlot.xBottom, pos.x())
            y = self.invTransform(QwtPlot.yLeft, pos.y())
            
            self.x[self.picked_id]=x
            self.y[self.picked_id]=y
            self.curve.setData(self.x, self.y)
            self.replot()
            
    def on_click(self, pos):
        if self.curve is not None:
            picked = self.curve.closestPoint(pos)
            
            if picked[1] > self.pick_tol:
                self.picked_id = None
            else:
                self.picked_id = picked[0]
                
    def on_release(self, pos):
        self.picked_id = None
                
    def setCurveData(self, title, x, y):
        self.curve = QwtPlotCurve(title)
        self.curve.setPen(QPen(Qt.black, 0))
        self.curve.setSymbol(QwtSymbol(QwtSymbol.Ellipse, QBrush(Qt.red), QPen(Qt.red), QSize(10,10)))    
        self.curve.setData(x,y)
        self.x = x
        self.y = y
        self.curve.attach(self)
                
class MultiPlotWidget(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.setWindowTitle("multiple graphs")
  
        hlayout = QHBoxLayout()
        self.setLayout(hlayout)
        
        #data creation
        x = np.linspace(-2*pi, 2*pi, 100)
        y1 = np.cos(x)

        #create plots
        self.plot1 = PlotWidget("plot1")
        self.plot1.setCurveData("y=cos(x)", x, y1)

        #add plots to the main widget
        self.layout().addWidget(self.plot1)
               
def main():
    app = QApplication(sys.argv)

    plot = MultiPlotWidget()
    plot.setMinimumSize(500,300)
    plot.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()


2012年7月25日水曜日

ヘビでもわかるライトフィールドカメラの原理 その1.1 ライトフィールド(light field)とは

ライトフィールドカメラの「ライトフィールド」とは「光のすべて」という意味である。
すこし大げさな言い方をすると
「ライトフィールドカメラ」=「光のすべて記録するカメラ」となる。


「光のすべて」とはなんなのか。
光の状態をどのように記述するかを考えてみると、
『ある時間』、『ある場所』で、『ある波長』の光が『ある強度』で『ある方向』へ伝搬している…
この状態を表す関数を考えると

時間=1次元
場所=3次元
方向=2次元
波長=1次元

となり、思いついた性質を並べただけで7次元の関数となる。
これら光のすべて記録しておけば、その状況を行ったり来たりすることができるのは想像できるだろうか?
実はそのいい例が映画「The Matrix」のあのシーン。
撮影風景をテレビでみたことある人もいると思うが、カメラを何台も並べてあらゆる方向から来る光を記録して、後から合成している。
http://goo.gl/HeJQk
つまりあれも一種のライトフィールドカメラということである。


これまでのカメラは、

時間→シャッターで一瞬をサンプリング
波長→カラーフィルターで分離

という具合で光のパラメータをCMOSの電荷情報に変換している。
残すは場所(3次元)と方向(2次元)の5次元であるが、実は光には輝度不変則というものがあるので、4次元まで減らすことができる。これは障害物にあたらなければ、光はエネルギーを失うことなく進む、みたいなもので、要するに情報を保存しているので、1つ次元減らしてもOK。ということである。


この4次元で表せられる光のことを論文とかでは”4D Light Field"なんていう。
4D Light Fieldをいかにして捕らえるかが今回説明したいライトフィールドカメラの原理の真髄なのだが、こんなに長文を書くのも久しぶりで疲れたのでまた今度。

あまり厳密な説明はするつもりがないので、
とりあえず今回はライトフィールドがどんなものかざっくりわかってくれたら嬉しいです。
次回は図も含めてライトフィールドとはなんぞやについて説きたい。




pyqwt入門その3 複数グラフの表示

pythonでグラフを描画その3は複数のグラフを描画させる。
これはQwtではなくQtの側の操作。

QwtPlotのオブジェクトを作ってそれをQWidgetにaddWidgetで加える。
それだけです。

データ同士を結ぶ線、左のグラフでは見えなくて、右では黒の線になっている。
実は左では線の色に白を指定して見難くしているだけ。
どうやって線自体を消すのだろう。。。


ソースも今後の参考に載せます。
import sys

from PyQt4.QtGui import QApplication, QWidget, QHBoxLayout 
from PyQt4.Qt import Qt, QPen, QSize, QBrush
from PyQt4.Qwt5.Qwt import QwtPlot, QwtPlotGrid, QwtPlotCurve, QwtSymbol

import numpy as np
from numpy import pi


class PlotWidget(QwtPlot):
    def __init__(self, title, *args):
        QwtPlot.__init__(self, *args)
        self.setTitle(title)
        self.setCanvasBackground(Qt.white)
        
        #grid
        grid = QwtPlotGrid()
        grid.attach(self)
        grid.setPen(QPen(Qt.black, 0, Qt.DotLine)) #make grid dotted-line
        
        self.replot()
        
class MultiPlotWidget(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.setWindowTitle("multiple graphs")
        
        hlayout = QHBoxLayout()
        self.setLayout(hlayout)
        
        #data creation
        x = np.linspace(-2*pi, 2*pi, 100)
        y1 = np.sin(x)
        y2 = np.cos(x)        
        
        #create plots
        plot1 = PlotWidget("plot1")
        curve1 = QwtPlotCurve('y=sin(x)')
        curve1.setPen(QPen(Qt.white, 0))
        curve1.setSymbol(QwtSymbol(QwtSymbol.Ellipse, QBrush(Qt.red), QPen(Qt.red), QSize(5,5)))       
        curve1.setData(x,y1)
        curve1.attach(plot1)
                
        plot2 = PlotWidget("plot2")
        curve2 = QwtPlotCurve('y=cos(x)')
        curve2.setPen(QPen(Qt.black, 0))
        curve2.setSymbol(QwtSymbol(QwtSymbol.Rect, QBrush(Qt.blue), QPen(Qt.blue), QSize(5,5)))       
        curve2.setData(x,y2)
        curve2.attach(plot2)
        
        #add plots to the main widget
        self.layout().addWidget(plot1)
        self.layout().addWidget(plot2)
        
        
        
def main():
    app = QApplication(sys.argv)

    plot = MultiPlotWidget()
    plot.setMinimumSize(300,200)
    plot.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

2012年7月24日火曜日

pythonでグラフ pyqwt入門その2

matplotlibはなんといってもグラフのクオリティが高いところがいいのだが、少々重たくGUIには向かん。そんなこんなでPyQwtに手を出しけど、Qwtをどれくらい使いこなせるかが鍵。
PyQwtの例はネットにあまりなく、Qwtをあたらないけん。
C++を使える人はいいかも知れんけど、知らん人はqwtのドキュメント読んでもなかなか分からんのよね~

少し苦労して、プロットの形と線を変える方法を発見したのでアップ。
curve.setPen(Qt.QPen(Qt.Qt.blue, 2, Qt.Qt.DotLine))
        curve.setSymbol(Qwt.QwtSymbol(Qwt.QwtSymbol.Ellipse, Qt.QBrush(Qt.Qt.red),
                                      Qt.QPen(Qt.Qt.red), Qt.QSize(5,5)))

結果は写真のようになる。 EllipseんとこをDiamondとかにしてもよい。形はここをみて好きなのを選ぶ


忘れんように全ソースもメモ
import sys
from PyQt4 import QtGui, Qt
import PyQt4.Qwt5 as Qwt
import numpy as np
from numpy import pi

class SimplePlot(Qwt.QwtPlot):
    def __init__(self, *args):
        Qwt.QwtPlot.__init__(self, *args)
        self.setTitle('simple plot demo')
        self.setCanvasBackground(Qt.Qt.white)
        
        #grid
        grid = Qwt.QwtPlotGrid()
        grid.attach(self)
        grid.setPen(Qt.QPen(Qt.Qt.black, 0, Qt.Qt.DotLine)) #make grid dotted-line
        
        x = np.linspace(-2*pi, 2*pi, 100)
        y = np.sin(x)
        
        curve = Qwt.QwtPlotCurve('y=sin(x)')
        curve.attach(self)
        curve.setPen(Qt.QPen(Qt.Qt.blue, 2, Qt.Qt.DotLine))
        curve.setSymbol(Qwt.QwtSymbol(Qwt.QwtSymbol.Ellipse, Qt.QBrush(Qt.Qt.red),
                                      Qt.QPen(Qt.Qt.red), Qt.QSize(5,5)))       
        curve.setData(x,y)
        
        self.replot()
        
def main():
    app = QtGui.QApplication(sys.argv)

    plot = SimplePlot()
    plot.setMinimumSize(300,200)
    plot.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()




pythonでグラフ pyqwt入門

python(x,y)に入っていたguiqwtはすごく高級なだけに、なかなかやりたいことがすぐ出来なかった。
ドキュメントがフランス語?みたいなのもあったし。試行錯誤したけど、もう諦めたwプログラムのわかっていない僕が複雑なことをやろうとすると、その場しのぎになって、かえってソースがぐちゃぐちゃしてしまう。

よってもう一つ低級なpyqwtでQtにプロットさせることを考える。
ドキュメントは「C++のqwtをみてね」、といった感じなので初心者には少し取っつきにくけど例を参考に少しずつやっていこう。 http://pyqwt.sourceforge.net/

まずは、シンプルなsin関数の表示をやってみた。




import sys
from PyQt4 import QtGui, Qt
import PyQt4.Qwt5 as Qwt
import numpy as np
from numpy import pi

class SimplePlot(Qwt.QwtPlot):
    def __init__(self, *args):
        Qwt.QwtPlot.__init__(self, *args)
        self.setTitle('simple plot demo')
        self.setCanvasBackground(Qt.Qt.white)
        
        #grid
        grid = Qwt.QwtPlotGrid()
        grid.attach(self)
        grid.setPen(Qt.QPen(Qt.Qt.black, 0, Qt.Qt.DotLine)) #make grid dotted-line
        
        x = np.linspace(-2*pi, 2*pi, 100)
        y = np.sin(x)
        
        curve = Qwt.QwtPlotCurve('y=sin(x)')
        curve.attach(self)
        curve.setData(x,y)
        
        self.replot()
        
def main():
    app = QtGui.QApplication(sys.argv)

    plot = SimplePlot()
    plot.setMinimumSize(300,200)
    plot.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()


2012年7月16日月曜日

ヘビでもわかるライトフィールドカメラの原理 まとめ

Lytroのライトフィールドカメラが世間を騒がせてから結構な時間が経ちました。
初めて見たときはまったく原理の想像がつきませんでした。幸いにもLytroのCEO, Ren NgのD論がHPで公開されているので、基本原理はだれでも勉強することができます。
https://www.lytro.com/downloads/resources/renng-thesis.pdf

また、元Adobe社員、現在はQualcommに勤めているTedor Georgievさんのページも大変参考になりました。いろんな資料がおいてあります。
http://www.tgeorgiev.net/

これから少しずつ自分の理解した範囲でLight Fieldカメラの原理(リフォーカスの原理)を明らかにしていきたいと思います。
以下目次です。

1 ライトフィールドとは。
    1.1 ライトフィールド(light field)とは

2. ライトフィールド記録の原理

3. ライトフィールドの処理方法
    3.2 データと分解能


このカメラの原理を勉強して勉強になったのは光線のイメージをより具体的に持てたということです。小学校で結像を理解するときには、光線は2本しか出て来ませんでした(レンズの中心を通るものと、光軸に平行なもの)。それ以上の本数でレンズを理解しようとした人はかなり少数なのではないでしょうか。このカメラの原理自体は100年くらい前に発案されているので、基本原理に立ち返ることがいかに大事かということを思い知らされます。


さて、ライトフィールドカメラはこれまでのカメラを置き換えていくのでしょうか。
正直そんなことはわかりません。時代の予測はいつだって難しいものです。
しかし、今後も確実なことは人間の目はライトフィールドを捉えていないという事実です。
人間は写真という2次元情報からそれ以上のさまざま情報を発起します。
ムービーがあっても写真がなくならないように、
映画ができても、小説がなくならないように、
今後も2次元の写真はなくならないでしょう。人間がそのようにできているからです。

もちろんLight Fieldを捉えてもよいのですが、
大事なのは最終的な2次元的成果物です。
その瞬間をいかに切り抜くかが写真の本質であり続けることに変わりはないでしょう。

Lytroについて追記した。(2014/11/06)
Lytroが日本に上陸してアクセスが増えたのでLytroのすごいと思うところをのべる。


2012年7月15日日曜日

guiqwtのpyplotを使ったグラフ

guiqwtでpyplotを使ったグラフの作成。
matplotlibと同様にかけてストレスがない。
とりあえずメモ。
参考URLを見てもわかるように、機能としては最低限なので、今後はこれ以外のモジュールを模索していきたい。

参考:http://packages.python.org/guiqwt/reference.html#guiqwt-pyplot

import numpy as np
from guiqwt import pyplot as plt

x = np.linspace(-5, 5, 100)
plt.figure(1)
plt.subplot(2,1,1)
plt.plot(x, np.sin(x), "r+")
plt.ylabel("sin")
plt.subplot(2, 1, 2)
y = np.random.rand(len(x))
plt.plot(x, y)
plt.ylabel("random")
plt.show()






guiqwtとmatplotlib

これまでmatplotlibのグラフ機能を勉強してそれをGUIアプリケーションに応用しようと考えていました。matplotlibはとても有名で、資料も豊富なのですが、どうも僕の用途には向かないようです。なぜならグラフをsubplotとして複数(10枚~)表示さえると著しく速度が低下してしまう。データ処理して表示さえるだけならいいのですが、インタラクティブに操作するには大変問題となってしまいます。

OpenGLで開発するしかないか。。。と諦めかけていた矢先、qtの開発環境としてPython(x,y)をインストールしたら、guiqwtなるものを発見しました。しかもサンプル付きで、そのサンプルがとても魅力的。

これはと思ってサンプルをいじって、何枚かグラフを表示させると明らかにmatplotlibよりも速い。

guiqwtとはPyQwt(PyQt4のプロットウィジット)とnumpyやscipyを元にしたもので、効率のよい2Dプロットを実現してくれるもの、らしい。http://packages.python.org/guiqwt/overview.html

たとえば、最低限の機能なら同じように記述できる。
import guiqwt.pyplot as plt # only this line has changed!
import numpy as np
x = np.linspace(-10, 10)
plt.plot(x, x**2, 'r+')
plt.show()


でも、前に書いたみたいにfig.add_subplot(111)などはできなかった。
今後はPyQtの勉強も兼ねてこのguiqwtの勉強をしていこう。



2012年7月1日日曜日

matplotlib その4 マウスで座標の取得

matplotlibでマウスのボタンが押されたときの情報を取得するプログラム。

ここでの取得項目は左から、
押されたボタンの種類(1:右、2:ホイール、3:左)、
マウスの押された場所でのピクセル座標(x, y)
マウスの押された場所でのデータ座標(x, y)


参考URL:http://matplotlib.sourceforge.net/users/event_handling.html



from numpy.random import rand
from pylab import figure, show

def onclick(event):
    print 'button=%d, x=%d, y=%d, xdata=%f, ydata=%f'%(
        event.button, event.x, event.y, event.xdata, event.ydata)

if __name__ == "__main__":
    fig = figure()
    ax = fig.add_subplot(111)
    ax.set_title("button pressed event demo")
    ax.plot(rand(5))

    fig.canvas.mpl_connect('button_press_event', onclick)
    show()