| |
|
| |
|
| | 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 |