基于Open3D的点云处理12-体素化

体素化Voxelization

体素(voxel)是像素(pixel)、体积(volume)和元素(element)的组合词,相当于3D空间中的像素;

体素化是通过用空间均匀大小的体素网格(voxel grid)来模拟模型或者点云的几何形态的过程; 表示3D模型的体素跟表示2D图像的像素相似,只不过从二维的点扩展到三维的立方体单元。体素化能够对模型进行简化,得到均匀一致的网格,在求模型的切片,物理仿真分析过程中有较好的应用。

实现模型体素化的方式有很多,比如基于八叉树的三模网格模型体素化,基于GPU并利用渲染管线中fragment shader部分实现的栅格化插值。

体素化优点

  • 点云数据将在内存中有序存储;
  • 数据有序存储和降采样,能够处理大规模的数据3、可以将二维的技术用到三维上
    体;

体素化缺点

  • 信息丢失,与分辨率有关;
  • 内存占用与分辨率有关;
  • 稀疏的点云体素化会构建很多空体素, 若不采用稀疏卷积, 将有大量的无意义运算,降低运算效率

算法流程:

  • 采用模型的AABB包围盒构建体素空间 N 3 N^3 N3;
  • 根据分辨率 N N N将整个AABB包围盒按照长宽高划分为NxNxN个体素cell;
  • 标记每个cell与三维模型的关系(在模型内部,在模型外部,在模型上);(对于表面体素空间,每个体素cell的flag则只需分辨与模型是相交还是分离即可,不必再区分实在模型内部还是外部);

测试用例

参考:http://www.open3d.org/docs/latest/tutorial/geometry/voxelization.html

Open3d中从三角网构建体素

接口 1: 从给定的TriangleMesh 创建VoxelGrid。创建的VoxelGrid 的边界是根据TriangleMesh 计算的。
在这里插入图片描述

接口2: 从给定的TriangleMesh 创建VoxelGrid。创建的VoxelGrid 的边界是根据输入参数确定的。
在这里插入图片描述
接口测试:

# 从三角网构建体素
import open3d as o3d
import numpy as np

mesh = o3d.io.read_triangle_mesh("data//BunnyMesh.ply")
# 缩放到单位尺寸
mesh.scale(1 / np.max(mesh.get_max_bound() - mesh.get_min_bound()), center=mesh.get_center())
mesh.compute_vertex_normals()
o3d.visualization.draw_geometries([mesh], window_name="mesh",
                                  mesh_show_back_face=False)  # 显示mesh


voxel_grid = o3d.geometry.VoxelGrid.create_from_triangle_mesh(mesh, voxel_size=0.01)
o3d.visualization.draw_geometries([voxel_grid], window_name="体素",
                                  mesh_show_back_face=False)  # 显示体素

mesh
体素

Open3d中从点云构建体素

**接口1:**从给定的 PointCloud 创建 VoxelGrid。给定体素的颜色值是落入其中的点的平均颜色值(如果PointCloud 有颜色)。创建的 VoxelGrid 的边界是根据 PointCloud 计算的。
在这里插入图片描述

**接口2:**从给定的 PointCloud 创建 VoxelGrid。给定体素的颜色值是落入其中的点的平均颜色值(如果PointCloud 有颜色)。创建的VoxelGrid 的边界是根据输入参数确定的。
在这里插入图片描述
接口测试:

# 从点云构建体素

import open3d as o3d
import numpy as np

mesh = o3d.io.read_triangle_mesh("data//BunnyMesh.ply")

N = 5000
pcd = mesh.sample_points_poisson_disk(N)
# fit to unit cube
pcd.scale(1 / np.max(pcd.get_max_bound() - pcd.get_min_bound()), center=pcd.get_center())
pcd.colors = o3d.utility.Vector3dVector(np.random.uniform(0, 1, size=(N, 3)))
o3d.visualization.draw_geometries([pcd])

voxel_grid = o3d.geometry.VoxelGrid.create_from_point_cloud(pcd, voxel_size=0.05)
o3d.visualization.draw_geometries([voxel_grid])

在这里插入图片描述
在这里插入图片描述

体素内外测试

接口:
在这里插入图片描述

测试:

# 从点云构建体素
import open3d as o3d
import numpy as np

