Spaces:
Running
on
Zero
Running
on
Zero
import os | |
import os | |
import sys | |
from pathlib import Path | |
sys.path.append(".") | |
from point_utils import get_fov_mask | |
import cv2 | |
import hydra as hydra | |
import torch.nn.functional as F | |
import yaml | |
from matplotlib import pyplot as plt | |
from omegaconf import open_dict | |
from torch import nn | |
from tqdm import tqdm | |
import glob | |
import numpy as np | |
import torch | |
from plyfile import PlyData, PlyElement | |
os.system("nvidia-smi") | |
in_path = Path("<PATH-IN>") | |
TARGET_PATH = Path("<PATH-TARGET>") | |
# out_path = Path("media/voxel/npy/") | |
out_path = Path("<PATH-OUT>") | |
# out_path = Path("/storage/slurm/hayler/bts/voxel_outputs/sscnet") | |
# out_path.mkdir(exist_ok=True, parents=True) | |
fov_mask = get_fov_mask() | |
X_RANGE = (25.6, -25.6) | |
Y_RANGE = (51.2, 0) | |
Z_RANGE = (0, 6.4) | |
# | |
# gpu_id = 1 | |
# | |
# device = f'cpu' | |
# if gpu_id is not None: | |
# os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID" | |
# os.environ["CUDA_VISIBLE_DEVICES"] = str(gpu_id) | |
# if torch.cuda.is_available(): | |
# torch.backends.cudnn.enabled = True | |
# torch.backends.cudnn.benchmark = True | |
# torch.backends.cudnn.deterministic = True | |
# classes_to_colors = torch.tensor( | |
# [ | |
# [255, 255, 255], | |
# [100, 150, 245], # 1 | |
# [255, 0, 0], | |
# [255, 0, 255], | |
# [255, 150, 255], | |
# [75, 0, 75], | |
# [175, 0, 75], # 6 | |
# [255, 200, 0], | |
# [150, 150, 150], | |
# [30, 60, 150], | |
# [80, 30, 180], | |
# [8, 97, 0], # 11 | |
# [184, 56, 2], | |
# [255, 143, 46], | |
# [112, 255, 50], | |
# [194, 0, 0], | |
# [135, 60, 0], | |
# [150, 240, 80], | |
# [255, 240, 150], | |
# [255, 0, 0], | |
# ] | |
# ) | |
classes_to_colors = torch.tensor( | |
[ | |
[70, 130, 180], # sky for unlabeled (?) | |
[0, 0, 142], # 1 | |
[119, 11, 32], | |
[0, 0, 230], | |
[0, 0, 70], | |
[0, 60,100], | |
[220, 20, 60], # 6 | |
[128, 64,128], | |
[244, 35,232], | |
[90, 90, 90], | |
[190,153,153], | |
[107,142, 35], # 11 | |
[152,251,152], | |
[153,153,153], | |
[220,220, 0], | |
[250,170, 30], | |
[135, 60, 0], | |
[150, 240, 80], | |
[255, 240, 150], | |
[255, 0, 0], | |
] | |
) | |
with open("sscbench/label_maps.yaml", "r") as f: | |
label_maps = yaml.safe_load(f) | |
device = "cpu" | |
r, c, = 0, 0 | |
n_rows, n_cols = 3, 3 | |
def plot(img, fig, axs, i=None): | |
global r, c | |
if r == 0 and c == 0: | |
plt.show() | |
fig, axs = plt.subplots(n_rows, n_cols, figsize=(n_cols * 4, n_rows * 2)) | |
axs[r][c].imshow(img, interpolation="none") | |
if i is not None: | |
axs[r][c].title.set_text(f"{i}") | |
c += 1 | |
r += c // n_cols | |
c %= n_cols | |
r %= n_rows | |
return fig, axs | |
def save_plot(img, file_name=None, grey=False, mask=None): | |
if mask is not None: | |
if mask.shape[-1] != img.shape[-1]: | |
mask = np.broadcast_to(np.expand_dims(mask, -1), img.shape) | |
img = np.array(img) | |
img[~mask] = 0 | |
if dry_run: | |
plt.imshow(img) | |
plt.title(file_name) | |
plt.show() | |
else: | |
cv2.imwrite(file_name, cv2.cvtColor((img * 255).clip(max=255).astype(np.uint8), cv2.COLOR_RGB2BGR) if not grey else (img * 255).clip(max=255).astype(np.uint8)) | |
faces = [[0, 1, 2, 3], [0, 3, 7, 4], [2, 6, 7, 3], [5, 6, 2, 1], [4, 5, 1, 0], [7, 6, 5, 4]] | |
faces_t = torch.tensor(faces, device=device) | |
def build_voxel(i, j, k, x_res, y_res, z_res, xyz, offset): | |
ids = [[i+1, j+1, k], [i+1, j, k], | |
[i, j, k], [i, j+1, k], | |
[i+1, j+1, k+1], [i+1, j, k+1], | |
[i, j, k+1], [i, j+1, k+1]] | |
faces_off = [[v+offset for v in f] for f in faces] | |
ids_flat = list(map(lambda ijk: ijk[0]*y_res*z_res + ijk[1]*z_res + ijk[2], ids)) | |
verts = xyz[:, ids_flat].cpu().numpy().T | |
colors = np.tile(np.array(plt.cm.get_cmap("magma")(1 - (verts[..., 1].mean().item() - Y_RANGE[0]) / (Y_RANGE[1] - Y_RANGE[0]))[:3]).reshape((1, 3)), ((len(faces_off), 1))) | |
colors = (colors * 255).astype(np.uint8) | |
return verts, faces_off, colors | |
ids_offset = torch.tensor( | |
[[1, 1, 0], [1, 0, 0], | |
[0, 0, 0], [0, 1, 0], | |
[1, 1, 1], [1, 0, 1], | |
[0, 0, 1], [0, 1, 1]], | |
dtype=torch.int32, | |
device=device | |
) # (8, 3) | |
def remove_invisible(volume): | |
kernel = torch.tensor([[[0, 0, 0], | |
[0, 1, 0], | |
[0, 0, 0]], | |
[[0, 1, 0], | |
[1, 0, 1], | |
[0, 1, 0]], | |
[[0, 0, 0], | |
[0, 1, 0], | |
[0, 0, 0]]], dtype=torch.float32, device=volume.device).view(1, 1, 3, 3, 3) | |
neighbors = F.conv3d(volume.to(torch.float32).view(1, 1, *volume.shape), kernel, stride=1, padding=1)[0, 0, :, :, :] | |
is_hidden = neighbors >= 6 | |
volume = volume & (~is_hidden) | |
return volume | |
def check_neighbors(volume): | |
kernel = torch.zeros((6, 3, 3, 3), device=volume.device, dtype=torch.float32) | |
kernel[0, 1, 1, 0] = 1 | |
kernel[1, 1, 2, 1] = 1 | |
kernel[2, 0, 1, 1] = 1 | |
kernel[3, 1, 0, 1] = 1 | |
kernel[4, 2, 1, 1] = 1 | |
kernel[5, 1, 1, 2] = 1 | |
kernel = kernel.unsqueeze(1) | |
neighbors = F.conv3d(volume.to(torch.float32).view(1, 1, *volume.shape), kernel, stride=1, padding=1)[0, :, :, :, :] | |
neighbors = neighbors >= 1 | |
return neighbors | |
def build_voxels(ijks, x_res, y_res, z_res, xyz, neighbors=None, colors=None, classes=None): | |
# ijks (N, 3) | |
ids = ijks.view(-1, 1, 3) + ids_offset.view(1, -1, 3) | |
ids_flat = ids[..., 0] * y_res * z_res + ids[..., 1] * z_res + ids[..., 2] | |
verts = xyz[:, ids_flat.reshape(-1)] | |
faces_off = torch.arange(0, ijks.shape[0] * 8, 8, device=device) | |
faces_off = faces_off.view(-1, 1, 1) + faces_t.view(-1, 6, 4) | |
if classes is not None: | |
index_classes = classes[ijks[:, 0], ijks[:, 1], ijks[:, 2]].to(int) | |
colors = classes_to_colors[index_classes].view(-1, 1, 3).expand(-1, 8, -1) | |
elif colors is None: | |
z_steps = (1 - (torch.linspace(0, 1 - 1 / z_res, z_res) + 1 / (2 * z_res))).tolist() | |
cmap = plt.cm.get_cmap("magma") | |
z_to_color = (torch.tensor(list(map(cmap, z_steps)), device=device)[:, :3] * 255).to(torch.uint8) | |
colors = z_to_color[ijks[:, 2], :].view(-1, 1, 3).expand(-1, 8, -1) | |
else: | |
colors = colors[ijks[:, 0], ijks[:, 1], ijks[:, 2]].view(-1, 1, 3).expand(-1, 8, -1) | |
if neighbors is not None: | |
faces_off = faces_off.reshape(-1, 4)[~neighbors.reshape(-1), :] | |
return verts.cpu().numpy().T, faces_off.reshape(-1, 4).cpu().numpy(), colors.reshape(-1, 3).cpu().numpy() | |
def get_pts(x_range, y_range, z_range, x_res, y_res, z_res): | |
x = torch.linspace(x_range[0], x_range[1], x_res).view(x_res, 1, 1).expand(-1, y_res, z_res) | |
y = torch.linspace(y_range[0], y_range[1], y_res).view(1, y_res, 1).expand(x_res, -1, z_res) | |
z = torch.linspace(z_range[0], z_range[1], z_res).view(1, 1, z_res).expand(x_res, y_res, -1) | |
xyz = torch.stack((x, y, z), dim=-1) # (x, y, z) | |
# The KITTI 360 cameras have a 5 degrees negative inclination. We need to account for that tan(5°) = 0.0874886635 | |
return xyz | |
def save_as_voxel_ply(path, is_occupied, voxel_size=0.2, size=(256, 256, 32), classes=None, colors=None, fov_mask=None): | |
is_occupied = remove_invisible(is_occupied) | |
if fov_mask is not None: | |
is_occupied &= fov_mask.to(is_occupied.device) | |
is_occupied[0] = 0 | |
is_occupied[-1] = 0 | |
is_occupied[:, 0] = 0 | |
is_occupied[:, -1] = 0 | |
is_occupied[:, :, 0] = 0 | |
is_occupied[:, :, -1] = 0 | |
res = (size[0] + 1, size[1] + 1, size[2] + 1) | |
x_range = (size[0] * voxel_size * .5, -size[0] * voxel_size * .5) | |
y_range = (size[1] * voxel_size, 0) | |
z_range = (0, size[2] * voxel_size) | |
neighbors = check_neighbors(is_occupied) | |
neighbors = neighbors.view(6, -1)[:, is_occupied.reshape(-1)].T | |
q_pts = get_pts(x_range, y_range, z_range, *res) | |
q_pts = q_pts.to(device).reshape(1, -1, 3) | |
verts, faces, colors = build_voxels(is_occupied.nonzero(), *res, q_pts.squeeze(0).T, neighbors, classes=classes, colors=colors) | |
verts = list(map(tuple, verts)) | |
colors = list(map(tuple, colors)) | |
verts_colors = [v + c for v, c in zip(verts, colors)] | |
verts_data = np.array(verts_colors, dtype=[('x', 'f4'), ('y', 'f4'), ('z', 'f4'), ('red', 'u1'), ('green', 'u1'), ('blue', 'u1')]) | |
face_data = np.array(faces, dtype='i4') | |
ply_faces = np.empty(len(faces), dtype=[('vertex_indices', 'i4', (4,))]) | |
ply_faces['vertex_indices'] = face_data | |
verts_el = PlyElement.describe(verts_data, "vertex") | |
faces_el = PlyElement.describe(ply_faces, "face") | |
PlyData([verts_el, faces_el]).write(str(path)) | |
def convert_voxels(arr, map_dict): | |
f = np.vectorize(map_dict.__getitem__) | |
return f(arr) | |
def main(): | |
print('Loading file') | |
is_occupied = torch.tensor(np.load(in_path), dtype=torch.bool, device=device) > 0 | |
save_as_voxel_ply(out_path / f"{in_path.stem}.ply", is_occupied) | |
def safe_filepath_segementation(path, suffix="", gt=None, sizes=None, use_fov_mask=False): | |
path = Path(path) | |
segmentations = convert_voxels(np.load(path).astype(int), label_maps["sscbench_to_label"]) | |
if gt is not None: | |
segmentations[gt == 255] = 0 | |
segmentations[segmentations == 255] = 0 | |
is_occupied = torch.tensor(segmentations > 0) | |
if use_fov_mask: | |
is_occupied[~fov_mask] = 0 | |
if sizes: | |
for size in sizes: | |
if suffix != "": | |
fp = out_path / str(int(size)) / f"{id:06d}.ply" | |
else: | |
fp = out_path / str(int(size)) / f"{path.stem}.ply" | |
num_voxels = int(size // 0.2) | |
save_as_voxel_ply(fp, | |
is_occupied[: num_voxels, (128 - num_voxels // 2): (128 + num_voxels // 2), :], | |
classes=torch.tensor( | |
segmentations[: num_voxels, (128 - num_voxels // 2): (128 + num_voxels // 2), :])) | |
else: | |
if suffix != "": | |
save_as_voxel_ply(out_path / f"{path.stem}_{suffix}.ply", is_occupied, classes=torch.tensor(segmentations)) | |
else: | |
save_as_voxel_ply(out_path / f"{path.stem}.ply", is_occupied, classes=torch.tensor(segmentations)) | |
def safe_folder_segmentation(path:str, suffix="", ids=None, sizes=None, use_fov_mask=False): | |
if sizes: | |
for size in sizes: | |
if not os.path.exists(out_path / str(int(size))): | |
os.makedirs(out_path / str(int(size))) | |
for file in tqdm(sorted(glob.glob(path + "/*"))): | |
frameId = int(file.split('/')[-1].split(".")[0]) | |
gt = np.load(TARGET_PATH / f"{frameId:06d}_1_1.npy") | |
if ids and frameId not in ids: | |
continue | |
safe_filepath_segementation(file, suffix, gt=gt, sizes=sizes, use_fov_mask=use_fov_mask) | |
def main_segmentation(): | |
segmentations = convert_voxels(np.load(in_path).astype(int), label_maps["sscbench_to_label"]) | |
segmentations[segmentations == 255] = 0 | |
is_occupied = torch.tensor(segmentations > 0) | |
save_as_voxel_ply(out_path / f"{in_path.stem}.ply", is_occupied, classes=torch.tensor(segmentations)) | |
if __name__ == '__main__': | |
# safe_folder_segmentation("/storage/slurm/hayler/sscbench/3data/monoscene/paper", "monoscene") | |
safe_folder_segmentation("/storage/slurm/hayler/sscbench/outputs/lmscnet", suffix="", sizes=[12.8, 25.6, 51.2], use_fov_mask=True) | |
# safe_folder_segmentation("/storage/slurm/hayler/sscbench/outputs/sscnet", suffix="", sizes=[12.8, 25.6, 51.2], use_fov_mask=True) | |
# safe_folder_segmentation("/storage/slurm/hayler/sscbench/outputs/sscnet", suffix="sscnet", | |
# ids=[125, 5475, 6670, 6775, 7860, 8000]) | |