从深度图到 3D 网格与点云:完整实现
在计算机视觉与三维重建中,深度图提供了对场景中每个像素与相机之间距离的精确测量。结合深度图,我们可以将场景从 2D 图像转换为 3D 空间表示。通常,有两种常见的 3D 表示方法:点云 和网格(Mesh)。点云由散布在三维空间中的一组顶点组成,而网格通过连接顶点形成三角形面片,从而表示一个更完整的 3D 对象。
在本文中,我们将介绍如何从深度图生成点云和网格,并展示如何使用深度学习模型(如 MiDaS)进行深度图预测,以及如何处理和可视化生成的 3D 数据。
1. 深度图到 3D 点云
1.1 什么是点云?
点云是由多个点(每个点通常包含 x、y 和 z 坐标)构成的数据集,表示物体表面或场景的三维结构。在计算机视觉中,点云是由深度图或 LiDAR 等传感器生成的。点云提供了物体的几何形状,但通常没有连接点的三角面片信息,因此不像网格那样具有表面。
1.2 生成点云
生成点云的关键步骤是将深度图中的每个像素的深度值转换为对应的三维坐标,然后将这些坐标作为点云数据。假设我们已经获得了深度图及相机的内参,我们可以根据以下公式计算每个像素的 3D 坐标:
( X = ( x − c x ) ⋅ Z f x ) ( X = \frac{(x - c_x) \cdot Z}{f_x} ) (X=fx(x−cx)⋅Z)
( Y = ( y − c y ) ⋅ Z f y ) ( Y = \frac{(y - c_y) \cdot Z}{f_y} ) (Y=fy(y−cy)⋅Z)
其中, ( Z ) 是深度图中每个像素的深度值, f x f_x fx和 f y f_y fy是相机的焦距, c x c_x cx和 c y c_y cy是相机的光心坐标。
1.3 代码实现:深度图到点云
以下代码示例展示了如何将深度图转换为点云,并使用 Open3D
库进行可视化。
python
import numpy as np
import open3d as o3d
import cv2
def depth_to_point_cloud(depth_image, intrinsics):
"""
将深度图转换为点云
:param depth_image: 深度图 (2D NumPy 数组)
:param intrinsics: 相机内参 (Camera Intrinsics)
:return: 点云 (open3d.geometry.PointCloud)
"""
height, width = depth_image.shape
# 获取相机内参
fx, fy, cx, cy = intrinsics['fx'], intrinsics['fy'], intrinsics['cx'], intrinsics['cy']
# 生成像素坐标网格
x_coords, y_coords = np.meshgrid(np.arange(width), np.arange(height))
# 根据深度图计算对应的3D坐标
z_coords = depth_image / 1000.0 # 假设深度单位是毫米,转换为米
x_coords = (x_coords - cx) * z_coords / fx
y_coords = (y_coords - cy) * z_coords / fy
# 将 3D 坐标转化为点 (x, y, z)
points = np.stack((x_coords, y_coords, z_coords), axis=-1)
# 扁平化点云
points = points.reshape((-1, 3))
# 创建 Open3D 点云对象
point_cloud = o3d.geometry.PointCloud()
point_cloud.points = o3d.utility.Vector3dVector(points)
return point_cloud
# 假设你有深度图数据
depth_image = np.load('depth_image.npy') # 这里是示例数据,深度图应为 numpy 数组
# 相机内参(假设为已知)
intrinsics = {
'fx': 525.0, # x轴焦距
'fy': 525.0, # y轴焦距
'cx': 319.5, # 光心x坐标
'cy': 239.5 # 光心y坐标
}
# 将深度图转换为点云
point_cloud = depth_to_point_cloud(depth_image, intrinsics)
# 可视化生成的点云
o3d.visualization.draw_geometries([point_cloud])
在这个示例中,我们将深度图中的每个像素的深度值映射到三维空间中的坐标,然后将这些坐标作为点云对象展示。Open3D
提供了一个简单的接口来显示生成的点云。
2. 深度图到 3D 网格
相比于点云,网格通过将多个相邻的顶点连接成三角形面片,构建了一个完整的 3D 表面。我们将在这里介绍如何从深度图生成网格,而不使用点云。
2.1 生成网格
与生成点云相似,我们仍然需要将深度图的每个像素转换为 3D 坐标。接着,我们需要通过连接这些顶点形成三角形面片,以构建网格。
2.2 代码实现:深度图到网格
python
def depth_to_mesh(depth_image, intrinsics):
"""
将深度图转为网格
:param depth_image: 深度图 (2D NumPy 数组)
:param intrinsics: 相机内参 (Camera Intrinsics)
:return: 网格 (open3d.geometry.TriangleMesh)
"""
height, width = depth_image.shape
# 获取相机内参
fx, fy, cx, cy = intrinsics['fx'], intrinsics['fy'], intrinsics['cx'], intrinsics['cy']
# 生成像素坐标网格
x_coords, y_coords = np.meshgrid(np.arange(width), np.arange(height))
# 根据深度图计算对应的3D坐标
z_coords = depth_image / 1000.0 # 假设深度单位是毫米,转换为米
x_coords = (x_coords - cx) * z_coords / fx
y_coords = (y_coords - cy) * z_coords / fy
# 将 3D 坐标转化为顶点 (x, y, z)
vertices = np.stack((x_coords, y_coords, z_coords), axis=-1)
# 扁平化顶点
vertices = vertices.reshape((-1, 3))
# 构建三角形面片
triangles = []
for i in range(height - 1):
for j in range(width - 1):
idx1 = i * width + j
idx2 = i * width + (j + 1)
idx3 = (i + 1) * width + j
idx4 = (i + 1) * width + (j + 1)
# 两个三角形,构成一个矩形面
triangles.append([idx1, idx2, idx3])
triangles.append([idx2, idx4, idx3])
triangles = np.array(triangles)
# 生成 Open3D 网格
mesh = o3d.geometry.TriangleMesh()
mesh.vertices = o3d.utility.Vector3dVector(vertices)
mesh.triangles = o3d.utility.Vector3iVector(triangles)
return mesh
# 将深度图转换为网格
mesh = depth_to_mesh(depth_image, intrinsics)
# 可视化生成的网格
o3d.visualization.draw_geometries([mesh])
该代码通过遍历深度图中的像素,计算每个像素对应的三维坐标,并通过相邻像素之间的连接构建网格。通过 Open3D
可视化,我们可以查看生成的 3D 网格。
3. 总结
本文介绍了如何从深度图生成 点云 和 网格,并展示了如何使用深度学习模型(如 MiDaS)预测深度图。具体步骤如下:
- 深度图到点云 :将深度图的每个像素映射为 3D 坐标,并通过
Open3D
可视化点云。 - 深度图到网格 :将每个像素的 3D 坐标连接成三角形面片,构建网格,并通过
Open3D
可视化。
通过这些方法,我们可以从深度图中直接生成 3D 数据,并进行各种应用,如环境建模、机器人导航、增强现实等。
希望本文能为你提供有用的工具与思路!