MonoScene / helpers.py
anhquancao's picture
downsample output
b5be88d
import numpy as np
import torch
import fusion
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
def read_calib(calib_path):
"""
Modify from https://github.com/utiasSTARS/pykitti/blob/d3e1bb81676e831886726cc5ed79ce1f049aef2c/pykitti/utils.py#L68
:param calib_path: Path to a calibration text file.
:return: dict with calibration matrices.
"""
calib_all = {}
with open(calib_path, "r") as f:
for line in f.readlines():
if line == "\n":
break
key, value = line.split(":", 1)
calib_all[key] = np.array([float(x) for x in value.split()])
# reshape matrices
calib_out = {}
# 3x4 projection matrix for left camera
calib_out["P2"] = calib_all["P2"].reshape(3, 4)
calib_out["Tr"] = np.identity(4) # 4x4 matrix
calib_out["Tr"][:3, :4] = calib_all["Tr"].reshape(3, 4)
return calib_out
def vox2pix(cam_E, cam_k,
vox_origin, voxel_size,
img_W, img_H,
scene_size):
"""
compute the 2D projection of voxels centroids
Parameters:
----------
cam_E: 4x4
=camera pose in case of NYUv2 dataset
=Transformation from camera to lidar coordinate in case of SemKITTI
cam_k: 3x3
camera intrinsics
vox_origin: (3,)
world(NYU)/lidar(SemKITTI) cooridnates of the voxel at index (0, 0, 0)
img_W: int
image width
img_H: int
image height
scene_size: (3,)
scene size in meter: (51.2, 51.2, 6.4) for SemKITTI and (4.8, 4.8, 2.88) for NYUv2
Returns
-------
projected_pix: (N, 2)
Projected 2D positions of voxels
fov_mask: (N,)
Voxels mask indice voxels inside image's FOV
pix_z: (N,)
Voxels'distance to the sensor in meter
"""
# Compute the x, y, z bounding of the scene in meter
vol_bnds = np.zeros((3,2))
vol_bnds[:,0] = vox_origin
vol_bnds[:,1] = vox_origin + np.array(scene_size)
# Compute the voxels centroids in lidar cooridnates
vol_dim = np.ceil((vol_bnds[:,1]- vol_bnds[:,0])/ voxel_size).copy(order='C').astype(int)
xv, yv, zv = np.meshgrid(
range(vol_dim[0]),
range(vol_dim[1]),
range(vol_dim[2]),
indexing='ij'
)
vox_coords = np.concatenate([
xv.reshape(1,-1),
yv.reshape(1,-1),
zv.reshape(1,-1)
], axis=0).astype(int).T
# Project voxels'centroid from lidar coordinates to camera coordinates
cam_pts = fusion.TSDFVolume.vox2world(vox_origin, vox_coords, voxel_size)
cam_pts = fusion.rigid_transform(cam_pts, cam_E)
# Project camera coordinates to pixel positions
projected_pix = fusion.TSDFVolume.cam2pix(cam_pts, cam_k)
pix_x, pix_y = projected_pix[:, 0], projected_pix[:, 1]
# Eliminate pixels outside view frustum
pix_z = cam_pts[:, 2]
fov_mask = np.logical_and(pix_x >= 0,
np.logical_and(pix_x < img_W,
np.logical_and(pix_y >= 0,
np.logical_and(pix_y < img_H,
pix_z > 0))))
return torch.from_numpy(projected_pix), torch.from_numpy(fov_mask), torch.from_numpy(pix_z)
def get_grid_coords(dims, resolution):
"""
:param dims: the dimensions of the grid [x, y, z] (i.e. [256, 256, 32])
:return coords_grid: is the center coords of voxels in the grid
"""
g_xx = np.arange(0, dims[0] + 1)
g_yy = np.arange(0, dims[1] + 1)
sensor_pose = 10
g_zz = np.arange(0, dims[2] + 1)
# Obtaining the grid with coords...
xx, yy, zz = np.meshgrid(g_xx[:-1], g_yy[:-1], g_zz[:-1])
coords_grid = np.array([xx.flatten(), yy.flatten(), zz.flatten()]).T
coords_grid = coords_grid.astype(np.float)
coords_grid = (coords_grid * resolution) + resolution / 2
temp = np.copy(coords_grid)
temp[:, 0] = coords_grid[:, 1]
temp[:, 1] = coords_grid[:, 0]
coords_grid = np.copy(temp)
return coords_grid
def get_projections(img_W, img_H):
scale_3ds = [1, 2]
data = {}
for scale_3d in scale_3ds:
scene_size = (51.2, 51.2, 6.4)
vox_origin = np.array([0, -25.6, -2])
voxel_size = 0.2
calib = read_calib("calib.txt")
cam_k = calib["P2"][:3, :3]
T_velo_2_cam = calib["Tr"]
# compute the 3D-2D mapping
projected_pix, fov_mask, pix_z = vox2pix(
T_velo_2_cam,
cam_k,
vox_origin,
voxel_size * scale_3d,
img_W,
img_H,
scene_size,
)
data["projected_pix_{}".format(scale_3d)] = projected_pix
data["pix_z_{}".format(scale_3d)] = pix_z
data["fov_mask_{}".format(scale_3d)] = fov_mask
return data
def majority_pooling(grid, k_size=2):
result = np.zeros(
(grid.shape[0] // k_size, grid.shape[1] // k_size, grid.shape[2] // k_size)
)
for xx in range(0, int(np.floor(grid.shape[0] / k_size))):
for yy in range(0, int(np.floor(grid.shape[1] / k_size))):
for zz in range(0, int(np.floor(grid.shape[2] / k_size))):
sub_m = grid[
(xx * k_size) : (xx * k_size) + k_size,
(yy * k_size) : (yy * k_size) + k_size,
(zz * k_size) : (zz * k_size) + k_size,
]
unique, counts = np.unique(sub_m, return_counts=True)
if True in ((unique != 0) & (unique != 255)):
# Remove counts with 0 and 255
counts = counts[((unique != 0) & (unique != 255))]
unique = unique[((unique != 0) & (unique != 255))]
else:
if True in (unique == 0):
counts = counts[(unique != 255)]
unique = unique[(unique != 255)]
value = unique[np.argmax(counts)]
result[xx, yy, zz] = value
return result
def draw(
voxels,
# T_velo_2_cam,
# vox_origin,
fov_mask,
# img_size,
# f,
voxel_size=0.4,
# d=7, # 7m - determine the size of the mesh representing the camera
):
fov_mask = fov_mask.reshape(-1)
# Compute the voxels coordinates
grid_coords = get_grid_coords(
[voxels.shape[0], voxels.shape[1], voxels.shape[2]], voxel_size
)
# Attach the predicted class to every voxel
grid_coords = np.vstack([grid_coords.T, voxels.reshape(-1)]).T
# Get the voxels inside FOV
fov_grid_coords = grid_coords[fov_mask, :]
# Get the voxels outside FOV
outfov_grid_coords = grid_coords[~fov_mask, :]
# Remove empty and unknown voxels
fov_voxels = fov_grid_coords[
(fov_grid_coords[:, 3] > 0) & (fov_grid_coords[:, 3] < 255), :
]
# print(np.unique(fov_voxels[:, 3], return_counts=True))
outfov_voxels = outfov_grid_coords[
(outfov_grid_coords[:, 3] > 0) & (outfov_grid_coords[:, 3] < 255), :
]
# figure = mlab.figure(size=(1400, 1400), bgcolor=(1, 1, 1))
colors = np.array(
[
[0,0,0],
[100, 150, 245],
[100, 230, 245],
[30, 60, 150],
[80, 30, 180],
[100, 80, 250],
[255, 30, 30],
[255, 40, 200],
[150, 30, 90],
[255, 0, 255],
[255, 150, 255],
[75, 0, 75],
[175, 0, 75],
[255, 200, 0],
[255, 120, 50],
[0, 175, 0],
[135, 60, 0],
[150, 240, 80],
[255, 240, 150],
[255, 0, 0],
]
).astype(np.uint8)
pts_colors = [f'rgb({colors[int(i)][0]}, {colors[int(i)][1]}, {colors[int(i)][2]})' for i in fov_voxels[:, 3]]
out_fov_colors = [f'rgb({colors[int(i)][0]//3*2}, {colors[int(i)][1]//3*2}, {colors[int(i)][2]//3*2})' for i in outfov_voxels[:, 3]]
pts_colors = pts_colors + out_fov_colors
fov_voxels = np.concatenate([fov_voxels, outfov_voxels], axis=0)
x = fov_voxels[:, 0].flatten()
y = fov_voxels[:, 1].flatten()
z = fov_voxels[:, 2].flatten()
# label = fov_voxels[:, 3].flatten()
fig = go.Figure(data=[go.Scatter3d(x=x, y=y, z=z,mode='markers',
marker=dict(
size=2,
color=pts_colors, # set color to an array/list of desired values
# colorscale='Viridis', # choose a colorscale
opacity=1.0,
symbol='square'
))])
fig.update_layout(
scene = dict(
aspectmode='data',
xaxis = dict(
backgroundcolor="rgb(255, 255, 255)",
gridcolor="black",
showbackground=True,
zerolinecolor="black",
nticks=4,
visible=False,
range=[-1,55],),
yaxis = dict(
backgroundcolor="rgb(255, 255, 255)",
gridcolor="black",
showbackground=True,
zerolinecolor="black",
visible=False,
nticks=4, range=[-1,55],),
zaxis = dict(
backgroundcolor="rgb(255, 255, 255)",
gridcolor="black",
showbackground=True,
zerolinecolor="black",
visible=False,
nticks=4, range=[-1,7],),
bgcolor="black",
),
)
# fig = px.scatter_3d(
# fov_voxels,
# x=fov_voxels[:, 0], y="y", z="z", color="label")
# Draw occupied inside FOV voxels
# plt_plot_fov = mlab.points3d(
# fov_voxels[:, 0],
# fov_voxels[:, 1],
# fov_voxels[:, 2],
# fov_voxels[:, 3],
# colormap="viridis",
# scale_factor=voxel_size - 0.05 * voxel_size,
# mode="cube",
# opacity=1.0,
# vmin=1,
# vmax=19,
# )
# # Draw occupied outside FOV voxels
# plt_plot_outfov = mlab.points3d(
# outfov_voxels[:, 0],
# outfov_voxels[:, 1],
# outfov_voxels[:, 2],
# outfov_voxels[:, 3],
# colormap="viridis",
# scale_factor=voxel_size - 0.05 * voxel_size,
# mode="cube",
# opacity=1.0,
# vmin=1,
# vmax=19,
# )
# plt_plot_fov.glyph.scale_mode = "scale_by_vector"
# plt_plot_outfov.glyph.scale_mode = "scale_by_vector"
# plt_plot_fov.module_manager.scalar_lut_manager.lut.table = colors
# outfov_colors = colors
# outfov_colors[:, :3] = outfov_colors[:, :3] // 3 * 2
# plt_plot_outfov.module_manager.scalar_lut_manager.lut.table = outfov_colors
# mlab.show()
return fig