Open3d: Pythonマルチプロセッシングとビジュアライザー

作成日 2018年06月11日  ·  3コメント  ·  ソース: intel-isl/Open3D

あるスレッドでジオメトリを変更し、別のスレッドでビジュアライザーを更新するアプリケーションが必要です。 それは可能ですか?
ICPが1つのスレッドで実行されてジオメトリが更新され、ビジュアライザーの更新が別のスレッドで実行される、非ブロッキング視覚化の例のバージョンを探していると思います。

これが私がこれまでに持っているものです:

"""
Multi-threaded open3D viewer
"""
import open3d
import multiprocessing as mp

class Consumer(mp.Process):
  def __init__(self, vis_q):
    super(Consumer, self).__init__()
    self.vis_q = vis_q

  def run(self):
    while True:
      next_vis = self.vis_q.get()
      if next_vis is None:
        break

      next_vis.run()
      next_vis.destroy_window()

    return


class Viewer(object):
  def __init__(self):
    self.vis = {}

  def add_geometry(self, g, window_name):
    if window_name in self.vis:
      self.vis[window_name].add_geometry(g)
    else:
      self.vis[window_name] = open3d.Visualizer()
      self.vis[window_name].create_window(window_name, width=640, height=480)
      self.vis[window_name].add_geometry(g)

  def show(self, block=True):

    q = mp.JoinableQueue()
    num_workers = mp.cpu_count() * 2
    workers = [Consumer(q) for _ in xrange(num_workers)]
    for w in workers:
      w.start()

    for vis in self.vis.values():
      q.put(vis)

    for _ in xrange(num_workers):
      q.put(None)

    if block:
      q.join()

if __name__ == '__main__':
  pc1 = open3d.read_point_cloud('../data/camera/000.pcd')
  pc2 = open3d.read_point_cloud('../data/camera/001.pcd')

  v = Viewer()
  v.add_geometry(pc1, 'window1')
  v.add_geometry(pc2, 'window2')
  v.show()

2つのウィンドウが作成されますが、空白でハングします。

私の考えは、これが機能したら、追加されたジオメトリgを外部から変更でき、それらの変更は視覚化に自動的に反映されるはずです。

help wanted

最も参考になるコメント

@qianyizh 、あなたの助けに感謝し、そして遅い返事をお詫びします。 これが最終的に私のために働いたものです:

import open3d
import multiprocessing as mp
import time
import numpy as np
import transforms3d.euler as txe 
from Queue import Empty as queue_empty

class Viewer(object):
  def __init__(self):
    self.q = mp.Queue()

  def worker(self, q): 
    for _ in range(5):
      time.sleep(1)
      T = np.eye(4)
      T[:3, :3] = txe.euler2mat(np.deg2rad(20), 0, 0)
      q.put(T)
      print('put')
    q.put(None)  # poison pill

  def run(self):
    pc = open3d.read_point_cloud('narf/lidar_processed.pcd')
    vis = open3d.Visualizer()
    vis.create_window('cloud', width=640, height=480)
    vis.add_geometry(pc)

    p = mp.Process(target=self.worker, args=(self.q, ))
    p.start()

    keep_running = True
    while keep_running:
      try:
        T = self.q.get(block=False)
        if T is not None:
          print('got T')
          pc.transform(T)
        else:
          print('got poison. dying')
          keep_running = False
        vis.update_geometry()
      except queue_empty:
        pass
      vis.update_renderer()
      keep_running = keep_running and vis.poll_events()
    vis.destroy_window()
    p.join()

if __name__ == '__main__':
  v = Viewer()
  v.run()

pcをクラスメンバにしてself.workerに変換してみましたが、その変更がビジュアライザに反映されませんでした。 mp.Process作成すると、 pcコピーが作成され、 workerがそれ自体のコピーを変更している可能性があると思います。
だから私はからの変換送ることになったworker使用してQueue 。 これにより、ロックやcloud_updatedフラグが不要になります。

全てのコメント3件

これは実行可能ですが、少し複雑です。

まず、 glfw の制限により、 Visualizerクラスはメインスレッドで処理する必要があります。 したがって、プログラムをレンダリング用のメインスレッドおよびジオメトリを更新するためのワーカースレッドとして構造化する必要があります。

次に、レンダリングループがどのように機能するかを理解していることを確認してください。 https://github.com/IntelVCL/Open3D/pull/343およびhttps://github.com/IntelVCL/Open3D/issues/357のコメントを参照してください つまり、レンダリングループは次のように機能します。

  1. poll_events() を呼び出して (a) 必要に応じてレンダリングし、(b) 応答する必要があるマウス/キーボード イベントがあるかどうかを確認します
  2. 必要に応じてジオメトリをバインドします(再バインドするにはvis.update_geometry()を呼び出します)
  3. 必要に応じて OpenGL シェーダーをバインドします (再バインドしてレンダリングするには vis.update_renderer() を呼び出します)。
  4. 1に戻る

