You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Open3D空间雕刻算法进行实体体素化结果异常的原因分析

问题分析:基于Open3D的空间雕刻体素化结果异常

问题描述

使用Open3D 0.18.0实现空间雕刻算法,对斯坦福数据库的Dragon三角网格模型进行实体体素化,运行代码后出现多条窗口尺寸不匹配警告,最终生成的VoxelGrid仅包含2184个体素,结果严重不符合预期。

体素化结果图:
体素化结果图

实现代码如下:

# ----------------------------------------------------------------------------
# -                        Open3D: www.open3d.org                            -
# ----------------------------------------------------------------------------
# The MIT License (MIT)
#
# Copyright (c) 2018-2021 www.open3d.org
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
# ----------------------------------------------------------------------------

import open3d as o3d
import numpy as np


def xyz_spherical(xyz):
    x = xyz[0]
    y = xyz[1]
    z = xyz[2]
    # 计算得到球体的半径
    r = np.sqrt(x * x + y * y + z * z)
    # 半径与x轴的夹角
    r_x = np.arccos(y / r)
    # 半径在y轴的夹角
    r_y = np.arctan2(z, x)
    return [r, r_x, r_y]


def get_rotation_matrix(r_x, r_y):
    rot_x = np.asarray([[1, 0, 0], [0, np.cos(r_x), -np.sin(r_x)],
                        [0, np.sin(r_x), np.cos(r_x)]])
    rot_y = np.asarray([[np.cos(r_y), 0, np.sin(r_y)], [0, 1, 0],
                        [-np.sin(r_y), 0, np.cos(r_y)]])
    return rot_y.dot(rot_x)


def get_extrinsic(xyz):
    rvec = xyz_spherical(xyz)
    # 计算该相机位姿下的旋转矩阵和平移向量并拼接成T矩阵
    r = get_rotation_matrix(rvec[1], rvec[2])
    t = np.asarray([0, 0, 2]).transpose()
    trans = np.eye(4)
    trans[:3, :3] = r
    trans[:3, 3] = t
    return trans


def preprocess(model):
    min_bound = model.get_min_bound()
    max_bound = model.get_max_bound()
    center = min_bound + (max_bound - min_bound) / 2.0
    scale = np.linalg.norm(max_bound - min_bound) / 2.0
    vertices = np.asarray(model.vertices)
    vertices -= center
    model.vertices = o3d.utility.Vector3dVector(vertices / scale)
    return model


def voxel_carving(mesh, cubic_size, voxel_resolution, w=300, h=300):
    # 计算mesh的顶点法向量
    mesh.compute_vertex_normals()
    # 创建球体
    camera_sphere = o3d.geometry.TriangleMesh.create_sphere(radius=1.0,
                                                            resolution=10)

    # o3d.visualization.draw_geometries([camera_sphere], mesh_show_back_face=True)

    # Setup dense voxel grid.
    voxel_carving = o3d.geometry.VoxelGrid.create_dense(
        width=cubic_size,
        height=cubic_size,
        depth=cubic_size,
        voxel_size=cubic_size / voxel_resolution,
        origin=[-cubic_size / 2.0, -cubic_size / 2.0, -cubic_size / 2.0],
        color=[1.0, 0.7, 0.0])

    # Rescale geometry.
    camera_sphere = preprocess(camera_sphere)
    mesh = preprocess(mesh)

    # Setup visualizer to render depthmaps.
    vis = o3d.visualization.Visualizer()
    vis.create_window(width=w, height=h, visible=False)
    vis.add_geometry(mesh)
    vis.get_render_option().mesh_show_back_face = True
    ctr = vis.get_view_control()
    param = ctr.convert_to_pinhole_camera_parameters()

    # Carve voxel grid.
    centers_pts = np.zeros((len(camera_sphere.vertices), 3))
    for cid, xyz in enumerate(camera_sphere.vertices):
        # Get new camera pose.
        trans = get_extrinsic(xyz)
        param.extrinsic = trans
        c = np.linalg.inv(trans).dot(np.asarray([0, 0, 0, 1]).transpose())
        centers_pts[cid, :] = c[:3]
        # 转换相机的参数到成open3d中的相机内外参
        ctr.convert_from_pinhole_camera_parameters(param)

        # Capture depth image and make a point cloud.
        vis.poll_events()
        vis.update_renderer()
        # 根据当前的位姿来进行渲染拍摄得到深度图
        depth = vis.capture_depth_float_buffer(False)

        # Depth map carving method.
        voxel_carving.carve_depth_map(o3d.geometry.Image(depth), param)
        print("Carve view %03d/%03d" % (cid + 1, len(camera_sphere.vertices)))
    vis.destroy_window()

    return voxel_carving

