4. 深度图与彩色图对齐
任务目标
将ToF相机输出的深度图与RGB相机输出的彩色图进行像素级对齐,生成同一视角下深度与颜色一一对应的图像对,为后续点云着色或三维重建提供对齐后的输入数据。
核心原理与实现思路
深度图与彩色图对齐的实质是:利用两个相机各自的内参以及二者之间的外参(旋转矩阵R、平移向量t),将深度图中的每个像素反投影到三维空间,再通过外参变换到RGB相机坐标系下,最后用RGB相机内参投影到彩色图像平面,得到该深度像素对应的彩色像素坐标。由于投影坐标通常为浮点数,需通过双线性插值采样彩色值,并对超出彩色图边界的深度像素做无效标记。
依赖与环境
- Python 3.8+,NumPy,OpenCV(用于图像读写与插值)
- 输入数据:深度图(16bit PNG,单位mm)、彩色图(8bit BGR)、相机参数文件(JSON格式)
关键完整代码
import numpy as np
import cv2
import json
def align_depth_to_color(depth, color, K_depth, K_color, R, t):
"""
将深度图对齐到彩色图视角
depth: (Hd, Wd) uint16, 单位mm
color: (Hc, Wc, 3) uint8, BGR
K_depth, K_color: 3x3 内参矩阵
R: 3x3, 从深度相机到彩色相机的旋转
t: (3,), 从深度相机到彩色相机的平移
返回: aligned_depth (Hc, Wc) float32, 无效像素为0
aligned_color (Hc, Wc, 3) uint8, 无效像素为黑色
"""
Hd, Wd = depth.shape
Hc, Wc = color.shape[:2]
# 1. 生成深度图像素网格
v, u = np.meshgrid(np.arange(Hd), np.arange(Wd), indexing='ij')
z = depth.astype(np.float32) / 1000.0 # mm -> m
valid = z > 0
# 2. 反投影到深度相机三维坐标
x = (u - K_depth[0, 2]) * z / K_depth[0, 0]
y = (v - K_depth[1, 2]) * z / K_depth[1, 1]
pts_depth = np.stack([x, y, z], axis=-1) # (Hd, Wd, 3)
# 3. 变换到彩色相机坐标系
pts_color = pts_depth @ R.T + t # (Hd, Wd, 3)
# 4. 投影到彩色图像平面
xc = pts_color[..., 0] / pts_color[..., 2]
yc = pts_color[..., 1] / pts_color[..., 2]
uc = xc * K_color[0, 0] + K_color[0, 2]
vc = yc * K_color[1, 1] + K_color[1, 2]
# 5. 判断投影后是否在彩色图范围内
in_bounds = (uc >= 0) & (uc < Wc - 1) & (vc >= 0) & (vc < Hc - 1) & valid & (pts_color[..., 2] > 0)
# 6. 双线性插值采样彩色值
map_x = uc.astype(np.float32)
map_y = vc.astype(np.float32)
sampled_color = cv2.remap(color, map_x, map_y, interpolation=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT, borderValue=(0, 0, 0))
# 7. 生成对齐后的深度图(在彩色图分辨率下)
aligned_depth = np.zeros((Hc, Wc), dtype=np.float32)
# 使用最近邻将深度值映射到彩色分辨率
uc_int = np.round(uc).astype(int)
vc_int = np.round(vc).astype(int)
mask = in_bounds & (uc_int >= 0) & (uc_int < Wc) & (vc_int >= 0) & (vc_int < Hc)
aligned_depth[vc_int[mask], uc_int[mask]] = depth[mask]
# 无效区域置零
aligned_color = sampled_color.copy()
aligned_color[~in_bounds] = 0
return aligned_depth, aligned_color
# ========== 使用示例 ==========
if __name__ == "__main__":
# 加载相机参数(示例结构,实际以标定结果为准)
with open("camera_params.json") as f:
params = json.load(f)
K_depth = np.array(params["K_depth"])
K_color = np.array(params["K_color"])
R = np.array(params["R"])
t = np.array(params["t"])
# 读取图像
depth = cv2.imread("depth.png", cv2.IMREAD_UNCHANGED)
color = cv2.imread("color.png", cv2.IMREAD_COLOR)
aligned_depth, aligned_color = align_depth_to_color(depth, color, K_depth, K_color, R, t)
# 保存结果
cv2.imwrite("aligned_depth.png", (aligned_depth * 1000).astype(np.uint16))
cv2.imwrite("aligned_color.png", aligned_color)
运行与验证
- 准备数据:将深度图、彩色图及标定得到的相机参数(内参、外参)放在同一目录下,按代码中路径命名。
- 执行:运行脚本
python align_depth_color.py,生成aligned_depth.png与aligned_color.png。 - 验证方法:用图像查看工具打开两张对齐结果,检查以下两点:
- 对齐后的彩色图中,物体边缘与深度图边缘是否基本重合(无严重错位);
- 在深度有效区域,彩色图与深度图内容是否对应同一场景视角。
- 定量验证(可选):选取场景中一个已知距离的平面区域,检查对齐后深度值是否与真实距离一致(误差应在传感器标称精度范围内)。
常见问题
- 对齐后出现黑色条纹:通常由深度图无效像素(值为0)或投影超出彩色图边界导致,属正常现象,可通过深度图预处理(如空洞填充)改善。
- 边缘重影:多因深度图与彩色图分辨率差异大或外参标定精度不足,建议先确认标定参数准确性。
- 运行速度慢:当前实现为逐像素向量化计算,若需实时处理,可改用GPU或降低处理分辨率。