|
|
|
|
| import numpy as np |
| import open3d as o3d |
|
|
| def depth2pcd(depth, intrinsic, color=None, input_mask=None, ret_pcd=False): |
| """ |
| Convert a depth map into a 3D point cloud. |
| |
| Args: |
| depth (np.ndarray): (H, W) depth map in meters. |
| intrinsic (np.ndarray): (3, 3) camera intrinsic matrix. |
| color (np.ndarray, optional): (H, W, 3) RGB image aligned with the depth map. |
| input_mask (np.ndarray, optional): (H, W) boolean mask indicating valid pixels. |
| ret_pcd (bool, optional): If True, returns an Open3D PointCloud object; |
| otherwise returns NumPy arrays. |
| |
| Returns: |
| - If ret_pcd=True: returns `o3d.geometry.PointCloud()` |
| - Otherwise: returns (N, 3) point coordinates and (N, 3) color arrays. |
| """ |
| H, W = depth.shape |
| u, v = np.meshgrid(np.arange(W), np.arange(H)) |
| |
| fx, fy = intrinsic[0,0], intrinsic[1,1] |
| cx, cy = intrinsic[0,2], intrinsic[1,2] |
| |
| Z = depth.reshape(-1) |
| X = ((u.reshape(-1) - cx) / fx) * Z |
| Y = ((v.reshape(-1) - cy) / fy) * Z |
|
|
| points = np.stack([X, Y, Z], axis=1) |
|
|
| |
| mask = np.ones_like(Z, dtype=bool) |
| if input_mask is not None: |
| mask &= input_mask.reshape(-1) |
|
|
| |
| points = points[mask] |
|
|
| |
| if color is not None: |
| color = color.astype(np.float32) / 255.0 |
| colors = color.reshape(-1, 3)[mask] |
| else: |
| colors = None |
| |
| |
| if ret_pcd: |
| pcd = o3d.geometry.PointCloud() |
| pcd.points = o3d.utility.Vector3dVector(points) |
| if colors is not None: |
| pcd.colors = o3d.utility.Vector3dVector(colors) |
| return pcd |
| else: |
| return points, colors |