これはメインスレッドにある必要があります。
poll_events()が呼び出されない場合、あなたの場合と同じようにウィンドウが動かなくなります。 複数のウィンドウが必要な場合は、それらが同じスレッドにある必要があります。https://github.com/IntelVCL/Open3D/issues/357の私の例を参照して

最後に、レンダリングと同時にジオメトリを編集する必要があるため、ジオメトリの編集と再バインドの間には実際に競合状態があります。 ロックを導入する必要があります。

サンプルの擬似コードは次のとおりです。

# in worker thread
while 1:
    obtain_lock(pc_i)
    do_something(pc_i)
    is_dirty_pc_i = True
    release_lock(pc_i)

# in main thread
vis1 = Visualizer()
vis1.create_window()
vis1.add_geometry(pc_1)
vis2 = Visualizer()
vis2.create_window()
vis2.add_geometry(pc_2)
while True:
    obtain_lock(pc_1)
    obtain_lock(pc_2)
    if is_dirty_pc_1:
        vis1.update_geometry()
    if is_dirty_pc_2:
        vis2.update_geometry()
    vis1.update_renderer()
    vis2.update_renderer()
    vis1.poll_events()
    vis2.poll_events()
    is_dirty_pc_1 = False
    is_dirty_pc_2 = False
    release_lock(pc_1)
    release_lock(pc_2)
vis1.destroy_window()
vis2.destroy_window()

ただし、いくつかの問題がある可能性があります。 また、メインスレッドが頻繁にブロックされる可能性があります。 それでも、ワーカースレッドがメインスレッドで動作できるように全体を再構築し、ウィンドウを時々更新することをお勧めします。 何かのようなもの:

vis1 = Visualizer()
vis1.create_window()
vis1.add_geometry(pc_1)
vis2 = Visualizer()
vis2.create_window()
vis2.add_geometry(pc_2)
while True:
    worker_run()
    vis1.update_geometry()
    vis2.update_geometry()
    vis1.update_renderer()
    vis2.update_renderer()
    vis1.poll_events()
    vis2.poll_events()
vis1.destroy_window()
vis2.destroy_window()

@qianyizh 、あなたの助けに感謝し、そして遅い返事をお詫びします。 これが最終的に私のために働いたものです:

import open3d
import multiprocessing as mp
import time
import numpy as np
import transforms3d.euler as txe 
from Queue import Empty as queue_empty

class Viewer(object):
  def __init__(self):
    self.q = mp.Queue()

  def worker(self, q): 
    for _ in range(5):
      time.sleep(1)
      T = np.eye(4)
      T[:3, :3] = txe.euler2mat(np.deg2rad(20), 0, 0)
      q.put(T)
      print('put')
    q.put(None)  # poison pill

  def run(self):
    pc = open3d.read_point_cloud('narf/lidar_processed.pcd')
    vis = open3d.Visualizer()
    vis.create_window('cloud', width=640, height=480)
    vis.add_geometry(pc)

    p = mp.Process(target=self.worker, args=(self.q, ))
    p.start()

    keep_running = True
    while keep_running:
      try:
        T = self.q.get(block=False)
        if T is not None:
          print('got T')
          pc.transform(T)
        else:
          print('got poison. dying')
          keep_running = False
        vis.update_geometry()
      except queue_empty:
        pass
      vis.update_renderer()
      keep_running = keep_running and vis.poll_events()
    vis.destroy_window()
    p.join()

if __name__ == '__main__':
  v = Viewer()
  v.run()

pcをクラスメンバにしてself.workerに変換してみましたが、その変更がビジュアライザに反映されませんでした。 mp.Process作成すると、 pcコピーが作成され、 workerがそれ自体のコピーを変更している可能性があると思います。
だから私はからの変換送ることになったworker使用してQueue 。 これにより、ロックやcloud_updatedフラグが不要になります。

また、ドキュメントのどこかで、 poll_events()がウィンドウを閉じる必要があるかどうかを示すフラグを返すことにも言及する価値があります。 これは、誰かがレンダリングループを自分で管理したい場合に非常に便利です。

このページは役に立ちましたか?
0 / 5 - 0 評価

関連する問題

edxsx picture edxsx  ·  3コメント

prerakmody picture prerakmody  ·  3コメント

blackccpie picture blackccpie  ·  3コメント

nrj127 picture nrj127  ·  4コメント

masonsun picture masonsun  ·  3コメント