« wxpythonとvtkでビデオプレーヤー作ってみる。その4。vtkImageViewerでnumpyアレイ表示。 | トップページ | スクラッチとはこうやるのさ。子犬のDJの技がすごすぎる件。 »

2013年7月 1日 (月)

wxpythonとvtkでビデオプレーヤー作ってみる。その5。完成。opencvとvtkImageViewerで読み込み・表示。

目次

とりあえず出来たwxVTKRenderWindowでつくるビデオプレーヤー

前回よりだいぶ間が開いてしまいましたが、wxpythonとvtkでビデオプレーヤー作ってみるシリーズその5で最終回です。opencvでビデオを読んでnumpyアレイとして画像を得て、これをvtkImageViewerで表示するという流れで作ってみました。わざわざvtk使う必要がまったくないですが、まあ練習です。

注意点は、opencvとvtkImageViewerでは画像のフォーマットに2つ違いがあること。一つは色でopencvはRGBじゃなくってBGR。もう一つはnumpyアレイの最初の次元はy軸だけど、これが(0,0)が左上なのか左下なのかという違いのせいでy座標を上下逆さにする必要もある。上下逆さに関してはカメラを回転させればいいじゃんとおもい、

renderer.GetActiveCamera().Azimuth(180)

とかやってみたが、wxに埋め込んだせいか、なぜかカメラの設定は何を変えても影響がなかった。actorの方はPositionとか変えるとちゃんと変化するのでactorの方でやろうかとおもったが、actor2DにはRotateZメソッドがない(普通の2Dじゃない普通のactorならある)。vtkでウィンドウ出した場合はこれで大丈夫なはずなので、wxVTKRenderWindowをつかっている為かなぁ。

opencvのウィンドウをwxに埋め込めたらいいんだけど、C++なら多分可能だろうけど、wxpythonではちょっとやり方がわからない。

ちょっとスピードアップ

dataImporter.SetImportVoidPointerを使うことで、numpyアレイのコピーなしでvtkImageViewerをアップデートできることが分かったので、ちょっとスピードアップしました。前回のコードでは

self.dataImporter.CopyImportVoidPointer(data_matrix, data_matrix.nbytes)
self.imageViewer.SetInputConnection( self.dataImporter.GetOutputPort() )
self.Refresh()

とやっていたが、CopyImportVoidPointerだとnumpyアレイがコピーされてしまうので、遅い。そのため、SetImportVoidPointer(self.data_matrix)としてnumpyアレイをそのまま渡してやるとnumpyアレイがあるメモリのポインターを見つけて来る。numpyアレイバッファーがそのまま画像データになるらしく、ImageViewer.SetInputConnectionは要らなくなった。

FPSについて

ビデオのFPSの値でwx.Timerイベントを発生させるようにtimer.Start(1000.0/self.fps)という感じでフレームの更新をしていますが、これだと微妙に遅れがちです。timer.Start()に渡す値を上げればちゃんと速くなるのでマシンの限界ではなく、処理の仕方がアホなんだとおもいます。

正確なFPSで表示するには、もうちょっと工夫しないといけないようですね。画像バッファーの更新を行うタイミングが、wx.Timerイベントの発生後なのがいけないのではと思い、タイマーイベントではself.Refresh()を先に行うようにし、リフレッシュの直後に画像バッファーの更新としてみたがダメ。ふーむ。まあいいか。

今回のコード

使い方:ビデオファイルをドロップすると、ファイルが開かれて、Playボタン、スライダーバー等が有効になる。

import cv2
import cv2.cv as cv
import numpy as np
import vtk
from vtk.wx.wxVTKRenderWindow import wxVTKRenderWindow
import wx


class FileDrop(wx.FileDropTarget):
    def __init__(self, parent):
        wx.FileDropTarget.__init__(self)
        self.parent = parent
    def OnDropFiles(self, x, y, filenames):
        fp = filenames[0] # I need one file only. discard others.
        self.parent.initialize(fp)


