使用Open3D的非阻塞绘图过程(Python)


我决定将Open3D(Python)用于我的工作,因此我进行了查找,发现日语中的信息很少。
通常取决于阻塞可视化(draw_geometry)。

开始,我将总结我研究的范围。
至于点云处理,似乎其他人并不专门从事它,因此请参考。

2019/07/09后记
Open3D 0.7.0已发布,已在参考中,但未实现!那似乎减少了(消失了吗?)。
尽管还实现了remove_geometry,但我认为仍然有必要正确使用它,因为其行为与下面的替代方法不同。
(具体来说,似乎某些信息已重置,因为在remove_geometry中"删除"而不是"隐藏"了几何)

环境

Python 3.6.1 64位
Open3D 0.6.0
numpy 1.16.3
opencv-python 3.2.0

参考

官方参考

屏幕显示

单击此处可在一个循环中将窗口显示为父母的脸。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import open3d

# ウィンドウ初期化
vis = open3d.Visualizer()
vis.create_window(
    window_name="Hoge",  # ウインドウ名
    width=800,           # 幅
    height=600,          # 高さ
    left=50,             # 表示位置(左)
    top=50               # 表示位置(上)
)

while True:
    # 更新処理
    vis.update_geometry()
    vis.poll_events()
    vis.update_renderer()

这将弹出一个空白窗口!完蛋了!
顺便说一句,它不以关闭按钮结尾,所以按Ctrl C或类似的东西即可。

从文件加载几何并添加

我觉得我已经做完了,但是我不能仅凭此完成基本的非阻塞对象操作。
在Open3D中,图像,点云,网格等是从open3d.geometry.Geometry继承的派生类。
它似乎已定义,因此,如果添加这些,则可以在窗口中绘制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import open3d

# ウィンドウ初期化
vis = open3d.Visualizer()
vis.create_window(
    window_name="Hoge",  # ウインドウ名
    width=800,           # 幅
    height=600,          # 高さ
    left=50,             # 表示位置(左)
    top=50               # 表示位置(上)
)

# Geometry追加
img = open3d.read_image("lenna.png")
mesh = open3d.read_triangle_mesh("bun_zipper.ply")

vis.add_geometry(img)
vis.add_geometry(mesh)

while True:
    # 更新処理
    vis.update_geometry()
    vis.poll_events()
    vis.update_renderer()

顺便说一下,这只著名的兔子是斯坦福兔子(Stanford Bunny)
这是正确的。
它将像这样显示。
00.png
顺便说一句,在这一点上,默认情况下可以进行鼠标操作,因此斯坦福·兔子(Stanford Bunny)和兰娜小姐(Miss Lenna)在后台
你可以四处走动。
相反,我不知道如何禁用此操作。请告诉我。

处理图像

到目前为止,只看本教程而无需看这样的文章就足够了,但是以下几点有点挤塞了。
open3d.read_image的返回值是open3d.geometry.Image类,但是触摸像素数据需要一些技巧。

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
31
32
33
34
35
36
37
38
39
import open3d
import cv2
import numpy as np

# ウィンドウ初期化
vis = open3d.Visualizer()
vis.create_window(
    window_name="Hoge",  # ウインドウ名
    width=800,           # 幅
    height=600,          # 高さ
    left=50,             # 表示位置(左)
    top=50               # 表示位置(上)
)

# Geometry追加
img = open3d.read_image("lenna.png")
mesh = open3d.read_triangle_mesh("bun_zipper.ply")
vis.add_geometry(img)
vis.add_geometry(mesh)

# 画素値をコピー
pix = np.asarray(img).copy()

# 任意の画像処理
bgr_pix = cv2.cvtColor(pix, cv2.COLOR_RGB2BGR)
bgr_flg = True

while True:
    # 毎フレーム処理
    if bgr_flg:
        np.asarray(img)[:] = bgr_pix
    else:
        np.asarray(img)[:] = pix
    bgr_flg = not bgr_flg

    # 更新処理
    vis.update_geometry()
    vis.poll_events()
    vis.update_renderer()

执行结果如下。最终,这是一个非阻塞过程。
anim.gif
我的眼睛在闪烁。
上面代码的关键是通过open3d.geometry.Image类中的numpy.asarray直接访问像素值。
顺便说一句,如果您忘记复制,像素值将被照原样引用,因此不会像上面那样闪烁。

如果要在内存中操作而不是从

文件读取图像,则可以按以下方式对其进行初始化。

1
2
3
4
5
6
7
import open3d
import numpy as np

width = 100
height = 100
buf = np.zeros([width, height, 3], np.uint8)
img = open3d.geometry.Image(buf)

但是,即使我篡改了缓冲区数组(buf)的这段代码,它也没有反映在图像中,因此看来我需要以np.asarray(img)的形式对其进行访问。

操纵网格和点云

如果您查看

geometry.TriangleMesh的官方参考,则有一个名为rotate的方法,因此似乎可以使用此方法旋转。
不,很方便。

1
2
 >> "rotate" in dir(open3d.TriangleMesh)
  False

