Spaces:
Runtime error
Runtime error
File size: 7,819 Bytes
1f418ff |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
import cv2
import torch
import numpy as np
import torch.nn as nn
import matplotlib.pyplot as plt
from scipy import ndimage
from skimage.feature import canny
import kornia
def compute_errors(gt, pred):
"""Compute metrics for 'pred' compared to 'gt'
Args:
gt (numpy.ndarray): Ground truth values
pred (numpy.ndarray): Predicted values
gt.shape should be equal to pred.shape
Returns:
dict: Dictionary containing the following metrics:
'a1': Delta1 accuracy: Fraction of pixels that are within a scale factor of 1.25
'a2': Delta2 accuracy: Fraction of pixels that are within a scale factor of 1.25^2
'a3': Delta3 accuracy: Fraction of pixels that are within a scale factor of 1.25^3
'abs_rel': Absolute relative error
'rmse': Root mean squared error
'log_10': Absolute log10 error
'sq_rel': Squared relative error
'rmse_log': Root mean squared error on the log scale
'silog': Scale invariant log error
"""
thresh = np.maximum((gt / pred), (pred / gt))
a1 = (thresh < 1.25).mean()
a2 = (thresh < 1.25 ** 2).mean()
a3 = (thresh < 1.25 ** 3).mean()
abs_rel = np.mean(np.abs(gt - pred) / gt)
sq_rel = np.mean(((gt - pred) ** 2) / gt)
rmse = (gt - pred) ** 2
rmse = np.sqrt(rmse.mean())
rmse_log = (np.log(gt) - np.log(pred)) ** 2
rmse_log = np.sqrt(rmse_log.mean())
err = np.log(pred) - np.log(gt)
silog = np.sqrt(np.mean(err ** 2) - np.mean(err) ** 2) * 100
log_10 = (np.abs(np.log10(gt) - np.log10(pred))).mean()
return dict(a1=a1, a2=a2, a3=a3, abs_rel=abs_rel, rmse=rmse, log_10=log_10, rmse_log=rmse_log,
silog=silog, sq_rel=sq_rel)
def shift_2d_replace(data, dx, dy, constant=False):
shifted_data = np.roll(data, dx, axis=1)
if dx < 0:
shifted_data[:, dx:] = constant
elif dx > 0:
shifted_data[:, 0:dx] = constant
shifted_data = np.roll(shifted_data, dy, axis=0)
if dy < 0:
shifted_data[dy:, :] = constant
elif dy > 0:
shifted_data[0:dy, :] = constant
return shifted_data
def soft_edge_error(pred, gt, radius=1):
abs_diff=[]
for i in range(-radius, radius + 1):
for j in range(-radius, radius + 1):
abs_diff.append(np.abs(shift_2d_replace(gt, i, j, 0) - pred))
return np.minimum.reduce(abs_diff)
def get_boundaries(disp, th=1., dilation=10):
edges_y = np.logical_or(np.pad(np.abs(disp[1:, :] - disp[:-1, :]) > th, ((1, 0), (0, 0))),
np.pad(np.abs(disp[:-1, :] - disp[1:, :]) > th, ((0, 1), (0, 0))))
edges_x = np.logical_or(np.pad(np.abs(disp[:, 1:] - disp[:, :-1]) > th, ((0, 0), (1, 0))),
np.pad(np.abs(disp[:, :-1] - disp[:,1:]) > th, ((0, 0), (0, 1))))
edges = np.logical_or(edges_y, edges_x).astype(np.float32)
if dilation > 0:
kernel = np.ones((dilation, dilation), np.uint8)
edges = cv2.dilate(edges, kernel, iterations=1)
return edges
def compute_metrics(gt, pred, interpolate=True, garg_crop=False, eigen_crop=True, dataset='nyu', min_depth_eval=0.1, max_depth_eval=10, disp_gt_edges=None, additional_mask=None):
"""Compute metrics of predicted depth maps. Applies cropping and masking as necessary or specified via arguments. Refer to compute_errors for more details on metrics.
"""
if gt.shape[-2:] != pred.shape[-2:] and interpolate:
pred = nn.functional.interpolate(
# pred, gt.shape[-2:], mode='bilinear', align_corners=True).squeeze()
pred, gt.shape[-2:], mode='bilinear', align_corners=False).squeeze()
pred = pred.squeeze().cpu().numpy()
pred[pred < min_depth_eval] = min_depth_eval
pred[pred > max_depth_eval] = max_depth_eval
pred[np.isinf(pred)] = max_depth_eval
pred[np.isnan(pred)] = min_depth_eval
gt_depth = gt.squeeze().cpu().numpy()
valid_mask = np.logical_and(
gt_depth > min_depth_eval, gt_depth < max_depth_eval)
eval_mask = np.ones(valid_mask.shape)
if garg_crop or eigen_crop:
gt_height, gt_width = gt_depth.shape
eval_mask = np.zeros(valid_mask.shape)
if garg_crop:
eval_mask[int(0.40810811 * gt_height):int(0.99189189 * gt_height),
int(0.03594771 * gt_width):int(0.96405229 * gt_width)] = 1
elif eigen_crop:
# print("-"*10, " EIGEN CROP ", "-"*10)
if dataset == 'kitti':
eval_mask[int(0.3324324 * gt_height):int(0.91351351 * gt_height),
int(0.0359477 * gt_width):int(0.96405229 * gt_width)] = 1
else:
# assert gt_depth.shape == (480, 640), "Error: Eigen crop is currently only valid for (480, 640) images"
eval_mask[45:471, 41:601] = 1
else:
eval_mask = np.ones(valid_mask.shape)
valid_mask = np.logical_and(valid_mask, eval_mask)
# for prompt depth
if additional_mask is not None:
additional_mask = additional_mask.squeeze().detach().cpu().numpy()
valid_mask = np.logical_and(valid_mask, additional_mask)
metrics = compute_errors(gt_depth[valid_mask], pred[valid_mask])
if disp_gt_edges is not None:
edges = disp_gt_edges.squeeze().numpy()
mask = valid_mask.squeeze() # squeeze
mask = np.logical_and(mask, edges)
see_depth = torch.tensor([0])
if mask.sum() > 0:
see_depth_map = soft_edge_error(pred, gt_depth)
see_depth_map_valid = see_depth_map[mask]
see_depth = see_depth_map_valid.mean()
metrics['see'] = see_depth
return metrics
def eps(x):
"""Return the `eps` value for the given `input` dtype. (default=float32 ~= 1.19e-7)"""
dtype = torch.float32 if x is None else x.dtype
return torch.finfo(dtype).eps
def to_log(depth):
"""Convert linear depth into log depth."""
depth = torch.tensor(depth)
depth = (depth > 0) * depth.clamp(min=eps(depth)).log()
return depth
def to_inv(depth):
"""Convert linear depth into disparity."""
depth = torch.tensor(depth)
disp = (depth > 0) / depth.clamp(min=eps(depth))
return disp
def extract_edges(depth,
preprocess=None,
sigma=1,
mask=None,
use_canny=True):
"""Detect edges in a dense LiDAR depth map.
:param depth: (ndarray) (h, w, 1) Dense depth map to extract edges.
:param preprocess: (str) Additional depth map post-processing. (log, inv, none)
:param sigma: (int) Gaussian blurring sigma.
:param mask: (Optional[ndarray]) Optional boolean mask of valid pixels to keep.
:param use_canny: (bool) If `True`, use `Canny` edge detection, otherwise `Sobel`.
:return: (ndarray) (h, w) Detected depth edges in the image.
"""
if preprocess not in {'log', 'inv', 'none', None}:
raise ValueError(f'Invalid depth preprocessing. ({preprocess})')
depth = depth.squeeze()
if preprocess == 'log':
depth = to_log(depth)
elif preprocess == 'inv':
depth = to_inv(depth)
depth -= depth.min()
depth /= depth.max()
else:
depth = torch.tensor(depth)
input_value = (depth > 0) * depth.clamp(min=eps(depth))
# depth = torch.log(input_value) / torch.log(torch.tensor(1.9))
# depth = torch.log(input_value) / torch.log(torch.tensor(1.9))
depth = torch.log(input_value) / torch.log(torch.tensor(1.5))
depth = depth.numpy()
if use_canny:
edges = canny(depth, sigma=sigma, mask=mask)
else:
raise NotImplementedError("Sobel edge detection is not implemented yet.")
return edges
|