mesh = o3d.io.read_triangle_mesh("data//BunnyMesh.ply")

N = 5000
pcd = mesh.sample_points_poisson_disk(N)
# fit to unit cube
pcd.scale(1 / np.max(pcd.get_max_bound() - pcd.get_min_bound()), center=pcd.get_center())
pcd.colors = o3d.utility.Vector3dVector(np.random.uniform(0, 1, size=(N, 3)))
# o3d.visualization.draw_geometries([pcd])

voxel_grid = o3d.geometry.VoxelGrid.create_from_point_cloud(pcd, voxel_size=0.05)
# o3d.visualization.draw_geometries([voxel_grid])

# 测试体素内外
queries = np.asarray(pcd.points)
# print(queries)
queries = np.row_stack((range(1,4), queries))
output = voxel_grid.check_if_included(o3d.utility.Vector3dVector(queries))
print(output[:10])

结果:[False, True, True, True, True, True, True, True, True, True]

雕刻一个体素(solid)

方法create_from_point_cloud和create_from_triangle_mesh只能够在几何体的表面创造体素网格。然而从大量的深度图或者轮廓中雕刻一个体素网格是有可能的。Open3d提供了carve_depth_map和 carve_silhouette方法用于体素雕刻。
测试用例:首先从几何图形渲染深度图并使用这些深度图雕刻密集体素网格的用法。结果是给定形状的填充体素网格。


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)
    r_x = np.arccos(y / r)
    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)
    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,
                  use_depth=True,
                  surface_method='pointcloud'):
    mesh.compute_vertex_normals()
    camera_sphere = o3d.geometry.TriangleMesh.create_sphere()

    # 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
    pcd_agg = o3d.geometry.PointCloud()
    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]
        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)
        pcd_agg += o3d.geometry.PointCloud.create_from_depth_image(
            o3d.geometry.Image(depth),
            param.intrinsic,
            param.extrinsic,
            depth_scale=1)

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

    # add voxel grid survace
    print('Surface voxel grid from %s' % surface_method)
    if surface_method == 'pointcloud':
        voxel_surface = o3d.geometry.VoxelGrid.create_from_point_cloud_within_bounds(
            pcd_agg,
            voxel_size=cubic_size / voxel_resolution,
            min_bound=(-cubic_size / 2, -cubic_size / 2, -cubic_size / 2),
            max_bound=(cubic_size / 2, cubic_size / 2, cubic_size / 2))
    elif surface_method == 'mesh':
        voxel_surface = o3d.geometry.VoxelGrid.create_from_triangle_mesh_within_bounds(
            mesh,
            voxel_size=cubic_size / voxel_resolution,
            min_bound=(-cubic_size / 2, -cubic_size / 2, -cubic_size / 2),
            max_bound=(cubic_size / 2, cubic_size / 2, cubic_size / 2))
    else:
        raise Exception('invalid surface method')
    voxel_carving_surface = voxel_surface + voxel_carving

    return voxel_carving_surface, voxel_carving, voxel_surface

if __name__ == "__main__":
    armadillo = o3d.data.ArmadilloMesh()
    mesh = o3d.io.read_triangle_mesh(armadillo.path)

    visualization = True
    cubic_size = 2.0
    voxel_resolution = 128.0

    voxel_grid, voxel_carving, voxel_surface = voxel_carving(
        mesh, cubic_size, voxel_resolution)

    print("surface voxels")
    print(voxel_surface)
    o3d.visualization.draw_geometries([voxel_surface])

    print("carved voxels")
    print(voxel_carving)
    o3d.visualization.draw_geometries([voxel_carving])

    print("combined voxels (carved + surface)")
    print(voxel_grid)
    o3d.visualization.draw_geometries([voxel_grid])

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

从体素中构建八叉树

Open3D从 VoxelGrid几何构造八叉树create_from_voxel_grid。输入的每个体素都VoxelGrid被视为 3D 空间中的一个点,其坐标对应于体素的原点。
接口:

octree = o3d.geometry.Octree(max_depth=4)
octree.create_from_voxel_grid(voxel_grid)

测试:


import open3d as o3d
import numpy as np

mesh = o3d.io.read_triangle_mesh("data//BunnyMesh.ply")

