2017-10-02 19 views
0

私は、画像データを表示するPyQt4アプリケーションの組み込みコントロールとしてmatplotlibを使用している初心者です。私は、ユーザーがクリックしてドラッグすることによって、イメージ上に線をインタラクティブに描くことを可能にしたいと思っています。私はそれが働いているが、それは使えないほど遅いので、私はそれを正しい方法で行かないと信じている。私がラインを表示できる唯一の方法は、マウスが動くたびにキャンバスを再描画することです(これが減速の原因と思われます)。次のように2D Matplotlibプロットで線をインタラクティブに描画する最も良い方法

例えば、マウスダウンイベントに私は、現在の座標を格納し、プロットにLine2Dオブジェクトを追加します。次のように

def onMouseMove(self, event): 

    if self.drawingLine: 

     self.lineStartX = event.xdata 
     self.lineStopX = event.xdata 
     self.lineStartY = event.ydata 
     self.lineStopY = event.ydata 

     self.line = Line2D([self.lineStartX, self.lineStopX], [self.lineStartY, self.lineStopY], linewidth = 1.5, color = 'r') 

     self.axes.add_line(self.line) 

その後、私のマウス移動イベントで、私はラインを再描画:

def onMouseMove(self, event): 

    if self.drawingLine: 

     self.lineStopX = event.xdata 
     self.lineStopY = event.ydata 

     # Adjust the line to the new endpoint: 
     self.line.set_data([self.lineStartX, self.lineStopX], [self.lineStartY, self.lineStopY]) 

     # Force a redraw otherwise you don't see any changes: 
     self.fig.canvas.draw() 

私が述べたように、このアプローチは非常に遅く、したがっておそらく間違っています。誰かがここで適切なアプローチが何であるかを私に手がかりにしてもらえますか?あらかじめありがとうございます。

答えて

1

まず第一に、あなたはすでに

self.fig.canvas.draw_idle() 

代わりのdraw()を使用することによって少しを得ることができます。これは、現在描画されていないときにのみキャンバスを再描画し、たくさんの描画を保存します。

これで十分でない場合は、blittingのテクニックを使用する必要があります。最小限の例がないので、ここでは完全な解決策を提供しませんが、この質問に対する回答、why is plotting with Matplotlib so slow?はその例です。 アイデアは、背景を保存し、変更部分(ここでは線)のみを再描画することです。

background = fig.canvas.copy_from_bbox(ax.bbox) 
# then during mouse move 
fig.canvas.restore_region(background) 
line.set_data(...) 
ax.draw_artist(line) 
fig.canvas.blit(ax.bbox) 
# only after mouse has stopped moving 
fig.canvas.draw_idle() 

このテクニックは、一部のmatplotlibウィジェットで内部的にも使用されています。 matplotlib.widgets.Cursorを使用すると、行をカーソルにすばやく追従させることができます。

これは私に最後のポイントをもたらします。これは、あなたがホイールを再発明する必要はありません。 matplotlib.widgets.RectangleSelectorがあり、defautによって選択のための矩形が描画されます。しかし、そのdrawtype='line'引数を使用して、選択肢を1行に変更し、引数blit=Trueと一緒にすれば、必要なものが得られるはずです。選択が終わったら最後に線を引くだけです。

最新のmatplotlibのバージョンでは、matplotlib.widgets.PolygonSelectorがあります。これはあなたが必要とするものかもしれません。

1

matplotlibは柔軟性があり、複数の異なるバックエンドで動作するように構築されています。リアルタイムプロットでは非常に遅いです。問題は、マウスの移動イベントが非常に速いことです。マウスの動きに追いつこうとするものは、おそらく遅いでしょう。プロットをあまり頻繁に呼び出す必要はありません。これは、マウスの移動機能の時間をチェックし、何かにプロットコールを制限しようとすることで実行できます。

import time 

def onMouseMove(self, event): 
    if self.drawingLine and time.time() - last_time > 0.03: # Change the 0.03 to change how often you plot. 
     last_time = time.time() 
     ... 

私は非常にpyqtgraphを提案します。 pyqtgraphには、これを行うために作業できる速度制限信号が組み込まれています。

以下は、これを行うための基本的な例です。

# Change the style to look like matplotlib 
pyqtgraph.setConfigOption("background", QtGui.QColor.fromRgbF(230/255, 230/255, 234/255, 255/255)) 
pyqtgraph.setConfigOption("background", 'w') 
pyqtgraph.setConfigOption("foreground", 'k') 
pyqtgraph.setConfigOption("antialias", True) 

# Create the widgets and plot items 
glw = pyqtgraph.GraphicsLayoutWidget() 
pg = glw.addPlot(0, 0) 


class MyClass(object): 
    ... 

    ... 
    def onMouseMove(self, event): 
     if self.drawingLine: 

      scene_pos = event[0] 
      data_pos = pg.getViewBox().mapSceneToView(scene_pos) 
      x, y = data_pos.x(), data_pos.y() 

      self.lineStopX = x 
      self.lineStopY = y 

      # Adjust the line to the new endpoint: 
      if not self.line: 
       self.line = pg.plot(x=[], y=[]) 
      self.line.setData(x=[self.lineStartX, self.lineStopX], 
           y=[self.lineStartY, self.lineStopY]) 

mouse_move_sig = pyqtgraph.SignalProxy(pg.scene().sigMouseMoved, 
             rateLimit=60, slot=onMouseMove) 
+0

時間制限を導入すると、線のダイナミクスに遅れが生じます。だから遅れを防ぐために、あなたは遅れを導入する。私には良い解決策のようには聞こえません。 – ImportanceOfBeingErnest

+0

遅れは、matplotlibが線を描くのに時間がかかるためです。 matplotlibに100万回線を描画するように指示すると、プログラムは線を何度も繰り返し描画します。あなたのプログラムは応答しなくなります。 figure.draw_idle()はこれをある程度助けますが、私がmatplotlibがリアルタイムプロットになると遅くなる前に言ったように、これは役に立ちます。 – HashSplat

+0

これを行うmatplotlibの方法は、FunctionAnimation https://matplotlib.org/2.0.0/examples/animation/animate_decay.htmlを使用することです。基本的には、実際にデータを収集してから、タイマの間隔がはるかに遅い別のタイマーでプロット関数を呼び出します。あなたは目を見て特定の速度でしか理解できません。行のすべての微小変化をプロットする必要はありません。ユーザーにスムーズに見えるレートでプロットするだけで済みます。私は、提案された最小値が20-30 fpsだと思います。画面上でのプロットや描画は、非常に時間がかかる非常に高価な操作です。 – HashSplat

関連する問題