如何基于图像、相机内参及Euclidean距离形式的深度图生成点云?
如何基于图像、相机内参及Euclidean距离形式的深度图生成点云?
嗨,我完全懂你现在的困惑——当深度图给出的是相机到3D点的欧氏距离(也就是空间中两点的直线距离),而不是相机坐标系下的轴向深度(沿着光轴的距离)时,常规的点云转换逻辑确实会失效,而且仓库里的那段代码确实有问题,咱们一步步理清楚:
先搞懂核心区别:欧氏距离 vs 相机轴向深度
常规的点云生成逻辑里,深度图的数值是相机坐标系下的Z值——也就是3D点沿着相机光轴(垂直于成像平面的方向)到相机中心的距离。但你手里的深度图是相机中心到3D点的直线距离(记为D),这俩只有当点正好在光轴上时才相等,其他位置D都比Z大。
正确的转换公式推导
咱们从相机成像模型出发:
像素坐标(u, v)和相机坐标系下的3D点(X, Y, Z)(Z是轴向深度)的关系是:
u = (f_x * X / Z) + c_x v = (f_y * Y / Z) + c_y
其中f_x、f_y是相机内参的焦距,c_x、c_y是主点坐标(对应你相机矩阵里的[0][0]、[1][1]、[0][2]、[1][2])。
已知欧氏距离D满足:D² = X² + Y² + Z²,把上面的X、Y用Z表示代入:
X = (u - c_x) * Z / f_x Y = (v - c_y) * Z / f_y
代入D的公式后整理,就能解出真实的轴向深度Z:
Z = D / sqrt( ((u - c_x)/f_x)² + ((v - c_y)/f_y)² + 1 )
得到Z之后,再代入X、Y的表达式,就能算出相机坐标系下的3D点(X, Y, Z)了。
为什么仓库里的代码不对?
仓库里的代码直接把深度图D当成Z来计算:
real_x = (x - camera_matrix[0][2]) / camera_matrix[0, 0] real_y = (y - self.camera_matrix[1][2]) / camera_matrix[1, 1] point_cloud = np.stack( (np.multiply(real_x, depth_map), np.multiply(real_y, depth_map), depth_map) )
这相当于默认了Z = D,只有当点在光轴上(u=c_x, v=c_y)时才成立,其他位置算出来的X、Y、Z都会偏离真实的3D坐标,结果自然不对。
正确的代码实现
用NumPy写的话,完整的转换逻辑如下:
import numpy as np def convert_euclidean_depth_to_point_cloud(depth_map, camera_matrix): h, w = depth_map.shape # 提取相机内参的关键参数 f_x = camera_matrix[0, 0] f_y = camera_matrix[1, 1] c_x = camera_matrix[0, 2] c_y = camera_matrix[1, 2] # 生成所有像素的坐标网格 pixel_x, pixel_y = np.meshgrid(np.arange(w), np.arange(h)) # 计算相对于主点的归一化坐标偏移 norm_x = (pixel_x - c_x) / f_x norm_y = (pixel_y - c_y) / f_y # 计算轴向深度Z denom = np.sqrt(norm_x ** 2 + norm_y ** 2 + 1) Z = depth_map / denom # 计算相机坐标系下的X、Y X = norm_x * Z Y = norm_y * Z # 堆叠成点云,shape为(3, h, w),也可以展平成(N, 3)的形式方便后续处理 point_cloud = np.stack((X, Y, Z), axis=0) point_cloud_flat = point_cloud.reshape(3, -1).T # 转成N个点的格式 return point_cloud, point_cloud_flat
最后总结
核心就是区分开欧氏距离D和相机轴向深度Z,通过推导的公式把D转换成真实的Z,再计算X、Y,这样得到的点云才是准确的相机坐标系下的3D点。
备注:内容来源于stack exchange,提问作者SashaDance
相关产品推荐
相关产品推荐