N = 5000
pcd = mesh.sample_points_poisson_disk(N)
# fit to unit cube
pcd.scale(1 / np.max(pcd.get_max_bound() - pcd.get_min_bound()), center=pcd.get_center())
pcd.colors = o3d.utility.Vector3dVector(np.random.uniform(0, 1, size=(N, 3)))
# o3d.visualization.draw_geometries([pcd])

# 构建八叉树分布
voxel_grid = o3d.geometry.VoxelGrid.create_from_point_cloud(pcd,
                                                            voxel_size=0.05)
o3d.visualization.draw_geometries([voxel_grid])

octree = o3d.geometry.Octree(max_depth=4)
octree.create_from_voxel_grid(voxel_grid)
o3d.visualization.draw_geometries([octree])

在这里插入图片描述
在这里插入图片描述

体素下采样

接口:

downpcd = pcd.voxel_down_sample(voxel_size=0.01)

测试:

# 体素下采样
import open3d as o3d
pcd = o3d.io.read_point_cloud("data//bunny.pcd")
o3d.visualization.draw_geometries([pcd])
downpcd = pcd.voxel_down_sample(voxel_size=0.01)
o3d.visualization.draw_geometries([downpcd])

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/51528.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C++】开源:grpc远程过程调用(RPC)配置与使用

😏★,:.☆( ̄▽ ̄)/$:.★ 😏 这篇文章主要介绍grpc远程过程调用(RPC)配置与使用。 无专精则不能成,无涉猎则不能通。。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜…

【编程规范】一文讲解开发中的命名规范

命名规范 好的代码本身就是注释, 所以我们需要统一命名风格。 ​ 在本文中,将从大到小,从外到内,总结Java编程中的命名规范。文中将会涉及到日常工作中常见的命名示例,如包命名,类命名,接口命名&#xff0c…

页面生成图片或PDF node-egg

没有特别的幸运,那么就特别的努力!!! 中间件:页面生成图片 node-egg 涉及到技术node egg Puppeteer 解决文书智能生成多样化先看效果环境准备初始化项目 目录结构核心代码 完整代码https://gitee.com/hammer1010_ad…

数组中出现次数超过一半的数字——剑指 Offer 39

