あるスレッドでジオメトリを変更し、別のスレッドでビジュアライザーを更新するアプリケーションが必要です。 それは可能ですか?
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
を外部から変更でき、それらの変更は視覚化に自動的に反映されるはずです。
これは実行可能ですが、少し複雑です。
まず、 glfw の制限により、 Visualizer
クラスはメインスレッドで処理する必要があります。 したがって、プログラムをレンダリング用のメインスレッドおよびジオメトリを更新するためのワーカースレッドとして構造化する必要があります。
次に、レンダリングループがどのように機能するかを理解していることを確認してください。 https://github.com/IntelVCL/Open3D/pull/343およびhttps://github.com/IntelVCL/Open3D/issues/357のコメントを参照してください。 つまり、レンダリングループは次のように機能します。
これはメインスレッドにある必要があります。
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()
がウィンドウを閉じる必要があるかどうかを示すフラグを返すことにも言及する価値があります。 これは、誰かがレンダリングループを自分で管理したい場合に非常に便利です。
最も参考になるコメント
@qianyizh 、あなたの助けに感謝し、そして遅い返事をお詫びします。 これが最終的に私のために働いたものです:
pc
をクラスメンバにしてself.worker
に変換してみましたが、その変更がビジュアライザに反映されませんでした。mp.Process
作成すると、pc
コピーが作成され、worker
がそれ自体のコピーを変更している可能性があると思います。だから私はからの変換送ることになった
worker
使用してQueue
。 これにより、ロックやcloud_updated
フラグが不要になります。