呢?是不是
显然,即使参考文件中有描述,也有许多未实现的部分。
它无能为力,所以让我们使用转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 変換行列(4*4)
rad = np.deg2rad(5)
mat = np.array([
    np.cos(rad), -np.sin(rad), 0, 0,
    np.sin(rad), np.cos(rad), 0, 0,
    0, 0, 1, 0,
    0, 0, 0, 1
]).reshape(4, 4)

while True:
    # 毎フレーム処理
    mesh.transform(mat)

    # 更新処理
    vis.update_geometry()
    vis.poll_events()
    vis.update_renderer()

省略,因为每次都写所有东西很长。
每帧将在Z轴方向旋转5度。

定义一个回调

查看

参考,在Visualizer的派生类中定义了一个不错的类VisualizerWithKeyCallback。
它非常易于使用。

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import open3d
import numpy as np

# ウィンドウ初期化
vis = open3d.VisualizerWithKeyCallback()  # <-コールバック対応クラス
vis.create_window(
    window_name="Hoge",  # ウインドウ名
    width=800,           # 幅
    height=600,          # 高さ
    left=50,             # 表示位置(左)
    top=50               # 表示位置(上)
)

# Geometry追加
img = open3d.read_image("lenna.png")
mesh = open3d.read_triangle_mesh("bun_zipper.ply")
vis.add_geometry(img)
vis.add_geometry(mesh)

# 変換行列(4*4)
rad = np.deg2rad(5)
mat = np.array([
    np.cos(rad), -np.sin(rad), 0, 0,
    np.sin(rad), np.cos(rad), 0, 0,
    0, 0, 1, 0,
    0, 0, 0, 1
]).reshape(4, 4)

# コールバックを登録
rotate_flg = False
def rotate_callback(_vis):
    global rotate_flg
    rotate_flg = not rotate_flg
vis.register_key_callback(ord("R"), rotate_callback)

while True:
    # 毎フレーム処理
    if rotate_flg:
        mesh.transform(mat)

    # 更新処理
    vis.update_geometry()
    vis.poll_events()
    vis.update_renderer()

顺便说一句,传递给回调的是调用Visualizer的类(可能)
现在,每次按" R"键,兔子的旋转将被打开/关闭。
重点是

1
vis.register_key_callback(ord("R"), rotate_callback)

此处指定的密钥必须为大写。
我沉迷于此,但我真的不明白原因,所以请问一个熟悉它的人。
(我认为这与C的实现是一种平衡。)

显示/隐藏点云/网格

到目前为止,作为总结,让我们使用回调通过触摸按钮来删除或释放兔子。
没关系,如果您查看Visualizer参考,则有一个名为remove_geometry的方法,因此看来您可以使用此方法轻松实现。

1
2
>> "remove_geometry" in dir(open3d.Visualizer)
False

不,不是吗? ?? ?? ?? ??
如果您在这里看,它会说:"它在C语言中,但是Python现在没有这种接口。"不要将其放在参考中。
但是,幸运的是,还编写了可以用Python完成的另一种方法,因此我将参考这一点来实现它。

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
31
32
33
34
35
36
37
38
39
import copy
import open3d
import numpy as np

# ウィンドウ初期化
vis = open3d.VisualizerWithKeyCallback()  # <-コールバック対応クラス
vis.create_window(
    window_name="Hoge",  # ウインドウ名
    width=800,           # 幅
    height=600,          # 高さ
    left=50,             # 表示位置(左)
    top=50               # 表示位置(上)
)

# ウサギを読み込んでコピーを取って表示用と分ける
base_mesh = open3d.read_triangle_mesh("bun_zipper.ply")
view_mesh = copy.copy(base_mesh)
vis.add_geometry(view_mesh)

# コールバックを登録
usagi_on = True
def usagi_callback(_vis):
    global usagi_on, view_mesh
    if usagi_on:
        view_mesh.vertices = open3d.Vector3dVector([])
        view_mesh.triangles = open3d.Vector3iVector([])
    usagi_on = not usagi_on
vis.register_key_callback(ord("U"), usagi_callback)

while True:
    # 毎フレーム処理
    if usagi_on:
        view_mesh.vertices = base_mesh.vertices
        view_mesh.triangles = base_mesh.triangles

    # 更新処理
    vis.update_geometry()
    vis.poll_events()
    vis.update_renderer()

现在,每按一次'U'键,兔子就会出现并消失。
有2分,

1
2
view_mesh = copy.copy(base_mesh)
vis.add_geometry(view_mesh)

在这里,我制作一个副本并将其注册在Visualizer中,仅用于显示以及用于显示和原始数据。
remove_geometry的基本替代方法是

1
2
view_mesh.vertices = open3d.Vector3dVector([])
view_mesh.triangles = open3d.Vector3iVector([])

部分中,将空值分配给顶点和三角形网格。
顺便说一下,顶点和三角形都可以在np.asarray中看到原始数据,但是如果您尝试直接将其清空,则Shape会有所不同! !! !!我会生气。

所以,你可以做什么?

如上所述,可以非常容易地实现3D模型RGB图像的非阻塞处理。
我认为使用RGB相机和cv2.aruco玩增强现实很容易,因此,如果可以承受的话,我想总结一下。