Commit 41b18fd8 authored by zhe chen's avatar zhe chen
Browse files

Use pre-commit to reformat code


Use pre-commit to reformat code
parent ff20ea39
from .epoch_based_runner import EpochBasedRunner_video from .epoch_based_runner import EpochBasedRunner_video
\ No newline at end of file
# Copyright (c) OpenMMLab. All rights reserved. # Copyright (c) OpenMMLab. All rights reserved.
# --------------------------------------------- # ---------------------------------------------
# Modified by Zhiqi Li # Modified by Zhiqi Li
# --------------------------------------------- # ---------------------------------------------
import os.path as osp import torch
import torch from mmcv.parallel.data_container import DataContainer
import mmcv from mmcv.runner.builder import RUNNERS
from mmcv.runner.base_runner import BaseRunner from mmcv.runner.epoch_based_runner import EpochBasedRunner
from mmcv.runner.epoch_based_runner import EpochBasedRunner
from mmcv.runner.builder import RUNNERS
from mmcv.runner.checkpoint import save_checkpoint @RUNNERS.register_module()
from mmcv.runner.utils import get_host_info class EpochBasedRunner_video(EpochBasedRunner):
from pprint import pprint '''
from mmcv.parallel.data_container import DataContainer # basic logic
input_sequence = [a, b, c] # given a sequence of samples
@RUNNERS.register_module()
class EpochBasedRunner_video(EpochBasedRunner): prev_bev = None
for each in input_sequcene[:-1]
''' prev_bev = eval_model(each, prev_bev)) # inference only.
# basic logic
model(input_sequcene[-1], prev_bev) # train the last sample.
input_sequence = [a, b, c] # given a sequence of samples '''
prev_bev = None def __init__(self,
for each in input_sequcene[:-1] model,
prev_bev = eval_model(each, prev_bev)) # inference only. eval_model=None,
batch_processor=None,
model(input_sequcene[-1], prev_bev) # train the last sample. optimizer=None,
''' work_dir=None,
logger=None,
def __init__(self, meta=None,
model, keys=['gt_bboxes_3d', 'gt_labels_3d', 'img'],
eval_model=None, max_iters=None,
batch_processor=None, max_epochs=None):
optimizer=None, super().__init__(model,
work_dir=None, batch_processor,
logger=None, optimizer,
meta=None, work_dir,
keys=['gt_bboxes_3d', 'gt_labels_3d', 'img'], logger,
max_iters=None, meta,
max_epochs=None): max_iters,
super().__init__(model, max_epochs)
batch_processor, keys.append('img_metas')
optimizer, self.keys = keys
work_dir, self.eval_model = eval_model
logger, self.eval_model.eval()
meta,
max_iters, def run_iter(self, data_batch, train_mode, **kwargs):
max_epochs) if self.batch_processor is not None:
keys.append('img_metas') assert False
self.keys = keys # outputs = self.batch_processor(
self.eval_model = eval_model # self.model, data_batch, train_mode=train_mode, **kwargs)
self.eval_model.eval() elif train_mode:
def run_iter(self, data_batch, train_mode, **kwargs): num_samples = data_batch['img'].data[0].size(1)
if self.batch_processor is not None: data_list = []
assert False prev_bev = None
# outputs = self.batch_processor( for i in range(num_samples):
# self.model, data_batch, train_mode=train_mode, **kwargs) data = {}
elif train_mode: for key in self.keys:
if key not in ['img_metas', 'img', 'points']:
num_samples = data_batch['img'].data[0].size(1) data[key] = data_batch[key]
data_list = [] else:
prev_bev = None if key == 'img':
for i in range(num_samples): data['img'] = DataContainer(data=[data_batch['img'].data[0][:, i]],
data = {} cpu_only=data_batch['img'].cpu_only, stack=True)
for key in self.keys: elif key == 'img_metas':
if key not in ['img_metas', 'img', 'points']: data['img_metas'] = DataContainer(
data[key] = data_batch[key] data=[[each[i] for each in data_batch['img_metas'].data[0]]],
else: cpu_only=data_batch['img_metas'].cpu_only)
if key == 'img': else:
data['img'] = DataContainer(data=[data_batch['img'].data[0][:, i]], cpu_only=data_batch['img'].cpu_only, stack=True) assert False
elif key == 'img_metas': data_list.append(data)
data['img_metas'] = DataContainer(data=[[each[i] for each in data_batch['img_metas'].data[0]]], cpu_only=data_batch['img_metas'].cpu_only) with torch.no_grad():
else: for i in range(num_samples - 1):
assert False if data_list[i]['img_metas'].data[0][0]['prev_bev_exists']:
data_list.append(data) data_list[i]['prev_bev'] = DataContainer(data=[prev_bev], cpu_only=False)
with torch.no_grad(): prev_bev = self.eval_model.val_step(data_list[i], self.optimizer, **kwargs)
for i in range(num_samples-1): if data_list[-1]['img_metas'].data[0][0]['prev_bev_exists']:
if data_list[i]['img_metas'].data[0][0]['prev_bev_exists']: data_list[-1]['prev_bev'] = DataContainer(data=[prev_bev], cpu_only=False)
data_list[i]['prev_bev'] = DataContainer(data=[prev_bev], cpu_only=False) outputs = self.model.train_step(data_list[-1], self.optimizer, **kwargs)
prev_bev = self.eval_model.val_step(data_list[i], self.optimizer, **kwargs) else:
if data_list[-1]['img_metas'].data[0][0]['prev_bev_exists']: assert False
data_list[-1]['prev_bev'] = DataContainer(data=[prev_bev], cpu_only=False) # outputs = self.model.val_step(data_batch, self.optimizer, **kwargs)
outputs = self.model.train_step(data_list[-1], self.optimizer, **kwargs)
else: if not isinstance(outputs, dict):
assert False raise TypeError('"batch_processor()" or "model.train_step()"'
# outputs = self.model.val_step(data_batch, self.optimizer, **kwargs) 'and "model.val_step()" must return a dict')
if 'log_vars' in outputs:
if not isinstance(outputs, dict): self.log_buffer.update(outputs['log_vars'], outputs['num_samples'])
raise TypeError('"batch_processor()" or "model.train_step()"' self.outputs = outputs
'and "model.val_step()" must return a dict')
if 'log_vars' in outputs:
self.log_buffer.update(outputs['log_vars'], outputs['num_samples'])
self.outputs = outputs
\ No newline at end of file
import torch import torch
from mmdet.core.bbox.assigners import AssignResult, BaseAssigner
from mmdet.core.bbox.builder import BBOX_ASSIGNERS from mmdet.core.bbox.builder import BBOX_ASSIGNERS
from mmdet.core.bbox.assigners import AssignResult
from mmdet.core.bbox.assigners import BaseAssigner
from mmdet.core.bbox.match_costs import build_match_cost from mmdet.core.bbox.match_costs import build_match_cost
from mmdet.models.utils.transformer import inverse_sigmoid
from projects.mmdet3d_plugin.core.bbox.util import normalize_bbox from projects.mmdet3d_plugin.core.bbox.util import normalize_bbox
try: try:
...@@ -52,7 +49,7 @@ class HungarianAssigner3D(BaseAssigner): ...@@ -52,7 +49,7 @@ class HungarianAssigner3D(BaseAssigner):
def assign(self, def assign(self,
bbox_pred, bbox_pred,
cls_pred, cls_pred,
gt_bboxes, gt_bboxes,
gt_labels, gt_labels,
gt_bboxes_ignore=None, gt_bboxes_ignore=None,
eps=1e-7): eps=1e-7):
...@@ -89,10 +86,10 @@ class HungarianAssigner3D(BaseAssigner): ...@@ -89,10 +86,10 @@ class HungarianAssigner3D(BaseAssigner):
num_gts, num_bboxes = gt_bboxes.size(0), bbox_pred.size(0) num_gts, num_bboxes = gt_bboxes.size(0), bbox_pred.size(0)
# 1. assign -1 by default # 1. assign -1 by default
assigned_gt_inds = bbox_pred.new_full((num_bboxes, ), assigned_gt_inds = bbox_pred.new_full((num_bboxes,),
-1, -1,
dtype=torch.long) dtype=torch.long)
assigned_labels = bbox_pred.new_full((num_bboxes, ), assigned_labels = bbox_pred.new_full((num_bboxes,),
-1, -1,
dtype=torch.long) dtype=torch.long)
if num_gts == 0 or num_bboxes == 0: if num_gts == 0 or num_bboxes == 0:
...@@ -107,14 +104,14 @@ class HungarianAssigner3D(BaseAssigner): ...@@ -107,14 +104,14 @@ class HungarianAssigner3D(BaseAssigner):
# classification and bboxcost. # classification and bboxcost.
cls_cost = self.cls_cost(cls_pred, gt_labels) cls_cost = self.cls_cost(cls_pred, gt_labels)
# regression L1 cost # regression L1 cost
normalized_gt_bboxes = normalize_bbox(gt_bboxes, self.pc_range) normalized_gt_bboxes = normalize_bbox(gt_bboxes, self.pc_range)
reg_cost = self.reg_cost(bbox_pred[:, :8], normalized_gt_bboxes[:, :8]) reg_cost = self.reg_cost(bbox_pred[:, :8], normalized_gt_bboxes[:, :8])
# weighted sum of above two costs # weighted sum of above two costs
cost = cls_cost + reg_cost cost = cls_cost + reg_cost
# 3. do Hungarian matching on CPU using linear_sum_assignment # 3. do Hungarian matching on CPU using linear_sum_assignment
cost = cost.detach().cpu() cost = cost.detach().cpu()
if linear_sum_assignment is None: if linear_sum_assignment is None:
...@@ -133,4 +130,4 @@ class HungarianAssigner3D(BaseAssigner): ...@@ -133,4 +130,4 @@ class HungarianAssigner3D(BaseAssigner):
assigned_gt_inds[matched_row_inds] = matched_col_inds + 1 assigned_gt_inds[matched_row_inds] = matched_col_inds + 1
assigned_labels[matched_row_inds] = gt_labels[matched_col_inds] assigned_labels[matched_row_inds] = gt_labels[matched_col_inds]
return AssignResult( return AssignResult(
num_gts, assigned_gt_inds, None, labels=assigned_labels) num_gts, assigned_gt_inds, None, labels=assigned_labels)
\ No newline at end of file
import torch import torch
from mmdet.core.bbox import BaseBBoxCoder from mmdet.core.bbox import BaseBBoxCoder
from mmdet.core.bbox.builder import BBOX_CODERS from mmdet.core.bbox.builder import BBOX_CODERS
from projects.mmdet3d_plugin.core.bbox.util import denormalize_bbox from projects.mmdet3d_plugin.core.bbox.util import denormalize_bbox
import numpy as np
@BBOX_CODERS.register_module() @BBOX_CODERS.register_module()
...@@ -56,10 +54,10 @@ class NMSFreeCoder(BaseBBoxCoder): ...@@ -56,10 +54,10 @@ class NMSFreeCoder(BaseBBoxCoder):
labels = indexs % self.num_classes labels = indexs % self.num_classes
bbox_index = indexs // self.num_classes bbox_index = indexs // self.num_classes
bbox_preds = bbox_preds[bbox_index] bbox_preds = bbox_preds[bbox_index]
final_box_preds = denormalize_bbox(bbox_preds, self.pc_range) final_box_preds = denormalize_bbox(bbox_preds, self.pc_range)
final_scores = scores final_scores = scores
final_preds = labels final_preds = labels
# use score threshold # use score threshold
if self.score_threshold is not None: if self.score_threshold is not None:
...@@ -113,10 +111,9 @@ class NMSFreeCoder(BaseBBoxCoder): ...@@ -113,10 +111,9 @@ class NMSFreeCoder(BaseBBoxCoder):
""" """
all_cls_scores = preds_dicts['all_cls_scores'][-1] all_cls_scores = preds_dicts['all_cls_scores'][-1]
all_bbox_preds = preds_dicts['all_bbox_preds'][-1] all_bbox_preds = preds_dicts['all_bbox_preds'][-1]
batch_size = all_cls_scores.size()[0] batch_size = all_cls_scores.size()[0]
predictions_list = [] predictions_list = []
for i in range(batch_size): for i in range(batch_size):
predictions_list.append(self.decode_single(all_cls_scores[i], all_bbox_preds[i])) predictions_list.append(self.decode_single(all_cls_scores[i], all_bbox_preds[i]))
return predictions_list return predictions_list
from mmdet.core.bbox.match_costs import build_match_cost
from .match_cost import BBox3DL1Cost from .match_cost import BBox3DL1Cost
__all__ = ['build_match_cost', 'BBox3DL1Cost'] __all__ = ['build_match_cost', 'BBox3DL1Cost']
\ No newline at end of file
...@@ -24,4 +24,4 @@ class BBox3DL1Cost(object): ...@@ -24,4 +24,4 @@ class BBox3DL1Cost(object):
torch.Tensor: bbox_cost value with weight torch.Tensor: bbox_cost value with weight
""" """
bbox_cost = torch.cdist(bbox_pred, gt_bboxes, p=1) bbox_cost = torch.cdist(bbox_pred, gt_bboxes, p=1)
return bbox_cost * self.weight return bbox_cost * self.weight
\ No newline at end of file
import torch import torch
def normalize_bbox(bboxes, pc_range): def normalize_bbox(bboxes, pc_range):
cx = bboxes[..., 0:1] cx = bboxes[..., 0:1]
cy = bboxes[..., 1:2] cy = bboxes[..., 1:2]
cz = bboxes[..., 2:3] cz = bboxes[..., 2:3]
...@@ -12,7 +11,7 @@ def normalize_bbox(bboxes, pc_range): ...@@ -12,7 +11,7 @@ def normalize_bbox(bboxes, pc_range):
rot = bboxes[..., 6:7] rot = bboxes[..., 6:7]
if bboxes.size(-1) > 7: if bboxes.size(-1) > 7:
vx = bboxes[..., 7:8] vx = bboxes[..., 7:8]
vy = bboxes[..., 8:9] vy = bboxes[..., 8:9]
normalized_bboxes = torch.cat( normalized_bboxes = torch.cat(
(cx, cy, w, l, cz, h, rot.sin(), rot.cos(), vx, vy), dim=-1 (cx, cy, w, l, cz, h, rot.sin(), rot.cos(), vx, vy), dim=-1
...@@ -23,8 +22,9 @@ def normalize_bbox(bboxes, pc_range): ...@@ -23,8 +22,9 @@ def normalize_bbox(bboxes, pc_range):
) )
return normalized_bboxes return normalized_bboxes
def denormalize_bbox(normalized_bboxes, pc_range): def denormalize_bbox(normalized_bboxes, pc_range):
# rotation # rotation
rot_sine = normalized_bboxes[..., 6:7] rot_sine = normalized_bboxes[..., 6:7]
rot_cosine = normalized_bboxes[..., 7:8] rot_cosine = normalized_bboxes[..., 7:8]
...@@ -34,20 +34,20 @@ def denormalize_bbox(normalized_bboxes, pc_range): ...@@ -34,20 +34,20 @@ def denormalize_bbox(normalized_bboxes, pc_range):
cx = normalized_bboxes[..., 0:1] cx = normalized_bboxes[..., 0:1]
cy = normalized_bboxes[..., 1:2] cy = normalized_bboxes[..., 1:2]
cz = normalized_bboxes[..., 4:5] cz = normalized_bboxes[..., 4:5]
# size # size
w = normalized_bboxes[..., 2:3] w = normalized_bboxes[..., 2:3]
l = normalized_bboxes[..., 3:4] l = normalized_bboxes[..., 3:4]
h = normalized_bboxes[..., 5:6] h = normalized_bboxes[..., 5:6]
w = w.exp() w = w.exp()
l = l.exp() l = l.exp()
h = h.exp() h = h.exp()
if normalized_bboxes.size(-1) > 8: if normalized_bboxes.size(-1) > 8:
# velocity # velocity
vx = normalized_bboxes[:, 8:9] vx = normalized_bboxes[:, 8:9]
vy = normalized_bboxes[:, 9:10] vy = normalized_bboxes[:, 9:10]
denormalized_bboxes = torch.cat([cx, cy, cz, w, l, h, rot, vx, vy], dim=-1) denormalized_bboxes = torch.cat([cx, cy, cz, w, l, h, rot, vx, vy], dim=-1)
else: else:
denormalized_bboxes = torch.cat([cx, cy, cz, w, l, h, rot], dim=-1) denormalized_bboxes = torch.cat([cx, cy, cz, w, l, h, rot], dim=-1)
return denormalized_bboxes return denormalized_bboxes
\ No newline at end of file
from .eval_hooks import CustomDistEvalHook from .eval_hooks import CustomDistEvalHook
\ No newline at end of file
# Note: Considering that MMCV's EvalHook updated its interface in V1.3.16, # Note: Considering that MMCV's EvalHook updated its interface in V1.3.16,
# in order to avoid strong version dependency, we did not directly # in order to avoid strong version dependency, we did not directly
# inherit EvalHook but BaseDistEvalHook. # inherit EvalHook but BaseDistEvalHook.
...@@ -9,9 +8,7 @@ import os.path as osp ...@@ -9,9 +8,7 @@ import os.path as osp
import mmcv import mmcv
import torch.distributed as dist import torch.distributed as dist
from mmcv.runner import DistEvalHook as BaseDistEvalHook from mmcv.runner import DistEvalHook as BaseDistEvalHook
from mmcv.runner import EvalHook as BaseEvalHook
from torch.nn.modules.batchnorm import _BatchNorm from torch.nn.modules.batchnorm import _BatchNorm
from mmdet.core.evaluation.eval_hooks import DistEvalHook
def _calc_dynamic_intervals(start_interval, dynamic_interval_list): def _calc_dynamic_intervals(start_interval, dynamic_interval_list):
...@@ -28,7 +25,7 @@ def _calc_dynamic_intervals(start_interval, dynamic_interval_list): ...@@ -28,7 +25,7 @@ def _calc_dynamic_intervals(start_interval, dynamic_interval_list):
class CustomDistEvalHook(BaseDistEvalHook): class CustomDistEvalHook(BaseDistEvalHook):
def __init__(self, *args, dynamic_intervals=None, **kwargs): def __init__(self, *args, dynamic_intervals=None, **kwargs):
super(CustomDistEvalHook, self).__init__(*args, **kwargs) super(CustomDistEvalHook, self).__init__(*args, **kwargs)
self.use_dynamic_intervals = dynamic_intervals is not None self.use_dynamic_intervals = dynamic_intervals is not None
if self.use_dynamic_intervals: if self.use_dynamic_intervals:
...@@ -73,7 +70,8 @@ class CustomDistEvalHook(BaseDistEvalHook): ...@@ -73,7 +70,8 @@ class CustomDistEvalHook(BaseDistEvalHook):
if tmpdir is None: if tmpdir is None:
tmpdir = osp.join(runner.work_dir, '.eval_hook') tmpdir = osp.join(runner.work_dir, '.eval_hook')
from projects.mmdet3d_plugin.bevformer.apis.test import custom_multi_gpu_test # to solve circlur import from projects.mmdet3d_plugin.bevformer.apis.test import \
custom_multi_gpu_test # to solve circlur import
results = custom_multi_gpu_test( results = custom_multi_gpu_test(
runner.model, runner.model,
...@@ -86,7 +84,6 @@ class CustomDistEvalHook(BaseDistEvalHook): ...@@ -86,7 +84,6 @@ class CustomDistEvalHook(BaseDistEvalHook):
# key_score = self.evaluate(runner, results) # key_score = self.evaluate(runner, results)
self.dataloader.dataset.evaluate_miou(results, self.dataloader.dataset.evaluate_miou(results,
runner=runner) runner=runner)
# if self.save_best: # if self.save_best:
# self._save_ckpt(runner, key_score) # self._save_ckpt(runner, key_score)
from .builder import custom_build_dataset
from .nuscenes_dataset import CustomNuScenesDataset from .nuscenes_dataset import CustomNuScenesDataset
from .nuscenes_occ import NuSceneOcc from .nuscenes_occ import NuSceneOcc
from .builder import custom_build_dataset
__all__ = [ __all__ = [
'CustomNuScenesDataset' 'CustomNuScenesDataset'
......
# Copyright (c) OpenMMLab. All rights reserved. # Copyright (c) OpenMMLab. All rights reserved.
import copy import copy
import platform import platform
...@@ -8,13 +7,14 @@ from functools import partial ...@@ -8,13 +7,14 @@ from functools import partial
import numpy as np import numpy as np
from mmcv.parallel import collate from mmcv.parallel import collate
from mmcv.runner import get_dist_info from mmcv.runner import get_dist_info
from mmcv.utils import Registry, build_from_cfg
from torch.utils.data import DataLoader
from mmdet.datasets.samplers import GroupSampler from mmdet.datasets.samplers import GroupSampler
from projects.mmdet3d_plugin.datasets.samplers.group_sampler import DistributedGroupSampler from projects.mmdet3d_plugin.datasets.samplers.distributed_sampler import \
from projects.mmdet3d_plugin.datasets.samplers.distributed_sampler import DistributedSampler DistributedSampler
from projects.mmdet3d_plugin.datasets.samplers.group_sampler import \
DistributedGroupSampler
from projects.mmdet3d_plugin.datasets.samplers.sampler import build_sampler from projects.mmdet3d_plugin.datasets.samplers.sampler import build_sampler
from torch.utils.data import DataLoader
def build_dataloader(dataset, def build_dataloader(dataset,
samples_per_gpu, samples_per_gpu,
...@@ -48,24 +48,26 @@ def build_dataloader(dataset, ...@@ -48,24 +48,26 @@ def build_dataloader(dataset,
# DistributedGroupSampler will definitely shuffle the data to satisfy # DistributedGroupSampler will definitely shuffle the data to satisfy
# that images on each GPU are in the same group # that images on each GPU are in the same group
if shuffle: if shuffle:
sampler = build_sampler(shuffler_sampler if shuffler_sampler is not None else dict(type='DistributedGroupSampler'), sampler = build_sampler(
dict( shuffler_sampler if shuffler_sampler is not None else dict(type='DistributedGroupSampler'),
dataset=dataset, dict(
samples_per_gpu=samples_per_gpu, dataset=dataset,
num_replicas=world_size, samples_per_gpu=samples_per_gpu,
rank=rank, num_replicas=world_size,
seed=seed) rank=rank,
) seed=seed)
)
else: else:
sampler = build_sampler(nonshuffler_sampler if nonshuffler_sampler is not None else dict(type='DistributedSampler'), sampler = build_sampler(
dict( nonshuffler_sampler if nonshuffler_sampler is not None else dict(type='DistributedSampler'),
dataset=dataset, dict(
num_replicas=world_size, dataset=dataset,
rank=rank, num_replicas=world_size,
shuffle=shuffle, rank=rank,
seed=seed) shuffle=shuffle,
) seed=seed)
)
batch_size = samples_per_gpu batch_size = samples_per_gpu
num_workers = workers_per_gpu num_workers = workers_per_gpu
...@@ -103,14 +105,15 @@ def worker_init_fn(worker_id, num_workers, rank, seed): ...@@ -103,14 +105,15 @@ def worker_init_fn(worker_id, num_workers, rank, seed):
# Copyright (c) OpenMMLab. All rights reserved. # Copyright (c) OpenMMLab. All rights reserved.
import platform import platform
from mmcv.utils import Registry, build_from_cfg
from mmcv.utils import Registry, build_from_cfg
from mmdet.datasets import DATASETS from mmdet.datasets import DATASETS
from mmdet.datasets.builder import _concat_dataset from mmdet.datasets.builder import _concat_dataset
if platform.system() != 'Windows': if platform.system() != 'Windows':
# https://github.com/pytorch/pytorch/issues/973 # https://github.com/pytorch/pytorch/issues/973
import resource import resource
rlimit = resource.getrlimit(resource.RLIMIT_NOFILE) rlimit = resource.getrlimit(resource.RLIMIT_NOFILE)
base_soft_limit = rlimit[0] base_soft_limit = rlimit[0]
hard_limit = rlimit[1] hard_limit = rlimit[1]
......
import copy import copy
import random
from os import path as osp
import mmcv
import numpy as np import numpy as np
from mmdet.datasets import DATASETS import torch
from mmcv.parallel import DataContainer as DC
from mmdet3d.core.bbox import LiDARInstance3DBoxes
from mmdet3d.datasets import NuScenesDataset from mmdet3d.datasets import NuScenesDataset
import mmcv
from os import path as osp
from mmdet.datasets import DATASETS from mmdet.datasets import DATASETS
import torch from nuscenes.eval.common.utils import Quaternion, quaternion_yaw
import numpy as np
from nuscenes.eval.common.utils import quaternion_yaw, Quaternion
from mmdet3d.core.bbox import Box3DMode, Coord3DMode, LiDARInstance3DBoxes
from .nuscnes_eval import NuScenesEval_custom from .nuscnes_eval import NuScenesEval_custom
from projects.mmdet3d_plugin.models.utils.visual import save_tensor
from mmcv.parallel import DataContainer as DC
import random
@DATASETS.register_module() @DATASETS.register_module()
...@@ -28,7 +26,7 @@ class CustomNuScenesDataset(NuScenesDataset): ...@@ -28,7 +26,7 @@ class CustomNuScenesDataset(NuScenesDataset):
self.queue_length = queue_length self.queue_length = queue_length
self.overlap_test = overlap_test self.overlap_test = overlap_test
self.bev_size = bev_size self.bev_size = bev_size
def prepare_train_data(self, index): def prepare_train_data(self, index):
""" """
Training data preparation. Training data preparation.
...@@ -38,7 +36,7 @@ class CustomNuScenesDataset(NuScenesDataset): ...@@ -38,7 +36,7 @@ class CustomNuScenesDataset(NuScenesDataset):
dict: Training data dict of the corresponding index. dict: Training data dict of the corresponding index.
""" """
queue = [] queue = []
index_list = list(range(index-self.queue_length, index)) index_list = list(range(index - self.queue_length, index))
random.shuffle(index_list) random.shuffle(index_list)
index_list = sorted(index_list[1:]) index_list = sorted(index_list[1:])
index_list.append(index) index_list.append(index)
...@@ -55,7 +53,6 @@ class CustomNuScenesDataset(NuScenesDataset): ...@@ -55,7 +53,6 @@ class CustomNuScenesDataset(NuScenesDataset):
queue.append(example) queue.append(example)
return self.union2one(queue) return self.union2one(queue)
def union2one(self, queue): def union2one(self, queue):
imgs_list = [each['img'].data for each in queue] imgs_list = [each['img'].data for each in queue]
metas_map = {} metas_map = {}
...@@ -127,8 +124,6 @@ class CustomNuScenesDataset(NuScenesDataset): ...@@ -127,8 +124,6 @@ class CustomNuScenesDataset(NuScenesDataset):
box_dim=gt_bboxes_3d.shape[-1], box_dim=gt_bboxes_3d.shape[-1],
origin=(0.5, 0.5, 0.5)).convert_to(self.box_mode_3d) origin=(0.5, 0.5, 0.5)).convert_to(self.box_mode_3d)
anns_results = dict( anns_results = dict(
gt_bboxes_3d=gt_bboxes_3d, gt_bboxes_3d=gt_bboxes_3d,
gt_labels_3d=gt_labels_3d, gt_labels_3d=gt_labels_3d,
...@@ -180,7 +175,7 @@ class CustomNuScenesDataset(NuScenesDataset): ...@@ -180,7 +175,7 @@ class CustomNuScenesDataset(NuScenesDataset):
# obtain lidar to image transformation matrix # obtain lidar to image transformation matrix
lidar2cam_r = np.linalg.inv(cam_info['sensor2lidar_rotation']) lidar2cam_r = np.linalg.inv(cam_info['sensor2lidar_rotation'])
lidar2cam_t = cam_info[ lidar2cam_t = cam_info[
'sensor2lidar_translation'] @ lidar2cam_r.T 'sensor2lidar_translation'] @ lidar2cam_r.T
lidar2cam_rt = np.eye(4) lidar2cam_rt = np.eye(4)
lidar2cam_rt[:3, :3] = lidar2cam_r.T lidar2cam_rt[:3, :3] = lidar2cam_r.T
lidar2cam_rt[3, :3] = -lidar2cam_t lidar2cam_rt[3, :3] = -lidar2cam_t
......
import copy import copy
import os import os
import numpy as np import random
from tqdm import tqdm
from mmdet.datasets import DATASETS
from mmdet3d.datasets import NuScenesDataset
import mmcv import mmcv
from os import path as osp
from mmdet.datasets import DATASETS
import torch
import numpy as np import numpy as np
from nuscenes.eval.common.utils import quaternion_yaw, Quaternion import torch
from .nuscnes_eval import NuScenesEval_custom
from projects.mmdet3d_plugin.models.utils.visual import save_tensor
from mmcv.parallel import DataContainer as DC from mmcv.parallel import DataContainer as DC
import random from mmdet3d.datasets import NuScenesDataset
from mmdet.datasets import DATASETS
from nuscenes.eval.common.utils import Quaternion, quaternion_yaw
from nuscenes.utils.geometry_utils import transform_matrix from nuscenes.utils.geometry_utils import transform_matrix
from .occ_metrics import Metric_mIoU, Metric_FScore from tqdm import tqdm
from .occ_metrics import Metric_FScore, Metric_mIoU
@DATASETS.register_module() @DATASETS.register_module()
...@@ -209,8 +206,8 @@ class NuSceneOcc(NuScenesDataset): ...@@ -209,8 +206,8 @@ class NuSceneOcc(NuScenesDataset):
if not os.path.exists(show_dir): if not os.path.exists(show_dir):
os.mkdir(show_dir) os.mkdir(show_dir)
print('\nSaving output and gt in {} for visualization.'.format(show_dir)) print('\nSaving output and gt in {} for visualization.'.format(show_dir))
begin=eval_kwargs.get('begin',None) begin = eval_kwargs.get('begin', None)
end=eval_kwargs.get('end',None) end = eval_kwargs.get('end', None)
self.occ_eval_metrics = Metric_mIoU( self.occ_eval_metrics = Metric_mIoU(
num_classes=18, num_classes=18,
use_lidar_mask=False, use_lidar_mask=False,
...@@ -233,15 +230,14 @@ class NuSceneOcc(NuScenesDataset): ...@@ -233,15 +230,14 @@ class NuSceneOcc(NuScenesDataset):
occ_gt = np.load(os.path.join(self.data_root, info['occ_gt_path'])) occ_gt = np.load(os.path.join(self.data_root, info['occ_gt_path']))
if show_dir is not None: if show_dir is not None:
if begin is not None and end is not None: if begin is not None and end is not None:
if index>= begin and index<end: if index >= begin and index < end:
sample_token = info['token'] sample_token = info['token']
save_path = os.path.join(show_dir,str(index).zfill(4)) save_path = os.path.join(show_dir, str(index).zfill(4))
np.savez_compressed(save_path, pred=occ_pred, gt=occ_gt, sample_token=sample_token) np.savez_compressed(save_path, pred=occ_pred, gt=occ_gt, sample_token=sample_token)
else: else:
sample_token=info['token'] sample_token = info['token']
save_path=os.path.join(show_dir,str(index).zfill(4)) save_path = os.path.join(show_dir, str(index).zfill(4))
np.savez_compressed(save_path,pred=occ_pred,gt=occ_gt,sample_token=sample_token) np.savez_compressed(save_path, pred=occ_pred, gt=occ_gt, sample_token=sample_token)
gt_semantics = occ_gt['semantics'] gt_semantics = occ_gt['semantics']
mask_lidar = occ_gt['mask_lidar'].astype(bool) mask_lidar = occ_gt['mask_lidar'].astype(bool)
...@@ -254,6 +250,3 @@ class NuSceneOcc(NuScenesDataset): ...@@ -254,6 +250,3 @@ class NuSceneOcc(NuScenesDataset):
self.occ_eval_metrics.count_miou() self.occ_eval_metrics.count_miou()
if self.eval_fscore: if self.eval_fscore:
self.fscore_eval_metrics.count_fscore() self.fscore_eval_metrics.count_fscore()
...@@ -3,74 +3,42 @@ import copy ...@@ -3,74 +3,42 @@ import copy
import json import json
import os import os
import time import time
from typing import Tuple, Dict, Any from typing import Any, Dict, Tuple
import torch
import numpy as np
import numpy as np
import tqdm
from matplotlib import pyplot as plt
from nuscenes import NuScenes from nuscenes import NuScenes
from nuscenes.eval.common.config import config_factory from nuscenes.eval.common.config import config_factory
from nuscenes.eval.common.data_classes import EvalBoxes from nuscenes.eval.common.data_classes import EvalBoxes
from nuscenes.eval.detection.data_classes import DetectionConfig from nuscenes.eval.common.loaders import (add_center_dist, filter_eval_boxes,
load_gt, load_prediction)
from nuscenes.eval.common.render import setup_axis
from nuscenes.eval.detection.algo import accumulate, calc_ap, calc_tp
from nuscenes.eval.detection.constants import (DETECTION_NAMES,
PRETTY_DETECTION_NAMES,
PRETTY_TP_METRICS, TP_METRICS,
TP_METRICS_UNITS)
from nuscenes.eval.detection.data_classes import (DetectionBox,
DetectionConfig,
DetectionMetricDataList,
DetectionMetrics)
from nuscenes.eval.detection.evaluate import NuScenesEval from nuscenes.eval.detection.evaluate import NuScenesEval
from pyquaternion import Quaternion from nuscenes.eval.detection.render import (class_pr_curve, dist_pr_curve,
summary_plot)
from nuscenes import NuScenes
from nuscenes.eval.common.data_classes import EvalBoxes
from nuscenes.eval.detection.data_classes import DetectionBox
from nuscenes.eval.detection.utils import category_to_detection_name from nuscenes.eval.detection.utils import category_to_detection_name
from nuscenes.eval.tracking.data_classes import TrackingBox from nuscenes.eval.tracking.data_classes import TrackingBox
from nuscenes.utils.data_classes import Box from nuscenes.utils.data_classes import Box
from nuscenes.utils.geometry_utils import points_in_box from nuscenes.utils.geometry_utils import (BoxVisibility, transform_matrix,
view_points)
from nuscenes.utils.splits import create_splits_scenes from nuscenes.utils.splits import create_splits_scenes
from nuscenes.eval.common.loaders import load_prediction, add_center_dist, filter_eval_boxes from pyquaternion import Quaternion
import tqdm
from nuscenes.utils.geometry_utils import view_points, box_in_image, BoxVisibility, transform_matrix
from torchvision.transforms.functional import rotate
import pycocotools.mask as mask_util
# from projects.mmdet3d_plugin.models.utils.visual import save_tensor
from torchvision.transforms.functional import rotate
import cv2
import argparse
import json
import os
import random
import time
from typing import Tuple, Dict, Any
import numpy as np
from nuscenes import NuScenes
from nuscenes.eval.common.config import config_factory
from nuscenes.eval.common.data_classes import EvalBoxes
from nuscenes.eval.common.loaders import load_prediction, load_gt, add_center_dist, filter_eval_boxes
from nuscenes.eval.detection.algo import accumulate, calc_ap, calc_tp
from nuscenes.eval.detection.constants import TP_METRICS
from nuscenes.eval.detection.data_classes import DetectionConfig, DetectionMetrics, DetectionBox, \
DetectionMetricDataList
from nuscenes.eval.detection.render import summary_plot, class_pr_curve, dist_pr_curve, visualize_sample
from nuscenes.eval.common.utils import quaternion_yaw, Quaternion
from mmdet3d.core.bbox.iou_calculators import BboxOverlaps3D
from IPython import embed
import json
from typing import Any
import numpy as np
from matplotlib import pyplot as plt
from nuscenes import NuScenes
from nuscenes.eval.common.data_classes import EvalBoxes
from nuscenes.eval.common.render import setup_axis
from nuscenes.eval.common.utils import boxes_to_sensor
from nuscenes.eval.detection.constants import TP_METRICS, DETECTION_NAMES, DETECTION_COLORS, TP_METRICS_UNITS, \
PRETTY_DETECTION_NAMES, PRETTY_TP_METRICS
from nuscenes.eval.detection.data_classes import DetectionMetrics, DetectionMetricData, DetectionMetricDataList
from nuscenes.utils.data_classes import LidarPointCloud
from nuscenes.utils.geometry_utils import view_points
# from projects.mmdet3d_plugin.models.utils.visual import save_tensor
Axis = Any Axis = Any
def class_tp_curve(md_list: DetectionMetricDataList, def class_tp_curve(md_list: DetectionMetricDataList,
metrics: DetectionMetrics, metrics: DetectionMetrics,
detection_name: str, detection_name: str,
...@@ -124,7 +92,7 @@ def class_tp_curve(md_list: DetectionMetricDataList, ...@@ -124,7 +92,7 @@ def class_tp_curve(md_list: DetectionMetricDataList,
label = '{}: {:.2f} ({})'.format(PRETTY_TP_METRICS[metric], tp, TP_METRICS_UNITS[metric]) label = '{}: {:.2f} ({})'.format(PRETTY_TP_METRICS[metric], tp, TP_METRICS_UNITS[metric])
if metric == 'trans_err': if metric == 'trans_err':
label += f' ({md.max_recall_ind})' # add recall label += f' ({md.max_recall_ind})' # add recall
print(f'Recall: {detection_name}: {md.max_recall_ind/100}') print(f'Recall: {detection_name}: {md.max_recall_ind / 100}')
ax.plot(recall, error, label=label) ax.plot(recall, error, label=label)
ax.axvline(x=md.max_recall, linestyle='-.', color=(0, 0, 0, 0.3)) ax.axvline(x=md.max_recall, linestyle='-.', color=(0, 0, 0, 0.3))
ax.legend(loc='best') ax.legend(loc='best')
...@@ -211,7 +179,7 @@ def center_in_image(box, intrinsic: np.ndarray, imsize: Tuple[int, int], vis_lev ...@@ -211,7 +179,7 @@ def center_in_image(box, intrinsic: np.ndarray, imsize: Tuple[int, int], vis_lev
elif vis_level == BoxVisibility.NONE: elif vis_level == BoxVisibility.NONE:
return True return True
else: else:
raise ValueError("vis_level: {} not valid".format(vis_level)) raise ValueError('vis_level: {} not valid'.format(vis_level))
def exist_corners_in_image_but_not_all(box, intrinsic: np.ndarray, imsize: Tuple[int, int], def exist_corners_in_image_but_not_all(box, intrinsic: np.ndarray, imsize: Tuple[int, int],
...@@ -259,7 +227,7 @@ def load_gt(nusc: NuScenes, eval_split: str, box_cls, verbose: bool = False): ...@@ -259,7 +227,7 @@ def load_gt(nusc: NuScenes, eval_split: str, box_cls, verbose: bool = False):
print('Loading annotations for {} split from nuScenes version: {}'.format(eval_split, nusc.version)) print('Loading annotations for {} split from nuScenes version: {}'.format(eval_split, nusc.version))
# Read out all sample_tokens in DB. # Read out all sample_tokens in DB.
sample_tokens_all = [s['token'] for s in nusc.sample] sample_tokens_all = [s['token'] for s in nusc.sample]
assert len(sample_tokens_all) > 0, "Error: Database has no samples!" assert len(sample_tokens_all) > 0, 'Error: Database has no samples!'
# Only keep samples from this split. # Only keep samples from this split.
splits = create_splits_scenes() splits = create_splits_scenes()
...@@ -354,7 +322,7 @@ def load_gt(nusc: NuScenes, eval_split: str, box_cls, verbose: bool = False): ...@@ -354,7 +322,7 @@ def load_gt(nusc: NuScenes, eval_split: str, box_cls, verbose: bool = False):
all_annotations.add_boxes(sample_token, sample_boxes) all_annotations.add_boxes(sample_token, sample_boxes)
if verbose: if verbose:
print("Loaded ground truth annotations for {} samples.".format(len(all_annotations.sample_tokens))) print('Loaded ground truth annotations for {} samples.'.format(len(all_annotations.sample_tokens)))
return all_annotations return all_annotations
...@@ -385,8 +353,8 @@ def filter_eval_boxes_by_id(nusc: NuScenes, ...@@ -385,8 +353,8 @@ def filter_eval_boxes_by_id(nusc: NuScenes,
eval_boxes.boxes[sample_token] = filtered_boxes eval_boxes.boxes[sample_token] = filtered_boxes
if verbose: if verbose:
print("=> Original number of boxes: %d" % total) print('=> Original number of boxes: %d' % total)
print("=> After anns based filtering: %d" % anns_filter) print('=> After anns based filtering: %d' % anns_filter)
return eval_boxes return eval_boxes
...@@ -417,13 +385,13 @@ def filter_eval_boxes_by_visibility( ...@@ -417,13 +385,13 @@ def filter_eval_boxes_by_visibility(
eval_boxes.boxes[sample_token] = filtered_boxes eval_boxes.boxes[sample_token] = filtered_boxes
if verbose: if verbose:
print("=> Original number of boxes: %d" % total) print('=> Original number of boxes: %d' % total)
print("=> After visibility based filtering: %d" % anns_filter) print('=> After visibility based filtering: %d' % anns_filter)
return eval_boxes return eval_boxes
def filter_by_sample_token(ori_eval_boxes, valid_sample_tokens=[], verbose=False): def filter_by_sample_token(ori_eval_boxes, valid_sample_tokens=[], verbose=False):
eval_boxes = copy.deepcopy(ori_eval_boxes) eval_boxes = copy.deepcopy(ori_eval_boxes)
for sample_token in eval_boxes.sample_tokens: for sample_token in eval_boxes.sample_tokens:
if sample_token not in valid_sample_tokens: if sample_token not in valid_sample_tokens:
...@@ -498,8 +466,8 @@ def filter_eval_boxes_by_overlap(nusc: NuScenes, ...@@ -498,8 +466,8 @@ def filter_eval_boxes_by_overlap(nusc: NuScenes,
verbose = True verbose = True
if verbose: if verbose:
print("=> Original number of boxes: %d" % total) print('=> Original number of boxes: %d' % total)
print("=> After anns based filtering: %d" % anns_filter) print('=> After anns based filtering: %d' % anns_filter)
return eval_boxes return eval_boxes
...@@ -620,7 +588,6 @@ class NuScenesEval_custom(NuScenesEval): ...@@ -620,7 +588,6 @@ class NuScenesEval_custom(NuScenesEval):
self.pred_boxes = filter_by_sample_token(self.all_preds, valid_tokens) self.pred_boxes = filter_by_sample_token(self.all_preds, valid_tokens)
self.sample_tokens = self.gt_boxes.sample_tokens self.sample_tokens = self.gt_boxes.sample_tokens
def evaluate(self) -> Tuple[DetectionMetrics, DetectionMetricDataList]: def evaluate(self) -> Tuple[DetectionMetrics, DetectionMetricDataList]:
""" """
Performs the actual evaluation. Performs the actual evaluation.
...@@ -698,7 +665,7 @@ class NuScenesEval_custom(NuScenesEval): ...@@ -698,7 +665,7 @@ class NuScenesEval_custom(NuScenesEval):
savepath=savepath('dist_pr_' + str(dist_th))) savepath=savepath('dist_pr_' + str(dist_th)))
if __name__ == "__main__": if __name__ == '__main__':
# Settings. # Settings.
parser = argparse.ArgumentParser(description='Evaluate nuScenes detection results.', parser = argparse.ArgumentParser(description='Evaluate nuScenes detection results.',
...@@ -746,6 +713,6 @@ if __name__ == "__main__": ...@@ -746,6 +713,6 @@ if __name__ == "__main__":
nusc_eval.update_gt(type_='vis', visibility=vis) nusc_eval.update_gt(type_='vis', visibility=vis)
print(f'================ {vis} ===============') print(f'================ {vis} ===============')
nusc_eval.main(plot_examples=plot_examples_, render_curves=render_curves_) nusc_eval.main(plot_examples=plot_examples_, render_curves=render_curves_)
#for index in range(1, 41): # for index in range(1, 41):
# nusc_eval.update_gt(type_='ord', index=index) # nusc_eval.update_gt(type_='ord', index=index)
# #
import numpy as np import os
import os from functools import reduce
from pathlib import Path
from tqdm import tqdm import numpy as np
import pickle as pkl from sklearn.neighbors import KDTree
import argparse from termcolor import colored
import time
import torch np.seterr(divide='ignore', invalid='ignore')
import sys, platform os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'
from sklearn.neighbors import KDTree
from termcolor import colored
from pathlib import Path def pcolor(string, color, on_color=None, attrs=None):
from copy import deepcopy """
from functools import reduce Produces a colored string for printing
np.seterr(divide='ignore', invalid='ignore') Parameters
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" ----------
string : str
String that will be colored
def pcolor(string, color, on_color=None, attrs=None): color : str
""" Color to use
Produces a colored string for printing on_color : str
Background color to use
Parameters attrs : list of str
---------- Different attributes for the string
string : str
String that will be colored Returns
color : str -------
Color to use string: str
on_color : str Colored string
Background color to use """
attrs : list of str return colored(string, color, on_color, attrs)
Different attributes for the string
Returns def getCellCoordinates(points, voxelSize):
------- return (points / voxelSize).astype(np.int)
string: str
Colored string
""" def getNumUniqueCells(cells):
return colored(string, color, on_color, attrs) M = cells.max() + 1
return np.unique(cells[:, 0] + M * cells[:, 1] + M ** 2 * cells[:, 2]).shape[0]
def getCellCoordinates(points, voxelSize):
return (points / voxelSize).astype(np.int) class Metric_mIoU():
def __init__(self,
save_dir='.',
def getNumUniqueCells(cells): num_classes=18,
M = cells.max() + 1 use_lidar_mask=False,
return np.unique(cells[:, 0] + M * cells[:, 1] + M ** 2 * cells[:, 2]).shape[0] use_image_mask=False,
):
self.class_names = ['others', 'barrier', 'bicycle', 'bus', 'car', 'construction_vehicle',
class Metric_mIoU(): 'motorcycle', 'pedestrian', 'traffic_cone', 'trailer', 'truck',
def __init__(self, 'driveable_surface', 'other_flat', 'sidewalk',
save_dir='.', 'terrain', 'manmade', 'vegetation', 'free']
num_classes=18, self.save_dir = save_dir
use_lidar_mask=False, self.use_lidar_mask = use_lidar_mask
use_image_mask=False, self.use_image_mask = use_image_mask
): self.num_classes = num_classes
self.class_names = ['others','barrier', 'bicycle', 'bus', 'car', 'construction_vehicle',
'motorcycle', 'pedestrian', 'traffic_cone', 'trailer', 'truck', self.point_cloud_range = [-40.0, -40.0, -1.0, 40.0, 40.0, 5.4]
'driveable_surface', 'other_flat', 'sidewalk', self.occupancy_size = [0.4, 0.4, 0.4]
'terrain', 'manmade', 'vegetation','free'] self.voxel_size = 0.4
self.save_dir = save_dir self.occ_xdim = int((self.point_cloud_range[3] - self.point_cloud_range[0]) / self.occupancy_size[0])
self.use_lidar_mask = use_lidar_mask self.occ_ydim = int((self.point_cloud_range[4] - self.point_cloud_range[1]) / self.occupancy_size[1])
self.use_image_mask = use_image_mask self.occ_zdim = int((self.point_cloud_range[5] - self.point_cloud_range[2]) / self.occupancy_size[2])
self.num_classes = num_classes self.voxel_num = self.occ_xdim * self.occ_ydim * self.occ_zdim
self.hist = np.zeros((self.num_classes, self.num_classes))
self.point_cloud_range = [-40.0, -40.0, -1.0, 40.0, 40.0, 5.4] self.cnt = 0
self.occupancy_size = [0.4, 0.4, 0.4]
self.voxel_size = 0.4 def hist_info(self, n_cl, pred, gt):
self.occ_xdim = int((self.point_cloud_range[3] - self.point_cloud_range[0]) / self.occupancy_size[0]) """
self.occ_ydim = int((self.point_cloud_range[4] - self.point_cloud_range[1]) / self.occupancy_size[1]) build confusion matrix
self.occ_zdim = int((self.point_cloud_range[5] - self.point_cloud_range[2]) / self.occupancy_size[2]) # empty classes:0
self.voxel_num = self.occ_xdim * self.occ_ydim * self.occ_zdim non-empty class: 0-16
self.hist = np.zeros((self.num_classes, self.num_classes)) free voxel class: 17
self.cnt = 0
Args:
def hist_info(self, n_cl, pred, gt): n_cl (int): num_classes_occupancy
""" pred (1-d array): pred_occupancy_label
build confusion matrix gt (1-d array): gt_occupancu_label
# empty classes:0
non-empty class: 0-16 Returns:
free voxel class: 17 tuple:(hist, correctly number_predicted_labels, num_labelled_sample)
"""
Args: assert pred.shape == gt.shape
n_cl (int): num_classes_occupancy k = (gt >= 0) & (gt < n_cl) # exclude 255
pred (1-d array): pred_occupancy_label labeled = np.sum(k)
gt (1-d array): gt_occupancu_label correct = np.sum((pred[k] == gt[k]))
Returns: return (
tuple:(hist, correctly number_predicted_labels, num_labelled_sample) np.bincount(
""" n_cl * gt[k].astype(int) + pred[k].astype(int), minlength=n_cl ** 2
assert pred.shape == gt.shape ).reshape(n_cl, n_cl),
k = (gt >= 0) & (gt < n_cl) # exclude 255 correct,
labeled = np.sum(k) labeled,
correct = np.sum((pred[k] == gt[k])) )
return ( def per_class_iu(self, hist):
np.bincount(
n_cl * gt[k].astype(int) + pred[k].astype(int), minlength=n_cl ** 2 return np.diag(hist) / (hist.sum(1) + hist.sum(0) - np.diag(hist))
).reshape(n_cl, n_cl),
correct, def compute_mIoU(self, pred, label, n_classes):
labeled, hist = np.zeros((n_classes, n_classes))
) new_hist, correct, labeled = self.hist_info(n_classes, pred.flatten(), label.flatten())
hist += new_hist
def per_class_iu(self, hist): mIoUs = self.per_class_iu(hist)
# for ind_class in range(n_classes):
return np.diag(hist) / (hist.sum(1) + hist.sum(0) - np.diag(hist)) # print(str(round(mIoUs[ind_class] * 100, 2)))
# print('===> mIoU: ' + str(round(np.nanmean(mIoUs) * 100, 2)))
def compute_mIoU(self, pred, label, n_classes): return round(np.nanmean(mIoUs) * 100, 2), hist
hist = np.zeros((n_classes, n_classes))
new_hist, correct, labeled = self.hist_info(n_classes, pred.flatten(), label.flatten()) def add_batch(self, semantics_pred, semantics_gt, mask_lidar, mask_camera):
hist += new_hist self.cnt += 1
mIoUs = self.per_class_iu(hist) if self.use_image_mask:
# for ind_class in range(n_classes): masked_semantics_gt = semantics_gt[mask_camera]
# print(str(round(mIoUs[ind_class] * 100, 2))) masked_semantics_pred = semantics_pred[mask_camera]
# print('===> mIoU: ' + str(round(np.nanmean(mIoUs) * 100, 2))) elif self.use_lidar_mask:
return round(np.nanmean(mIoUs) * 100, 2), hist masked_semantics_gt = semantics_gt[mask_lidar]
masked_semantics_pred = semantics_pred[mask_lidar]
else:
def add_batch(self,semantics_pred,semantics_gt,mask_lidar,mask_camera): masked_semantics_gt = semantics_gt
self.cnt += 1 masked_semantics_pred = semantics_pred
if self.use_image_mask:
masked_semantics_gt = semantics_gt[mask_camera] # # pred = np.random.randint(low=0, high=17, size=masked_semantics.shape)
masked_semantics_pred = semantics_pred[mask_camera] _, _hist = self.compute_mIoU(masked_semantics_pred, masked_semantics_gt, self.num_classes)
elif self.use_lidar_mask: self.hist += _hist
masked_semantics_gt = semantics_gt[mask_lidar]
masked_semantics_pred = semantics_pred[mask_lidar] def count_miou(self):
else: mIoU = self.per_class_iu(self.hist)
masked_semantics_gt = semantics_gt # assert cnt == num_samples, 'some samples are not included in the miou calculation'
masked_semantics_pred = semantics_pred print(f'===> per class IoU of {self.cnt} samples:')
for ind_class in range(self.num_classes - 1):
# # pred = np.random.randint(low=0, high=17, size=masked_semantics.shape) print(f'===> {self.class_names[ind_class]} - IoU = ' + str(round(mIoU[ind_class] * 100, 2)))
_, _hist = self.compute_mIoU(masked_semantics_pred, masked_semantics_gt, self.num_classes)
self.hist += _hist print(f'===> mIoU of {self.cnt} samples: ' + str(round(np.nanmean(mIoU[:self.num_classes - 1]) * 100, 2)))
# print(f'===> sample-wise averaged mIoU of {cnt} samples: ' + str(round(np.nanmean(mIoU_avg), 2)))
def count_miou(self):
mIoU = self.per_class_iu(self.hist) # return mIoU
# assert cnt == num_samples, 'some samples are not included in the miou calculation'
print(f'===> per class IoU of {self.cnt} samples:')
for ind_class in range(self.num_classes-1): class Metric_FScore():
print(f'===> {self.class_names[ind_class]} - IoU = ' + str(round(mIoU[ind_class] * 100, 2))) def __init__(self,
print(f'===> mIoU of {self.cnt} samples: ' + str(round(np.nanmean(mIoU[:self.num_classes-1]) * 100, 2))) leaf_size=10,
# print(f'===> sample-wise averaged mIoU of {cnt} samples: ' + str(round(np.nanmean(mIoU_avg), 2))) threshold_acc=0.6,
threshold_complete=0.6,
# return mIoU voxel_size=[0.4, 0.4, 0.4],
range=[-40, -40, -1, 40, 40, 5.4],
void=[17, 255],
class Metric_FScore(): use_lidar_mask=False,
def __init__(self, use_image_mask=False, ) -> None:
leaf_size=10, self.leaf_size = leaf_size
threshold_acc=0.6, self.threshold_acc = threshold_acc
threshold_complete=0.6, self.threshold_complete = threshold_complete
voxel_size=[0.4, 0.4, 0.4], self.voxel_size = voxel_size
range=[-40, -40, -1, 40, 40, 5.4], self.range = range
void=[17, 255], self.void = void
use_lidar_mask=False, self.use_lidar_mask = use_lidar_mask
use_image_mask=False, ) -> None: self.use_image_mask = use_image_mask
self.cnt = 0
self.leaf_size = leaf_size self.tot_acc = 0.
self.threshold_acc = threshold_acc self.tot_cmpl = 0.
self.threshold_complete = threshold_complete self.tot_f1_mean = 0.
self.voxel_size = voxel_size self.eps = 1e-8
self.range = range
self.void = void def voxel2points(self, voxel):
self.use_lidar_mask = use_lidar_mask # occIdx = torch.where(torch.logical_and(voxel != FREE, voxel != NOT_OBSERVED))
self.use_image_mask = use_image_mask # if isinstance(voxel, np.ndarray): voxel = torch.from_numpy(voxel)
self.cnt=0 mask = np.logical_not(reduce(np.logical_or, [voxel == self.void[i] for i in range(len(self.void))]))
self.tot_acc = 0. occIdx = np.where(mask)
self.tot_cmpl = 0.
self.tot_f1_mean = 0. points = np.concatenate((occIdx[0][:, None] * self.voxel_size[0] + self.voxel_size[0] / 2 + self.range[0], \
self.eps = 1e-8 occIdx[1][:, None] * self.voxel_size[1] + self.voxel_size[1] / 2 + self.range[1], \
occIdx[2][:, None] * self.voxel_size[2] + self.voxel_size[2] / 2 + self.range[2]),
axis=1)
return points
def voxel2points(self, voxel):
# occIdx = torch.where(torch.logical_and(voxel != FREE, voxel != NOT_OBSERVED)) def add_batch(self, semantics_pred, semantics_gt, mask_lidar, mask_camera):
# if isinstance(voxel, np.ndarray): voxel = torch.from_numpy(voxel)
mask = np.logical_not(reduce(np.logical_or, [voxel == self.void[i] for i in range(len(self.void))])) # for scene_token in tqdm(preds_dict.keys()):
occIdx = np.where(mask) self.cnt += 1
points = np.concatenate((occIdx[0][:, None] * self.voxel_size[0] + self.voxel_size[0] / 2 + self.range[0], \ if self.use_image_mask:
occIdx[1][:, None] * self.voxel_size[1] + self.voxel_size[1] / 2 + self.range[1], \ semantics_gt[mask_camera == False] = 255
occIdx[2][:, None] * self.voxel_size[2] + self.voxel_size[2] / 2 + self.range[2]), semantics_pred[mask_camera == False] = 255
axis=1) elif self.use_lidar_mask:
return points semantics_gt[mask_lidar == False] = 255
semantics_pred[mask_lidar == False] = 255
def add_batch(self,semantics_pred,semantics_gt,mask_lidar,mask_camera ): else:
pass
# for scene_token in tqdm(preds_dict.keys()):
self.cnt += 1 ground_truth = self.voxel2points(semantics_gt)
prediction = self.voxel2points(semantics_pred)
if self.use_image_mask: if prediction.shape[0] == 0:
accuracy = 0
semantics_gt[mask_camera == False] = 255 completeness = 0
semantics_pred[mask_camera == False] = 255 fmean = 0
elif self.use_lidar_mask:
semantics_gt[mask_lidar == False] = 255 else:
semantics_pred[mask_lidar == False] = 255 prediction_tree = KDTree(prediction, leaf_size=self.leaf_size)
else: ground_truth_tree = KDTree(ground_truth, leaf_size=self.leaf_size)
pass complete_distance, _ = prediction_tree.query(ground_truth)
complete_distance = complete_distance.flatten()
ground_truth = self.voxel2points(semantics_gt)
prediction = self.voxel2points(semantics_pred) accuracy_distance, _ = ground_truth_tree.query(prediction)
if prediction.shape[0] == 0: accuracy_distance = accuracy_distance.flatten()
accuracy=0
completeness=0 # evaluate completeness
fmean=0 complete_mask = complete_distance < self.threshold_complete
completeness = complete_mask.mean()
else:
prediction_tree = KDTree(prediction, leaf_size=self.leaf_size) # evalute accuracy
ground_truth_tree = KDTree(ground_truth, leaf_size=self.leaf_size) accuracy_mask = accuracy_distance < self.threshold_acc
complete_distance, _ = prediction_tree.query(ground_truth) accuracy = accuracy_mask.mean()
complete_distance = complete_distance.flatten()
fmean = 2.0 / (1 / (accuracy + self.eps) + 1 / (completeness + self.eps))
accuracy_distance, _ = ground_truth_tree.query(prediction)
accuracy_distance = accuracy_distance.flatten() self.tot_acc += accuracy
self.tot_cmpl += completeness
# evaluate completeness self.tot_f1_mean += fmean
complete_mask = complete_distance < self.threshold_complete
completeness = complete_mask.mean() def count_fscore(self, ):
base_color, attrs = 'red', ['bold', 'dark']
# evalute accuracy print(pcolor('\n######## F score: {} #######'.format(self.tot_f1_mean / self.cnt), base_color, attrs=attrs))
accuracy_mask = accuracy_distance < self.threshold_acc
accuracy = accuracy_mask.mean()
fmean = 2.0 / (1 / (accuracy+self.eps) + 1 / (completeness+self.eps))
self.tot_acc += accuracy
self.tot_cmpl += completeness
self.tot_f1_mean += fmean
def count_fscore(self,):
base_color, attrs = 'red', ['bold', 'dark']
print(pcolor('\n######## F score: {} #######'.format(self.tot_f1_mean / self.cnt), base_color, attrs=attrs))
from .transform_3d import (
PadMultiViewImage, NormalizeMultiviewImage,
PhotoMetricDistortionMultiViewImage, CustomCollect3D, RandomScaleImageMultiViewImage)
from .formating import CustomDefaultFormatBundle3D from .formating import CustomDefaultFormatBundle3D
from .loading import LoadOccGTFromFile from .loading import LoadOccGTFromFile
from .transform_3d import (CustomCollect3D, NormalizeMultiviewImage,
PadMultiViewImage,
PhotoMetricDistortionMultiViewImage,
RandomScaleImageMultiViewImage)
__all__ = [ __all__ = [
'PadMultiViewImage', 'NormalizeMultiviewImage', 'PadMultiViewImage', 'NormalizeMultiviewImage',
'PhotoMetricDistortionMultiViewImage', 'CustomDefaultFormatBundle3D', 'CustomCollect3D', 'RandomScaleImageMultiViewImage' 'PhotoMetricDistortionMultiViewImage', 'CustomDefaultFormatBundle3D', 'CustomCollect3D',
] 'RandomScaleImageMultiViewImage'
\ No newline at end of file ]
# Copyright (c) OpenMMLab. All rights reserved.
# Copyright (c) OpenMMLab. All rights reserved. from mmcv.parallel import DataContainer as DC
import numpy as np from mmdet3d.datasets.pipelines import DefaultFormatBundle3D
from mmcv.parallel import DataContainer as DC from mmdet.datasets.builder import PIPELINES
from mmdet.datasets.pipelines import to_tensor
from mmdet3d.core.bbox import BaseInstance3DBoxes
from mmdet3d.core.points import BasePoints
from mmdet.datasets.builder import PIPELINES @PIPELINES.register_module()
from mmdet.datasets.pipelines import to_tensor class CustomDefaultFormatBundle3D(DefaultFormatBundle3D):
from mmdet3d.datasets.pipelines import DefaultFormatBundle3D """Default formatting bundle.
It simplifies the pipeline of formatting common fields for voxels,
@PIPELINES.register_module() including "proposals", "gt_bboxes", "gt_labels", "gt_masks" and
class CustomDefaultFormatBundle3D(DefaultFormatBundle3D): "gt_semantic_seg".
"""Default formatting bundle. These fields are formatted as follows.
It simplifies the pipeline of formatting common fields for voxels, - img: (1)transpose, (2)to tensor, (3)to DataContainer (stack=True)
including "proposals", "gt_bboxes", "gt_labels", "gt_masks" and - proposals: (1)to tensor, (2)to DataContainer
"gt_semantic_seg". - gt_bboxes: (1)to tensor, (2)to DataContainer
These fields are formatted as follows. - gt_bboxes_ignore: (1)to tensor, (2)to DataContainer
- img: (1)transpose, (2)to tensor, (3)to DataContainer (stack=True) - gt_labels: (1)to tensor, (2)to DataContainer
- proposals: (1)to tensor, (2)to DataContainer """
- gt_bboxes: (1)to tensor, (2)to DataContainer
- gt_bboxes_ignore: (1)to tensor, (2)to DataContainer def __call__(self, results):
- gt_labels: (1)to tensor, (2)to DataContainer """Call function to transform and format common fields in results.
""" Args:
results (dict): Result dict contains the data to convert.
def __call__(self, results): Returns:
"""Call function to transform and format common fields in results. dict: The result dict contains the data that is formatted with
Args: default bundle.
results (dict): Result dict contains the data to convert. """
Returns: # Format 3D data
dict: The result dict contains the data that is formatted with results = super(CustomDefaultFormatBundle3D, self).__call__(results)
default bundle. results['gt_map_masks'] = DC(
""" to_tensor(results['gt_map_masks']), stack=True)
# Format 3D data
results = super(CustomDefaultFormatBundle3D, self).__call__(results) return results
results['gt_map_masks'] = DC(
to_tensor(results['gt_map_masks']), stack=True)
return results
\ No newline at end of file
import numpy as np import os
from numpy import random
import mmcv import numpy as np
from mmdet.datasets.builder import PIPELINES from mmdet.datasets.builder import PIPELINES
from mmcv.parallel import DataContainer as DC
import os
@PIPELINES.register_module()
@PIPELINES.register_module() class LoadOccGTFromFile(object):
class LoadOccGTFromFile(object): """Load multi channel images from a list of separate channel files.
"""Load multi channel images from a list of separate channel files.
Expects results['img_filename'] to be a list of filenames.
Expects results['img_filename'] to be a list of filenames. note that we read image in BGR style to align with opencv.imread
note that we read image in BGR style to align with opencv.imread Args:
Args: to_float32 (bool): Whether to convert the img to float32.
to_float32 (bool): Whether to convert the img to float32. Defaults to False.
Defaults to False. color_type (str): Color type of the file. Defaults to 'unchanged'.
color_type (str): Color type of the file. Defaults to 'unchanged'. """
"""
def __init__(
def __init__( self,
self, data_root,
data_root, ):
): self.data_root = data_root
self.data_root = data_root
def __call__(self, results):
def __call__(self, results): # print(results.keys())
# print(results.keys()) occ_gt_path = results['occ_gt_path']
occ_gt_path = results['occ_gt_path'] occ_gt_path = os.path.join(self.data_root, occ_gt_path)
occ_gt_path = os.path.join(self.data_root,occ_gt_path)
occ_labels = np.load(occ_gt_path)
occ_labels = np.load(occ_gt_path) semantics = occ_labels['semantics']
semantics = occ_labels['semantics'] mask_lidar = occ_labels['mask_lidar']
mask_lidar = occ_labels['mask_lidar'] mask_camera = occ_labels['mask_camera']
mask_camera = occ_labels['mask_camera']
results['voxel_semantics'] = semantics
results['voxel_semantics'] = semantics results['mask_lidar'] = mask_lidar
results['mask_lidar'] = mask_lidar results['mask_camera'] = mask_camera
results['mask_camera'] = mask_camera
return results
return results def __repr__(self):
"""str: Return a string that describes the module."""
def __repr__(self): return "{} (data_root={}')".format(
"""str: Return a string that describes the module.""" self.__class__.__name__, self.data_root)
return "{} (data_root={}')".format(
self.__class__.__name__, self.data_root)
\ No newline at end of file
import numpy as np
from numpy import random
import mmcv import mmcv
from mmdet.datasets.builder import PIPELINES import numpy as np
from mmcv.parallel import DataContainer as DC from mmcv.parallel import DataContainer as DC
import os from mmdet.datasets.builder import PIPELINES
from numpy import random
@PIPELINES.register_module() @PIPELINES.register_module()
...@@ -36,7 +33,7 @@ class PadMultiViewImage(object): ...@@ -36,7 +33,7 @@ class PadMultiViewImage(object):
elif self.size_divisor is not None: elif self.size_divisor is not None:
padded_img = [mmcv.impad_to_multiple( padded_img = [mmcv.impad_to_multiple(
img, self.size_divisor, pad_val=self.pad_val) for img in results['img']] img, self.size_divisor, pad_val=self.pad_val) for img in results['img']]
results['ori_shape'] = [img.shape for img in results['img']] results['ori_shape'] = [img.shape for img in results['img']]
results['img'] = padded_img results['img'] = padded_img
results['img_shape'] = [img.shape for img in padded_img] results['img_shape'] = [img.shape for img in padded_img]
...@@ -78,7 +75,6 @@ class NormalizeMultiviewImage(object): ...@@ -78,7 +75,6 @@ class NormalizeMultiviewImage(object):
self.std = np.array(std, dtype=np.float32) self.std = np.array(std, dtype=np.float32)
self.to_rgb = to_rgb self.to_rgb = to_rgb
def __call__(self, results): def __call__(self, results):
"""Call function to normalize images. """Call function to normalize images.
Args: Args:
...@@ -140,12 +136,12 @@ class PhotoMetricDistortionMultiViewImage: ...@@ -140,12 +136,12 @@ class PhotoMetricDistortionMultiViewImage:
new_imgs = [] new_imgs = []
for img in imgs: for img in imgs:
assert img.dtype == np.float32, \ assert img.dtype == np.float32, \
'PhotoMetricDistortion needs the input image of dtype np.float32,'\ 'PhotoMetricDistortion needs the input image of dtype np.float32,' \
' please set "to_float32=True" in "LoadImageFromFile" pipeline' ' please set "to_float32=True" in "LoadImageFromFile" pipeline'
# random brightness # random brightness
if random.randint(2): if random.randint(2):
delta = random.uniform(-self.brightness_delta, delta = random.uniform(-self.brightness_delta,
self.brightness_delta) self.brightness_delta)
img += delta img += delta
# mode == 0 --> do random contrast first # mode == 0 --> do random contrast first
...@@ -154,7 +150,7 @@ class PhotoMetricDistortionMultiViewImage: ...@@ -154,7 +150,7 @@ class PhotoMetricDistortionMultiViewImage:
if mode == 1: if mode == 1:
if random.randint(2): if random.randint(2):
alpha = random.uniform(self.contrast_lower, alpha = random.uniform(self.contrast_lower,
self.contrast_upper) self.contrast_upper)
img *= alpha img *= alpha
# convert color from BGR to HSV # convert color from BGR to HSV
...@@ -163,7 +159,7 @@ class PhotoMetricDistortionMultiViewImage: ...@@ -163,7 +159,7 @@ class PhotoMetricDistortionMultiViewImage:
# random saturation # random saturation
if random.randint(2): if random.randint(2):
img[..., 1] *= random.uniform(self.saturation_lower, img[..., 1] *= random.uniform(self.saturation_lower,
self.saturation_upper) self.saturation_upper)
# random hue # random hue
if random.randint(2): if random.randint(2):
...@@ -178,7 +174,7 @@ class PhotoMetricDistortionMultiViewImage: ...@@ -178,7 +174,7 @@ class PhotoMetricDistortionMultiViewImage:
if mode == 0: if mode == 0:
if random.randint(2): if random.randint(2):
alpha = random.uniform(self.contrast_lower, alpha = random.uniform(self.contrast_lower,
self.contrast_upper) self.contrast_upper)
img *= alpha img *= alpha
# randomly swap channels # randomly swap channels
...@@ -199,7 +195,6 @@ class PhotoMetricDistortionMultiViewImage: ...@@ -199,7 +195,6 @@ class PhotoMetricDistortionMultiViewImage:
return repr_str return repr_str
@PIPELINES.register_module() @PIPELINES.register_module()
class CustomCollect3D(object): class CustomCollect3D(object):
"""Collect data from the loader relevant to the specific task. """Collect data from the loader relevant to the specific task.
...@@ -247,7 +242,7 @@ class CustomCollect3D(object): ...@@ -247,7 +242,7 @@ class CustomCollect3D(object):
def __init__(self, def __init__(self,
keys, keys,
meta_keys=('filename', 'ori_shape', 'img_shape', 'lidar2img','ego2lidar', meta_keys=('filename', 'ori_shape', 'img_shape', 'lidar2img', 'ego2lidar',
'depth2img', 'cam2img', 'pad_shape', 'depth2img', 'cam2img', 'pad_shape',
'scale_factor', 'flip', 'pcd_horizontal_flip', 'scale_factor', 'flip', 'pcd_horizontal_flip',
'pcd_vertical_flip', 'box_mode_3d', 'box_type_3d', 'pcd_vertical_flip', 'box_mode_3d', 'box_type_3d',
...@@ -269,10 +264,10 @@ class CustomCollect3D(object): ...@@ -269,10 +264,10 @@ class CustomCollect3D(object):
- keys in ``self.keys`` - keys in ``self.keys``
- ``img_metas`` - ``img_metas``
""" """
data = {} data = {}
img_metas = {} img_metas = {}
for key in self.meta_keys: for key in self.meta_keys:
if key in results: if key in results:
img_metas[key] = results[key] img_metas[key] = results[key]
...@@ -285,8 +280,7 @@ class CustomCollect3D(object): ...@@ -285,8 +280,7 @@ class CustomCollect3D(object):
def __repr__(self): def __repr__(self):
"""str: Return a string that describes the module.""" """str: Return a string that describes the module."""
return self.__class__.__name__ + \ return self.__class__.__name__ + \
f'(keys={self.keys}, meta_keys={self.meta_keys})' f'(keys={self.keys}, meta_keys={self.meta_keys})'
@PIPELINES.register_module() @PIPELINES.register_module()
...@@ -298,7 +292,7 @@ class RandomScaleImageMultiViewImage(object): ...@@ -298,7 +292,7 @@ class RandomScaleImageMultiViewImage(object):
def __init__(self, scales=[]): def __init__(self, scales=[]):
self.scales = scales self.scales = scales
assert len(self.scales)==1 assert len(self.scales) == 1
def __call__(self, results): def __call__(self, results):
"""Call function to pad images, masks, semantic segmentation maps. """Call function to pad images, masks, semantic segmentation maps.
...@@ -324,8 +318,7 @@ class RandomScaleImageMultiViewImage(object): ...@@ -324,8 +318,7 @@ class RandomScaleImageMultiViewImage(object):
return results return results
def __repr__(self): def __repr__(self):
repr_str = self.__class__.__name__ repr_str = self.__class__.__name__
repr_str += f'(size={self.scales}, ' repr_str += f'(size={self.scales}, '
return repr_str return repr_str
\ No newline at end of file
from .group_sampler import DistributedGroupSampler
from .distributed_sampler import DistributedSampler from .distributed_sampler import DistributedSampler
from .group_sampler import DistributedGroupSampler
from .sampler import SAMPLER, build_sampler from .sampler import SAMPLER, build_sampler
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment