LiDAR-Diffusion / lidm /utils /lidar_utils.py
Hancy's picture
init
851751e
raw
history blame
7.08 kB
import math
import numpy as np
def pcd2coord2d(pcd, fov, depth_range, labels=None):
# laser parameters
fov_up = fov[0] / 180.0 * np.pi # field of view up in rad
fov_down = fov[1] / 180.0 * np.pi # field of view down in rad
fov_range = abs(fov_down) + abs(fov_up) # get field of view total in rad
# get depth (distance) of all points
depth = np.linalg.norm(pcd, 2, axis=-1)
# mask points out of range
mask = np.logical_and(depth > depth_range[0], depth < depth_range[1])
if pcd.ndim == 3:
mask = mask.all(axis=1)
depth, pcd = depth[mask], pcd[mask]
# get scan components
scan_x, scan_y, scan_z = pcd[..., 0], pcd[..., 1], pcd[..., 2]
# get angles of all points
yaw = -np.arctan2(scan_y, scan_x)
pitch = np.arcsin(scan_z / depth)
# get projections in image coords
proj_x = np.clip(0.5 * (yaw / np.pi + 1.0), 0., 1.) # in [0.0, 1.0]
proj_y = np.clip(1.0 - (pitch + abs(fov_down)) / fov_range, 0., 1.) # in [0.0, 1.0]
proj_coord2d = np.stack([proj_x, proj_y], axis=-1)
if labels is not None:
proj_labels = labels[mask]
else:
proj_labels = None
return proj_coord2d, proj_labels
def pcd2range(pcd, size, fov, depth_range, remission=None, labels=None, **kwargs):
# laser parameters
fov_up = fov[0] / 180.0 * np.pi # field of view up in rad
fov_down = fov[1] / 180.0 * np.pi # field of view down in rad
fov_range = abs(fov_down) + abs(fov_up) # get field of view total in rad
# get depth (distance) of all points
depth = np.linalg.norm(pcd, 2, axis=1)
# mask points out of range
mask = np.logical_and(depth > depth_range[0], depth < depth_range[1])
depth, pcd = depth[mask], pcd[mask]
# get scan components
scan_x, scan_y, scan_z = pcd[:, 0], pcd[:, 1], pcd[:, 2]
# get angles of all points
yaw = -np.arctan2(scan_y, scan_x)
pitch = np.arcsin(scan_z / depth)
# get projections in image coords
proj_x = 0.5 * (yaw / np.pi + 1.0) # in [0.0, 1.0]
proj_y = 1.0 - (pitch + abs(fov_down)) / fov_range # in [0.0, 1.0]
# scale to image size using angular resolution
proj_x *= size[1] # in [0.0, W]
proj_y *= size[0] # in [0.0, H]
# round and clamp for use as index
proj_x = np.maximum(0, np.minimum(size[1] - 1, np.floor(proj_x))).astype(np.int32) # in [0,W-1]
proj_y = np.maximum(0, np.minimum(size[0] - 1, np.floor(proj_y))).astype(np.int32) # in [0,H-1]
# order in decreasing depth
order = np.argsort(depth)[::-1]
proj_x, proj_y = proj_x[order], proj_y[order]
# project depth
depth = depth[order]
proj_range = np.full(size, -1, dtype=np.float32)
proj_range[proj_y, proj_x] = depth
# project point feature
if remission is not None:
remission = remission[mask][order]
proj_feature = np.full(size, -1, dtype=np.float32)
proj_feature[proj_y, proj_x] = remission
elif labels is not None:
labels = labels[mask][order]
proj_feature = np.full(size, 0, dtype=np.float32)
proj_feature[proj_y, proj_x] = labels
else:
proj_feature = None
return proj_range, proj_feature
def range2pcd(range_img, fov, depth_range, depth_scale, log_scale=True, label=None, color=None, **kwargs):
# laser parameters
size = range_img.shape
fov_up = fov[0] / 180.0 * np.pi # field of view up in rad
fov_down = fov[1] / 180.0 * np.pi # field of view down in rad
fov_range = abs(fov_down) + abs(fov_up) # get field of view total in rad
# inverse transform from depth
depth = (range_img * depth_scale).flatten()
if log_scale:
depth = np.exp2(depth) - 1
scan_x, scan_y = np.meshgrid(np.arange(size[1]), np.arange(size[0]))
scan_x = scan_x.astype(np.float64) / size[1]
scan_y = scan_y.astype(np.float64) / size[0]
yaw = (np.pi * (scan_x * 2 - 1)).flatten()
pitch = ((1.0 - scan_y) * fov_range - abs(fov_down)).flatten()
pcd = np.zeros((len(yaw), 3))
pcd[:, 0] = np.cos(yaw) * np.cos(pitch) * depth
pcd[:, 1] = -np.sin(yaw) * np.cos(pitch) * depth
pcd[:, 2] = np.sin(pitch) * depth
# mask out invalid points
mask = np.logical_and(depth > depth_range[0], depth < depth_range[1])
pcd = pcd[mask, :]
# label
if label is not None:
label = label.flatten()[mask]
# default point color
if color is not None:
color = color.reshape(-1, 3)[mask, :]
else:
color = np.ones((pcd.shape[0], 3)) * [0.7, 0.7, 1]
return pcd, color, label
def range2xyz(range_img, fov, depth_range, depth_scale, log_scale=True, **kwargs):
# laser parameters
size = range_img.shape
fov_up = fov[0] / 180.0 * np.pi # field of view up in rad
fov_down = fov[1] / 180.0 * np.pi # field of view down in rad
fov_range = abs(fov_down) + abs(fov_up) # get field of view total in rad
# inverse transform from depth
if log_scale:
depth = (np.exp2(range_img * depth_scale) - 1)
else:
depth = range_img
scan_x, scan_y = np.meshgrid(np.arange(size[1]), np.arange(size[0]))
scan_x = scan_x.astype(np.float64) / size[1]
scan_y = scan_y.astype(np.float64) / size[0]
yaw = np.pi * (scan_x * 2 - 1)
pitch = (1.0 - scan_y) * fov_range - abs(fov_down)
xyz = -np.ones((3, *size))
xyz[0] = np.cos(yaw) * np.cos(pitch) * depth
xyz[1] = -np.sin(yaw) * np.cos(pitch) * depth
xyz[2] = np.sin(pitch) * depth
# mask out invalid points
mask = np.logical_and(depth > depth_range[0], depth < depth_range[1])
xyz[:, ~mask] = -1
return xyz
def pcd2bev(pcd, x_range, y_range, z_range, resolution, **kwargs):
# mask out invalid points
mask_x = np.logical_and(pcd[:, 0] > x_range[0], pcd[:, 0] < x_range[1])
mask_y = np.logical_and(pcd[:, 1] > y_range[0], pcd[:, 1] < y_range[1])
mask_z = np.logical_and(pcd[:, 2] > z_range[0], pcd[:, 2] < z_range[1])
mask = mask_x & mask_y & mask_z
pcd = pcd[mask]
# points to bev coords
bev_x = np.floor((pcd[:, 0] - x_range[0]) / resolution).astype(np.int32)
bev_y = np.floor((pcd[:, 1] - y_range[0]) / resolution).astype(np.int32)
# 2D bev grid
bev_shape = (math.ceil((x_range[1] - x_range[0]) // resolution), math.ceil((y_range[1] - y_range[0]) // resolution))
bev_grid = np.zeros(bev_shape, dtype=np.float64)
# populate the BEV grid with bev coords
bev_grid[bev_x, bev_y] = 1
return bev_grid
if __name__ == '__main__':
# test = np.loadtxt('test_range.txt')
# pcd, _, _ = range2pcd(test, (32, 1024), (10, -30))
# np.savetxt('test_pcd.txt', pcd, fmt='%.4f')
# import matplotlib.pyplot as plt
# pcd = np.loadtxt('test_origin.txt')
# bev_grid = pcd2bev(pcd)
# plt.imshow(bev_grid[:, :, 0], cmap='gray') # Display the BEV for the first height level
# plt.savefig('test.png', dpi=300, bbox_inches='tight', pad_inches=0, transparent=True)
from PIL import Image
img = Image.open('assets/kitti/range.png')
img.convert('L')
img = np.array(img) / 255.