将深度投影到原始网格上

Deprojecting depth onto original mesh

我正在尝试获取对象的搅拌机渲染深度图,然后移动它以覆盖原始对象。目前我对渲染对象并将其提取到它的位置没有问题。

但是,我在尝试将对象定位到其原始位置时卡住了。

我正在尝试将逆相机世界矩阵应用于渲染的点云(蓝色)。不幸的是,当我应用所述相机反转时,它并没有出现在我期望的地方(红色)。

我附上了复制此行为的全部代码。如果有人能指出我应该乘以点云的正确矩阵,我将不胜感激。

from mathutils import Vector, Quaternion, Euler, Matrix
import numpy as np
import bpy


def main_script():
    clear_scene()
    prepare_views()

    tmp_path = "/tmp/tmp_render.exr"
    scene = get_scene("Scene")
    camera = create_camera("Camera")
    camera.rotation_euler = Euler([np.pi * 0.5, 0, np.pi * 0.5], "XYZ")
    camera.location = Vector([4.5, 0, 1])

    bpy.ops.mesh.primitive_monkey_add(
        location=(0, 0, 1), rotation=(0, 0, np.pi*0.5), size=1.0)

    _w, _h = 640, 480

    update_scene()
    init_rendering(scene, camera, width=640, height=480)
    update_scene()

    matrix_K = get_calibration_matrix_K_from_blender(scene, camera.data)
    _fy, _fx = matrix_K[0][0], matrix_K[1][1]
    _cy, _cx = matrix_K[0][2], matrix_K[1][2]

    scene.render.filepath = tmp_path
    bpy.ops.render.render(write_still=True)
    depth = read_exr(tmp_path, "R")["R"]
    depth = np.reshape(convert_to_numpy(depth), [_h, _w])
    exr_cloud = depth_to_cloud(
        _w, _h, _fx, _fy, _cx, _cy, depth)
    exr_cloud = np.reshape(exr_cloud, [-1, 3])
    exr_cloud = exr_cloud[(exr_cloud[..., 2] < 100) & (exr_cloud[..., 2] > 0)]

    matrix = np.reshape(camera.matrix_world, [4, 4])
    matrix = np.linalg.inv(matrix) # why doesn't this place the depth properly

    vertices = np.ones([exr_cloud.shape[0], 4], dtype=np.float32)
    vertices[:, 0:3] = exr_cloud
    vertices = np.array(
        [matrix @ vertex for vertex in vertices], dtype=np.float32)
    vertices = vertices[..., :3]

    create_mesh("Suzanne_EXR", exr_cloud, [])
    create_mesh("SuzanneT_EXR", vertices, [])

"""
    utilities methods required to run the script
"""

def clear_scene():
    for scene in bpy.data.scenes:
        for obj in scene.objects:
            bpy.context.collection.objects.unlink(obj)

def read_exr(path, channels):
    import OpenEXR as _OpenEXR
    import Imath as _Imath

    file = _OpenEXR.InputFile(path)

    FLOAT = _Imath.PixelType(_Imath.PixelType.FLOAT)

    results = {}
    for ch in channels:
        results[ch] = file.channel(ch, FLOAT)

    file.close()

    return results


def convert_to_numpy(data):
    import array as _array
    return np.array(_array.array("f", data).tolist())


def update_scene():
    dg = bpy.context.evaluated_depsgraph_get()
    dg.update()


def prepare_views():
    preferences = bpy.context.preferences

    preferences.view.show_tooltips_python = True
    preferences.view.show_developer_ui = True
    preferences.view.render_display_type = "NONE"


def init_rendering(scene, camera, width=None, height=None):
    def set_rendering_settings(camera, scene, width=640, height=480):
        image_settings = scene.render.image_settings
        image_settings.file_format = "OPEN_EXR"
        image_settings.use_zbuffer = True

        scene.render.resolution_x = width
        scene.render.resolution_y = height
        # scene.render.use_antialiasing = False

    scene.use_nodes = True
    scene.camera = camera
    node_tree = scene.node_tree
    nodes = node_tree.nodes

    node_render_layers = nodes["Render Layers"]
    node_composite = nodes["Composite"]

    node_tree.links.clear()
    node_tree.links.new(
        node_render_layers.outputs["Depth"], node_composite.inputs["Image"])

    set_rendering_settings(camera, scene)


