深度图转点云是三维视觉领域非常基础且常用的操作,很多AI相关的三维任务都会用到这个转换流程。下面我们先看一张转换效果的示意图:

核心原理:从二维深度到三维坐标
深度图本身是二维图像,每个像素点存储的是该位置对应物体到相机的直线距离,默认单位是毫米或者米。要把它转成三维点云,核心是把每个像素的二维图像坐标,结合深度值和相机内参,映射到三维世界坐标系中。
这里需要先明确几个关键概念:
- 相机内参:包含相机的焦距、主点坐标等参数,是二维坐标转三维坐标的核心依据,一般通过相机标定得到。
- 深度值:深度图中每个像素对应的数值,代表该像素对应点到相机光心的距离。
- 像素坐标:深度图中每个点的位置,用(u, v)表示,u是列索引,v是行索引。
具体转换步骤
第一步:准备输入数据
首先需要拿到两个核心输入:一是待转换的深度图,二是对应相机的标定内参。深度图可以是AI模型输出的预测深度图,也可以是深度相机采集的真实深度图,格式一般是单通道的灰度图或者数值矩阵。相机内参一般是一个3x3的矩阵,结构如下:
| 参数 | 含义 |
|---|---|
| fx | x轴方向焦距,单位通常是像素 |
| fy | y轴方向焦距,单位通常是像素 |
| cx | 图像主点的x坐标,一般是图像宽度的一半 |
| cy | 图像主点的y坐标,一般是图像高度的一半 |
第二步:遍历深度图像素,计算三维坐标
对深度图中的每个有效像素(深度值大于0的像素),按照下面的公式把二维像素坐标转换成相机坐标系下的三维坐标:
相机坐标系下的X坐标:X = (u - cx) * depth / fx
相机坐标系下的Y坐标:Y = (v - cy) * depth / fy
相机坐标系下的Z坐标:Z = depth
这里的depth是当前像素对应的深度值,需要根据深度图的存储单位做换算,比如如果深度图存储的是毫米单位的整数,而你需要米为单位的点云,就需要先把depth除以1000。
第三步:整理输出点云
把每个像素计算得到的(X, Y, Z)坐标收集起来,就可以得到完整的点云数据。如果需要带颜色的点云,还可以把深度图对应像素的RGB值取出来,和点坐标绑定存储。
Python实现示例
下面给出基于OpenCV和NumPy的完整转换代码,你可以直接替换自己的深度图和相机内参运行:
import cv2
import numpy as np
def depth_to_pointcloud(depth_map, camera_intrinsics, depth_scale=1.0):
"""
深度图转点云函数
:param depth_map: 输入深度图,单通道矩阵,深度值单位可以是任意,通过depth_scale缩放
:param camera_intrinsics: 相机内参矩阵,3x3的numpy数组
:param depth_scale: 深度缩放系数,比如深度图存的是毫米,要转米就设为0.001
:return: 点云数组,shape为(N, 3),N是有效点数量
"""
# 提取相机内参
fx = camera_intrinsics[0, 0]
fy = camera_intrinsics[1, 1]
cx = camera_intrinsics[0, 2]
cy = camera_intrinsics[1, 2]
# 获取深度图的高和宽
height, width = depth_map.shape
# 生成所有像素的u和v坐标网格
u, v = np.meshgrid(np.arange(width), np.arange(height))
u = u.flatten()
v = v.flatten()
# 取出所有像素的深度值,缩放后得到有效深度
depth = depth_map.flatten() * depth_scale
# 过滤掉深度为0的无效点
valid_mask = depth > 0
u = u[valid_mask]
v = v[valid_mask]
depth = depth[valid_mask]
# 计算相机坐标系下的三维坐标
X = (u - cx) * depth / fx
Y = (v - cy) * depth / fy
Z = depth
# 组合成点云数组
pointcloud = np.stack([X, Y, Z], axis=-1)
return pointcloud
# 示例用法
if __name__ == "__main__":
# 读取深度图,这里假设是16位单通道深度图
depth_img = cv2.imread("depth_map.png", cv2.IMREAD_UNCHANGED)
# 定义相机内参,这里以常见参数示例,实际使用时替换成自己的标定结果
intrinsics = np.array([
[500.0, 0.0, 320.0],
[0.0, 500.0, 240.0],
[0.0, 0.0, 1.0]
])
# 深度缩放系数,假设深度图存的是毫米,转成米
scale = 0.001
# 转换点云
pc = depth_to_pointcloud(depth_img, intrinsics, scale)
print(f"生成点云数量:{pc.shape[0]}")
print(f"前5个点坐标:\n{pc[:5]}")常见问题说明
很多人在转换时会遇到点云错位的问题,大部分原因是相机内参不匹配,或者深度值的单位没有和缩放系数对应上。如果使用的是AI模型输出的深度图,还要注意模型输出的深度范围是0到1还是实际的物理距离,避免深度值缩放错误。
另外如果深度图有噪声,转换后的点云也会有大量噪点,这时候可以先对深度图做高斯模糊或者中值滤波,再转换点云,能有效提升点云质量。