class Test(wx.Frame):

    def __init__ (self):

        wx.Frame.__init__(self, None, title='vtkImageViewer')
        self.SetBackgroundColour('#EEEEEE')
        self.SetDropTarget(FileDrop(self))

        self.canvas = wx.Panel(self, size=(640/2,480/2))

        self.playbtn = wx.Button(self, -1, 'Play')
        self.playbtn.Bind(wx.EVT_BUTTON, self.OnPlaybtn)
        self.playbtn.Enable(False)

        self.stopbtn = wx.Button(self, -1, 'Stop')
        self.stopbtn.Bind(wx.EVT_BUTTON, self.OnStopbtn)
        self.stopbtn.Enable(False)

        self.slider = wx.Slider( self, -1, 0, 
                minValue=1, maxValue=1, style=wx.SL_HORIZONTAL )
        self.slider.Bind(wx.EVT_SLIDER, self.OnSlider)
        self.slider.Enable(False)

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add(self.playbtn, 0, wx.EXPAND|wx.ALL, border=2)
        hbox.Add(self.stopbtn, 0, wx.EXPAND|wx.ALL, border=2)
        hbox.Add(self.slider, 1, wx.EXPAND|wx.ALL, border=5)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.canvas, 1, wx.EXPAND)
        sizer.Add(hbox, 0, wx.EXPAND)
        self.SetSizerAndFit(sizer)
        self.Layout()
        self.Show()

        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)

        self.playing = False
        self.current_position = 0


    def initialize(self, fp):

        self.capture = cv2.VideoCapture(fp)
        success, self.data_matrix = self.capture.read()
        if not success:
            dlg = wx.MessageDialog(
                    self, 
                    "%s\nInput file is not supported" % fp, 
                    'A Message Box',
                    wx.OK | wx.ICON_INFORMATION )
            dlg.ShowModal()
            dlg.Destroy()
            return

        h,w,_ = self.data_matrix.shape
        max_frame = int(self.capture.get(cv.CV_CAP_PROP_FRAME_COUNT))
        self.fps = self.capture.get(cv.CV_CAP_PROP_FPS)

        self.timer.Start(1000.0/self.fps)

        self.canvas.DestroyChildren()
        self.canvas.SetMinSize( (w, h) )

        self.SetSize( self.GetSizer().ComputeFittingClientSize(self) )
        self.Layout()

        # Set a numpy array up and import it to vtk
        dataImporter = vtk.vtkImageImport()
        dataImporter.SetImportVoidPointer(self.data_matrix)
        dataImporter.SetDataScalarTypeToUnsignedChar()
        dataImporter.SetNumberOfScalarComponents(3)
        dataImporter.SetDataExtent(0, w-1, 0, h-1, 0, 0)
        dataImporter.SetWholeExtent(0, w-1, 0, h-1, 0, 0)
        self.dataImporter = dataImporter

        imageViewer = vtk.vtkImageViewer()
        imageViewer.SetInputConnection( dataImporter.GetOutputPort() )
        imageViewer.SetColorWindow( 255 )
        imageViewer.SetColorLevel ( 128 )

        actor = imageViewer.GetActor2D()

        renderer = vtk.vtkRenderer()
        renderer.AddActor(actor)

        #cam = renderer.GetActiveCamera().Azimuth(180) # no effect?

        rwi = wxVTKRenderWindow(self.canvas, -1)
        rwi.GetRenderWindow().AddRenderer(renderer)

        self.playbtn.Enable(True)
        self.stopbtn.Enable(True)

        self.slider.Enable(True)
        self.slider.SetMax(max_frame-1)
        self.slider.SetValue(0)

        self.updateimg(None)


    def updateimg(self, event):
        success, self.data_matrix = self.capture.read() 
        # opencv uses BGR and y-axis is flipped. slow here
        self.data_matrix = cv2.cvtColor( self.data_matrix, cv.CV_BGR2RGB)
        self.data_matrix = cv2.flip( self.data_matrix, 0 )

        self.dataImporter.SetImportVoidPointer(self.data_matrix)
        self.Refresh()

    def changeTitle(self, current_position):
        self.SetTitle( 
            'vtkImageViewer  Elasped %2.2f (s)' % 
                    (current_position / self.fps) )

    def OnTimer(self, event):
        if self.playing:
            current_position = self.capture.get(cv.CV_CAP_PROP_POS_FRAMES)
            self.changeTitle(current_position)
            self.slider.SetValue(current_position)
            self.updateimg(event)

    def OnSlider(self, event):
        current_position = self.slider.GetValue()
        self.capture.set(cv.CV_CAP_PROP_POS_FRAMES, current_position)
        self.changeTitle(current_position)
        self.updateimg(event)

    def OnStopbtn(self, event):
        self.capture.set(cv.CV_CAP_PROP_POS_FRAMES, 0)
        self.updateimg(event)
        self.slider.SetValue(0)
        self.changeTitle(0)
        self.playing = False
        self.playbtn.SetLabel('Play')

    def OnPlaybtn(self, event):
        if self.playing:
            self.playbtn.SetLabel('Play')
        else:
            self.playbtn.SetLabel('Pause')
        self.playing = (self.playing == False)


if __name__ == '__main__':

    app = wx.App(0)
    frame = Test()
    app.MainLoop()

« wxpythonとvtkでビデオプレーヤー作ってみる。その4。vtkImageViewerでnumpyアレイ表示。 | トップページ | スクラッチとはこうやるのさ。子犬のDJの技がすごすぎる件。 »

Python」カテゴリの記事

コメント

コメントを書く

コメントは記事投稿者が公開するまで表示されません。

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/1169291/52256418

この記事へのトラックバック一覧です: wxpythonとvtkでビデオプレーヤー作ってみる。その5。完成。opencvとvtkImageViewerで読み込み・表示。:

« wxpythonとvtkでビデオプレーヤー作ってみる。その4。vtkImageViewerでnumpyアレイ表示。 | トップページ | スクラッチとはこうやるのさ。子犬のDJの技がすごすぎる件。 »

広告欄


やっつけタイムライン

広告欄

はてブ

人目の訪問です。

  • follow us in feedly

    かなり更新が不定期なため、RSSリーダーをオススメします。現在Feedlyに122人登録頂いています。多謝!RSSを表示

    ブログランキング用 にほんブログ村 IT技術ブログ Pythonへ ブログランキングならblogram






    Jenny Mayhem
2017年11月
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    

IT技術注目記事

無料ブログはココログ