def get_scene(name): return bpy.data.scenes[name]


def create_camera(name):
    camera = bpy.data.cameras.new(name)
    camera.lens = 50

    obj = bpy.data.objects.new(name, camera)
    bpy.context.collection.objects.link(obj)

    return obj

# ---------------------------------------------------------------
# 3x4 P matrix from Blender camera
# ---------------------------------------------------------------

# Build intrinsic camera parameters from Blender camera data
#
# See notes on this in
# blender.stackexchange.com/questions/15102/what-is-blenders-camera-projection-matrix-model


def get_calibration_matrix_K_from_blender(scene, camera):
    from mathutils import Matrix as _Matrix
    f_in_mm = camera.lens
    resolution_x_in_px = scene.render.resolution_x
    resolution_y_in_px = scene.render.resolution_y
    scale = scene.render.resolution_percentage / 100
    sensor_width_in_mm = camera.sensor_width
    sensor_height_in_mm = camera.sensor_height
    pixel_aspect_ratio = scene.render.pixel_aspect_x / scene.render.pixel_aspect_y
    if (camera.sensor_fit == 'VERTICAL'):
        # the sensor height is fixed (sensor fit is horizontal),
        # the sensor width is effectively changed with the pixel aspect ratio
        s_u = resolution_x_in_px * scale / sensor_width_in_mm / pixel_aspect_ratio
        s_v = resolution_y_in_px * scale / sensor_height_in_mm
    else:  # 'HORIZONTAL' and 'AUTO'
        # the sensor width is fixed (sensor fit is horizontal),
        # the sensor height is effectively changed with the pixel aspect ratio
        pixel_aspect_ratio = scene.render.pixel_aspect_x / scene.render.pixel_aspect_y
        s_u = resolution_x_in_px * scale / sensor_width_in_mm
        s_v = resolution_y_in_px * scale * pixel_aspect_ratio / sensor_height_in_mm

    # Parameters of intrinsic calibration matrix K
    alpha_u = f_in_mm * s_u
    alpha_v = f_in_mm * s_v
    u_0 = resolution_x_in_px * scale / 2
    v_0 = resolution_y_in_px * scale / 2
    skew = 0  # only use rectangular pixels

    K = _Matrix(
        ((alpha_u, skew,    u_0),
         (0, alpha_v, v_0),
         (0, 0,        1)))
    return K


def create_mesh(name, vertices, faces):
    import bmesh as _bmesh
    mesh = bpy.data.meshes.new("Mesh_%s" % name)
    mesh.from_pydata(vertices, [], faces)
    mesh.update()

    obj = bpy.data.objects.new(name, mesh)

    bpy.context.collection.objects.link(obj)

    bm = _bmesh.new()
    bm.from_mesh(mesh)

    bm.to_mesh(mesh)
    bm.free()

    return obj


def depth_to_cloud(w, h, fx, fy, cx, cy, depth):
    from numpy import concatenate as _concat
    from numpy import indices as _indices
    from numpy import newaxis as _newaxis

    indices = _indices(depth.shape)

    indices_y, indices_x = indices

    ys, xs, zs = \
        (indices_y - cy) * depth / fy, \
        (indices_x - cx) * depth / fx, \
        depth

    points = _concat([xs[..., _newaxis], ys[..., _newaxis],
                      zs[..., _newaxis]], axis=2)

    return points


if __name__ == "__main__":
    raise main_script()

问题很复杂,首先我需要将变换后的顶点计算从使用逆相机世界矩阵替换为像这样的负缩放相机世界矩阵

    matrix_cam = np.reshape(camera.matrix_world, [4, 4])
    mat_scale = np.array(Matrix.Scale(-1, 4))

    matrix = matrix_cam @ mat_scale

    vertices = np.ones([exr_cloud.shape[0], 4], dtype=np.float32)
    vertices[:, 0:3] = exr_cloud
    vertices = np.array(
        [matrix @ vertex for vertex in vertices], dtype=np.float32)
    vertices = vertices[..., :3]

此外,深度解码有问题,导致点云变形,已修复

    ys, xs, zs = \
        (indices_y - cx) * depth / fx, \
        (indices_x - cy) * depth / fy, \
        depth