"""
流程:
1 先创建一个固定大小的稠密(dense)voxleGrid对象
2 创建一个球形用于虚拟相机的位姿来拍摄拍摄深度图
3 根据拍摄得到的深度图与相机位姿使用carve_depth_map融合到dense voxelGrid中
"""
if __name__ == "__main__":
    mesh = o3d.io.read_triangle_mesh("DragonTriangleMesh.ply")
    cubic_size = 2.0
    voxel_resolution = 128.0

    carved_voxels = voxel_carving(mesh, cubic_size, voxel_resolution)
    print("Carved voxels ...")
    print(carved_voxels)
    o3d.visualization.draw_geometries([carved_voxels])

原因分析

1. 虚拟相机视角数量不足

代码中创建的camera_sphere使用resolution=10,生成的顶点数量过少(仅约320个顶点),导致虚拟相机的拍摄视角覆盖不全面,大量体素未被多视角深度图验证,最终被错误剔除。

2. 相机内参未匹配窗口尺寸

创建Visualizer时指定了w=300, h=300,但未显式设置相机内参(焦距、主点),默认内参可能与窗口尺寸不匹配,引发窗口尺寸警告,同时导致深度图的像素坐标与相机投影关系错位,雕刻逻辑失效。

3. 深度图获取未对齐

调用capture_depth_float_buffer(False)时未启用对齐模式,获取的深度图可能与当前窗口尺寸不完全匹配,进一步加剧内参和深度图的不匹配问题。

4. 相机与模型的空间位置匹配问题

预处理后模型被缩放到直径为2的立方体,而相机平移向量t = [0,0,2],相机与模型距离过近,可能导致模型部分区域超出相机视野,深度图无法完整捕捉模型信息。

解决建议

1. 增加相机视角数量

将camera_sphere的resolution调整为20或更高,例如:

camera_sphere = o3d.geometry.TriangleMesh.create_sphere(radius=1.0, resolution=20)

更多的顶点意味着更多拍摄视角,能更全面地覆盖模型表面。

2. 显式设置相机内参

在创建Visualizer后,手动设置与窗口尺寸匹配的内参,确保投影关系正确:

param = ctr.convert_to_pinhole_camera_parameters()
# 设置焦距和主点,这里焦距可根据需求调整,示例用500
param.intrinsic.set_intrinsics(w, h, 500, 500, w/2, h/2)
ctr.convert_from_pinhole_camera_parameters(param)

3. 启用深度图对齐

修改深度图获取代码,启用对齐模式:

depth = vis.capture_depth_float_buffer(True)

确保深度图与窗口尺寸完全对齐,解决尺寸不匹配警告。

4. 调整相机与模型的相对位置

可以适当增大相机与模型的距离,例如将t向量的z值调整为3:

t = np.asarray([0, 0, 3]).transpose()

确保模型完整处于相机视野内,提升深度图的完整性。

5. 验证模型加载正确性

在预处理前添加模型可视化代码,确认模型加载完整:

o3d.visualization.draw_geometries([mesh])

排除模型文件损坏或加载错误的可能。

内容的提问来源于stack exchange,提问作者user23620257

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 07:18:11