giantmonkeyTC
2344
34d1f8b
# Copyright (c) OpenMMLab. All rights reserved.
from unittest import TestCase
import torch
from mmengine import Config
from mmengine.structures import InstanceData
from mmdet3d import * # noqa
from mmdet3d.models.dense_heads import Anchor3DHead
from mmdet3d.structures import Box3DMode, LiDARInstance3DBoxes
class TestAnchor3DHead(TestCase):
def test_anchor3d_head_loss(self):
"""Test anchor head loss when truth is empty and non-empty."""
cfg = Config(
dict(
assigner=[
dict( # for Pedestrian
type='Max3DIoUAssigner',
iou_calculator=dict(type='BboxOverlapsNearest3D'),
pos_iou_thr=0.35,
neg_iou_thr=0.2,
min_pos_iou=0.2,
ignore_iof_thr=-1),
dict( # for Cyclist
type='Max3DIoUAssigner',
iou_calculator=dict(type='BboxOverlapsNearest3D'),
pos_iou_thr=0.35,
neg_iou_thr=0.2,
min_pos_iou=0.2,
ignore_iof_thr=-1),
dict( # for Car
type='Max3DIoUAssigner',
iou_calculator=dict(type='BboxOverlapsNearest3D'),
pos_iou_thr=0.6,
neg_iou_thr=0.45,
min_pos_iou=0.45,
ignore_iof_thr=-1),
],
allowed_border=0,
pos_weight=-1,
debug=False))
anchor3d_head = Anchor3DHead(
num_classes=3,
in_channels=512,
feat_channels=512,
use_direction_classifier=True,
anchor_generator=dict(
type='Anchor3DRangeGenerator',
ranges=[
[0, -40.0, -0.6, 70.4, 40.0, -0.6],
[0, -40.0, -0.6, 70.4, 40.0, -0.6],
[0, -40.0, -1.78, 70.4, 40.0, -1.78],
],
sizes=[[0.8, 0.6, 1.73], [1.76, 0.6, 1.73], [3.9, 1.6, 1.56]],
rotations=[0, 1.57],
reshape_out=False),
diff_rad_by_sin=True,
bbox_coder=dict(type='DeltaXYZWLHRBBoxCoder'),
loss_cls=dict(
type='mmdet.FocalLoss',
use_sigmoid=True,
gamma=2.0,
alpha=0.25,
loss_weight=1.0),
loss_bbox=dict(
type='mmdet.SmoothL1Loss', beta=1.0 / 9.0, loss_weight=2.0),
loss_dir=dict(
type='mmdet.CrossEntropyLoss',
use_sigmoid=False,
loss_weight=0.2),
train_cfg=cfg)
# Anchor head expects a multiple levels of features per image
feats = (torch.rand([1, 512, 200, 176], dtype=torch.float32), )
(cls_scores, bbox_preds, dir_cls_preds) = anchor3d_head.forward(feats)
self.assertEqual(cls_scores[0].shape, torch.Size([1, 18, 200, 176]))
self.assertEqual(bbox_preds[0].shape, torch.Size([1, 42, 200, 176]))
self.assertEqual(dir_cls_preds[0].shape, torch.Size([1, 12, 200, 176]))
# # Test that empty ground truth encourages the network to
# # predict background
gt_instances = InstanceData()
gt_bboxes_3d = LiDARInstance3DBoxes(torch.empty((0, 7)))
gt_labels_3d = torch.tensor([])
input_metas = dict(sample_idx=1234)
# fake input_metas
gt_instances.bboxes_3d = gt_bboxes_3d
gt_instances.labels_3d = gt_labels_3d
empty_gt_losses = anchor3d_head.loss_by_feat(cls_scores, bbox_preds,
dir_cls_preds,
[gt_instances],
[input_metas])
# When there is no truth, the cls loss should be nonzero but
# there should be no box and dir loss.
self.assertGreater(empty_gt_losses['loss_cls'][0], 0,
'cls loss should be non-zero')
self.assertEqual(
empty_gt_losses['loss_bbox'][0], 0,
'there should be no box loss when there are no true boxes')
self.assertEqual(
empty_gt_losses['loss_dir'][0], 0,
'there should be no dir loss when there are no true dirs')
# When truth is non-empty then both cls and box loss
# should be nonzero for random inputs
gt_instances = InstanceData()
gt_bboxes_3d = LiDARInstance3DBoxes(
torch.tensor(
[[6.4118, -3.4305, -1.7291, 1.7033, 3.4693, 1.6197, -0.9091]],
dtype=torch.float32))
gt_labels_3d = torch.tensor([1], dtype=torch.int64)
gt_instances.bboxes_3d = gt_bboxes_3d
gt_instances.labels_3d = gt_labels_3d
gt_losses = anchor3d_head.loss_by_feat(cls_scores, bbox_preds,
dir_cls_preds, [gt_instances],
[input_metas])
self.assertGreater(gt_losses['loss_cls'][0], 0,
'cls loss should be non-zero')
self.assertGreater(gt_losses['loss_bbox'][0], 0,
'box loss should be non-zero')
self.assertGreater(gt_losses['loss_dir'][0], 0,
'dir loss should be none-zero')
def test_anchor3d_head_predict(self):
cfg = Config(
dict(
use_rotate_nms=True,
nms_across_levels=False,
nms_thr=0.01,
score_thr=0.1,
min_bbox_size=0,
nms_pre=100,
max_num=50))
anchor3d_head = Anchor3DHead(
num_classes=3,
in_channels=512,
feat_channels=512,
use_direction_classifier=True,
anchor_generator=dict(
type='Anchor3DRangeGenerator',
ranges=[
[0, -40.0, -0.6, 70.4, 40.0, -0.6],
[0, -40.0, -0.6, 70.4, 40.0, -0.6],
[0, -40.0, -1.78, 70.4, 40.0, -1.78],
],
sizes=[[0.8, 0.6, 1.73], [1.76, 0.6, 1.73], [3.9, 1.6, 1.56]],
rotations=[0, 1.57],
reshape_out=False),
diff_rad_by_sin=True,
bbox_coder=dict(type='DeltaXYZWLHRBBoxCoder'),
loss_cls=dict(
type='mmdet.FocalLoss',
use_sigmoid=True,
gamma=2.0,
alpha=0.25,
loss_weight=1.0),
loss_bbox=dict(
type='mmdet.SmoothL1Loss', beta=1.0 / 9.0, loss_weight=2.0),
loss_dir=dict(
type='mmdet.CrossEntropyLoss',
use_sigmoid=False,
loss_weight=0.2),
test_cfg=cfg)
feats = (torch.rand([2, 512, 200, 176], dtype=torch.float32), )
(cls_scores, bbox_preds, dir_cls_preds) = anchor3d_head.forward(feats)
# fake input_metas
input_metas = [{
'sample_idx': 1234,
'box_type_3d': LiDARInstance3DBoxes,
'box_mode_3d': Box3DMode.LIDAR
}, {
'sample_idx': 2345,
'box_type_3d': LiDARInstance3DBoxes,
'box_mode_3d': Box3DMode.LIDAR
}]
# test get_boxes
cls_scores[0] -= 1.5 # too many positive samples may cause cuda oom
results = anchor3d_head.predict_by_feat(cls_scores, bbox_preds,
dir_cls_preds, input_metas)
pred_instances = results[0]
scores_3d = pred_instances.scores_3d
assert (scores_3d > 0.3).all()