文章目录 题目描述法一 哈希表法二 摩尔投票 题目描述 法一 哈希表 使用哈希映射(HashMap)来存储每个元素以及出现的次数。对于哈希映射中的每个键值对,键表示一个元素,值表示该元素出现的次数。 class Solution { public:int maj…

【Python机器学习】实验03 logstic回归

文章目录 简单分类模型 - 逻辑回归1.1 准备数据1.2 定义假设函数Sigmoid 函数 1.3 定义代价函数1.4 定义梯度下降算法gradient descent(梯度下降) 1.5 绘制决策边界1.6 计算准确率1.7 试试用Sklearn来解决2.1 准备数据(试试第二个例子)2.2 假设函数与前h相同2.3 代价函数与前相…

【语音识别】- 声学,词汇和语言模型

一、说明 语音识别是指计算机通过处理人类语言的音频信号,将其转换为可理解的文本形式的技术。也就是说,它可以将人类的口语语音转换为文本,以便计算机能够进一步处理和理解。它是自然语言处理技术的一部分,被广泛应用于语音识别助…

Linux 之 systemctl

systemctl 可以控制软件(一般指服务)的启动、关闭、开机自启动 能被systemctl 管理的软件,一般也称 服务 系统内置服务均可被 systemctl 控制第三方软件,如果 自动注册了 可被systemctl 控制第三方软件,如果没有自动…

better scoll右 联左

这是先拿一个数组装进我们所有 获取到的dom节点的 高度 因为算的 都是 到最上面的 高度,所以我们 要减去他的 高度 就得到自身的高度 然后给右边加一个滚动事件,得到每一次滑动的高度,在循环上面的数组,就是我们右边的 y就在算出…

微信小程序实现日历功能、日历转换插件、calendar

文章目录 演示htmlJavaScript 演示 效果图 微信小程序实现交互 html <view wx:if"{{calendarArr.length}}"><view class"height_786 df_fdc_aic"><view class"grid_c7_104"><view class"font_weight_800 text_align…

Debezium日常分享系列之:定制Debezium 信号发送和通知

Debezium日常分享系列之&#xff1a;定制Debezium 信号发送和通知 一、自定义信号和通知通道二、结论 Debezium 2.3 在信号和通知功能方面引入了新的改进。除了 Debezium 提供的预定义信号和通知通道之外&#xff0c;您还可以设置新的信号和通知通道。此功能使用户能够自定义系…

微服务——服务异步通讯RabbitMQ

前置文章 消息队列——RabbitMQ基本概念容器化部署和简单工作模式程序_北岭山脚鼠鼠的博客-CSDN博客 消息队列——rabbitmq的不同工作模式_北岭山脚鼠鼠的博客-CSDN博客 消息队列——spring和springboot整合rabbitmq_北岭山脚鼠鼠的博客-CSDN博客 目录 Work queues 工作队列…

JS——输入输出语法数组的操作

JavaScript输入输出语法 目标&#xff1a;能写出常见的JavaScript输入输出语法 输出语法 语法1&#xff1a; document.write(要输出的内容)作用&#xff1a; 向body内输出内容 注意&#xff1a; 如果输出的内容写的是标签&#xff0c;也会被解析成网页元素 语法2&#xff1a…

Verilog语法学习——LV9_使用子模块实现三输入数的大小比较

LV9_使用子模块实现三输入数的大小比较 题目来源于牛客网 [牛客网在线编程_Verilog篇_Verilog快速入门 (nowcoder.com)](https://www.nowcoder.com/exam/oj?page1&tabVerilog篇&topicId301) 题目 描述 在数字芯片设计中&#xff0c;通常把完成特定功能且相对独立的…

特殊矩阵的压缩存储

1 数组的存储结构 1.1 一维数组 各数组元素大小相同&#xff0c;且物理上连续存放。第i个元素的地址位置是&#xff1a;a[i] LOC i*sizeof(ElemType) (LOC为起始地址) 1.2 二维数组 对于多维数组有行优先、列优先的存储方法 行优先&#xff1a;先行后列&#xff0c;先存储…

无涯教程-jQuery - Select menu组件函数

小部件选择菜单功能可与JqueryUI中的小部件一起使用&#xff0c;它提供了可替换样式的选择元素。一个简单的选择菜单如下所示。 Select menu - 语法 $( "#menu" ).selectmenu(); Select menu - 示例 以下是显示选择菜单用法的简单示例- <!doctype html> &…

自动驾驶感知系统-全球卫星定位系统

卫星定位系统 车辆定位是让无人驾驶汽车获取自身确切位置的技术&#xff0c;在自动驾驶技术中定位担负着相当重要的职责。车辆自身定位信息获取的方式多样&#xff0c;涉及多种传感器类型与相关技术。自动驾驶汽车能够持续安全可靠运行的一个关键前提是车辆的定位系统必须实时…

Go语言进阶 + 依赖管理

依赖配置 - version开始&#xff0c;就开始很难听懂了&#xff0c;需要结合很多课后配套资料查阅很多文档和网站....然而好像没有那么多时间&#xff0c;一天给3小时学Go真的顶天了.....还有算法和Linux的Mysql... 这几天学Go已经把算法给挤掉了.....下步要权衡一下&#xff0c…

Centos7中实现脚本使用mysqldump实现分库分表备份

脚本 #!/bash/bin userroot #用户名 password123456 #密码 back_path/backup/db databases_file/backup/databases.list [ -f $databases_file ] || touch /backup/databases.list if [[ ! -s ${databases_file} ]] then while read line do[ -d ${back_path}/$line ] …

ERROR in unable to locate ‘***/public/**/*‘ glob

前提 自己搭了一个react项目的脚手架&#xff0c;npm包下载一切都很正常&#xff0c;启动的时候突然就报ERROR in unable to locate ***/public/**/* glob这个错误&#xff0c;根据百度分析了一下产生的原因&#xff1a;webpack配置文件中的CopyWebpackPlugin导致的 网上给出的…

C语言指针应该这么学?

数组名的意义&#xff1a; 1. sizeof(数组名)&#xff0c;这里的数组名表示整个数组&#xff0c;计算的是整个数组的大小。 2. &数组名&#xff0c;这里的数组名表示整个数组&#xff0c;取出的是整个数组的地址。 3. 除此之外所有的数组名都表示首元素的地址。 根据以上数…