wxpythonとvtkでビデオプレーヤー作ってみる。その5。完成。opencvとvtkImageViewerで読み込み・表示。
目次
- その0。wxpythonのウィンドウ内部にvtkを表示するパネルを作ってみた
- その1。C++のJpeg表示サンプルをPythonに翻訳。
- その2。numpyアレイで用意した2Dテクスチャを四角ポリゴンに貼る。
- その3。vtkImageViewerでjpeg表示。
- その4。vtkImageViewerでnumpyアレイ表示。
- その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」カテゴリの記事
- Noteの記事をPythonでバックアップしといた。(2021.05.05)
- JupyterLabでも好きな外部エディターを使いたい!(2018.05.02)
- ローカルエリア内のJupyterLabサーバーにLAN経由で接続する。(2018.05.02)
- Juliaやってみよう。五日目。Pythonと速度比較。(2017.08.01)
- Juliaやってみよう。四日目。@timeでプロファイリング(2017.07.16)
« wxpythonとvtkでビデオプレーヤー作ってみる。その4。vtkImageViewerでnumpyアレイ表示。 | トップページ | スクラッチとはこうやるのさ。子犬のDJの技がすごすぎる件。 »



コメント