validate_nyu.py
May 13, 2020 ยท View on GitHub
import torch import cv2 import h5py import numpy as np
from scipy.io import loadmat
import torch.utils.data as data import torch.nn.functional as F from torchvision.transforms import Compose
from models.midas_net import MidasNet from models.transforms import Resize, NormalizeImage, PrepareForNet
class NyuDepthV2(data.Dataset): def init(self, datapath, splitpath, split="test", transform=None):
self.__image_list = []
self.__depth_list = []
self.__transform = transform
mat = loadmat(splitpath)
if split == "train":
indices = [ind[0] - 1 for ind in mat["trainNdxs"]]
elif split == "test":
indices = [ind[0] - 1 for ind in mat["testNdxs"]]
else:
raise ValueError("Split {} not found.".format(split))
with h5py.File(datapath, "r") as f:
for ind in indices:
self.__image_list.append(np.swapaxes(f["images"][ind], 0, 2))
self.__depth_list.append(np.swapaxes(f["rawDepths"][ind], 0, 1))
self.__length = len(self.__image_list)
def __len__(self):
return self.__length
def __getitem__(self, index):
# image
image = self.__image_list[index]
image = image / 255
# depth
depth = self.__depth_list[index]
# mask; cf. project_depth_map.m in toolbox_nyu_depth_v2 (max depth = 10.0)
mask = (depth > 0) & (depth < 10)
# sample
sample = {}
sample["image"] = image
sample["depth"] = depth
sample["mask"] = mask
# transforms
if self.__transform is not None:
sample = self.__transform(sample)
return sample
class BadPixelMetric: def init(self, threshold=1.25, depth_cap=10): self.__threshold = threshold self.__depth_cap = depth_cap
def compute_scale_and_shift(self, prediction, target, mask):
# system matrix: A = [[a_00, a_01], [a_10, a_11]]
a_00 = torch.sum(mask * prediction * prediction, (1, 2))
a_01 = torch.sum(mask * prediction, (1, 2))
a_11 = torch.sum(mask, (1, 2))
# right hand side: b = [b_0, b_1]
b_0 = torch.sum(mask * prediction * target, (1, 2))
b_1 = torch.sum(mask * target, (1, 2))
# solution: x = A^-1 . b = [[a_11, -a_01], [-a_10, a_00]] / (a_00 * a_11 - a_01 * a_10) . b
x_0 = torch.zeros_like(b_0)
x_1 = torch.zeros_like(b_1)
det = a_00 * a_11 - a_01 * a_01
# A needs to be a positive definite matrix.
valid = det > 0
x_0[valid] = (a_11[valid] * b_0[valid] - a_01[valid] * b_1[valid]) / det[valid]
x_1[valid] = (-a_01[valid] * b_0[valid] + a_00[valid] * b_1[valid]) / det[valid]
return x_0, x_1
def __call__(self, prediction, target, mask):
# transform predicted disparity to aligned depth
target_disparity = torch.zeros_like(target)
target_disparity[mask == 1] = 1.0 / target[mask == 1]
scale, shift = self.compute_scale_and_shift(prediction, target_disparity, mask)
prediction_aligned = scale.view(-1, 1, 1) * prediction + shift.view(-1, 1, 1)
disparity_cap = 1.0 / self.__depth_cap
prediction_aligned[prediction_aligned < disparity_cap] = disparity_cap
prediciton_depth = 1.0 / prediction_aligned
# bad pixel
err = torch.zeros_like(prediciton_depth, dtype=torch.float)
err[mask == 1] = torch.max(
prediciton_depth[mask == 1] / target[mask == 1],
target[mask == 1] / prediciton_depth[mask == 1],
)
err[mask == 1] = (err[mask == 1] > self.__threshold).float()
p = torch.sum(err, (1, 2)) / torch.sum(mask, (1, 2))
return 100 * torch.mean(p)
def validate(model, nyu_data, nyu_split): # set torch options torch.backends.cudnn.enabled = True torch.backends.cudnn.benchmark = True
# select device
device = torch.device("cuda")
print("device: %s" % device)
# load network
model = MidasNet(MODEL_PATH, non_negative=True)
model.to(device)
model.eval()
# get data
transform = Compose(
[
Resize(
384,
384,
resize_target=None,
keep_aspect_ratio=True,
ensure_multiple_of=32,
resize_method="upper_bound",
image_interpolation_method=cv2.INTER_CUBIC,
),
NormalizeImage(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
PrepareForNet(),
]
)
ds = NyuDepthV2(NYU_DATA_PATH, NYU_SPLIT_PATH, split="test", transform=transform)
dl = data.DataLoader(
ds, batch_size=1, num_workers=1, shuffle=False, pin_memory=True
)
# validate
metric = BadPixelMetric()
loss_sum = 0
with torch.no_grad():
for i, batch in enumerate(dl):
print(f"processing: {i + 1} / {len(ds)}")
# to device
for k, v in batch.items():
batch[k] = v.to(device)
# run model
prediction = model.forward(batch["image"])
# resize prediction to match target
prediction = F.interpolate(
prediction.unsqueeze(1),
size=batch["mask"].shape[1:],
mode="bilinear",
align_corners=False,
)
prediction = prediction.squeeze(1)
loss = metric(prediction, batch["depth"], batch["mask"])
loss_sum += loss
print(f"bad pixel: {loss_sum / len(ds):.2f}")
if name == "main": # download from http://horatio.cs.nyu.edu/mit/silberman/nyu_depth_v2/nyu_depth_v2_labeled.mat NYU_DATA_PATH = "nyu_depth_v2_labeled.mat"
# download from http://horatio.cs.nyu.edu/mit/silberman/indoor_seg_sup/splits.mat
NYU_SPLIT_PATH = "splits.mat"
# download from https://drive.google.com/file/d/1nqW_Hwj86kslfsXR7EnXpEWdO2csz1cC
MODEL_PATH = "model.pt"
# validate
validate(MODEL_PATH, NYU_DATA_PATH, NYU_SPLIT_PATH)