3. 从RGB-D图像生成彩色点云
任务目标
给定同一视角下的RGB彩色图像与对应的深度图像(16位单通道PNG,单位mm),生成包含XYZ坐标和RGB颜色的彩色点云,并保存为PCD格式文件。本案例使用合成数据演示完整流程,不依赖真实传感器。
环境依赖
需要安装:numpy、opencv-python、open3d(仅用于PCD保存与可视化验证)。相机内参在本案例中直接给定,无需标定。
核心实现
import numpy as np
import cv2
import open3d as o3d
# 相机内参(合成数据设定)
FX, FY = 525.0, 525.0
CX, CY = 319.5, 239.5
def generate_synthetic_data(h=480, w=640):
"""生成一张简单的合成RGB图与深度图(单位mm)"""
# 深度:平面z=1000mm,中心叠加一个半径100px的球面凸起
y, x = np.mgrid[0:h, 0:w].astype(np.float32)
depth = np.full((h, w), 1000.0, dtype=np.float32)
dist = np.sqrt((x - CX)**2 + (y - CY)**2)
sphere_mask = dist < 100
depth[sphere_mask] = 1000.0 - 50.0 * np.sqrt(1 - (dist[sphere_mask]/100)**2)
# RGB:背景为灰色,球体区域为蓝色
rgb = np.full((h, w, 3), 128, dtype=np.uint8)
rgb[sphere_mask] = [200, 100, 50] # BGR格式
return rgb, depth
def rgbd_to_colored_pointcloud(rgb_img, depth_img):
"""将RGB-D图像转换为彩色点云(单位:米)"""
h, w = depth_img.shape
# 生成像素坐标网格
v, u = np.mgrid[0:h, 0:w].astype(np.float32)
# 反投影:z = depth/1000(mm转m),x=(u-cx)*z/fx, y=(v-cy)*z/fy
z = depth_img / 1000.0
x = (u - CX) * z / FX
y = (v - CY) * z / FY
# 堆叠为Nx3点云,并过滤无效深度(z=0或无穷)
points = np.stack([x, y, z], axis=-1).reshape(-1, 3)
colors = rgb_img.reshape(-, 3).astype(np.float32) / 255.0 # 归一化到[0,1]
valid = np.isfinite(points).all(axis=1) & (points[:, 2] > 0)
return points[valid], colors[valid]
# 生成合成数据
rgb_img, depth_img = generate_synthetic_data()
# 生成彩色点云
points, colors = rgbd_to_colored_pointcloud(rgb_img, depth_img)
# 构建Open3D点云对象并保存为PCD
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(points)
pcd.colors = o3d.utility.Vector3dVector(colors)
o3d.io.write_point_cloud("colored_pointcloud.pcd", pcd)
print(f"点云点数: {len(points)}")
print("已保存: colored_pointcloud.pcd")
运行与验证
- 将上述代码保存为
rgbd_to_pcd.py,在终端运行python rgbd_to_pcd.py。 - 程序执行后,当前目录下应生成
colored_pointcloud.pcd文件。 - 使用以下命令可视化验证点云内容:
python -c "import open3d as o3d; pcd=o3d.io.read_point_cloud('colored_pointcloud.pcd'); o3d.visualization.draw_geometries([pcd])"
通过条件:可视化窗口中应看到约30万个点,整体呈灰色平面,中心区域有蓝色球面凸起,且点云颜色与原始RGB图像一致。终端输出的点云点数应接近640×480=307200(去除无效点后略少)。
关键说明
- 深度图单位必须为毫米,代码中除以1000转换为米,与Open3D默认单位一致。
- RGB图像需为BGR顺序(OpenCV默认),转换颜色时直接reshape即可,无需通道转换。
- 若使用真实RGB-D相机(如Kinect),需将深度图无效像素(值为0或超过量程)过滤,本案例合成数据已保证深度有效。