Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
OpenDAS
mmdetection3d
Commits
f8f05baf
You need to sign in or sign up before continuing.
Commit
f8f05baf
authored
May 12, 2020
by
yinchimaoliang
Browse files
before evaluation testunit
parent
ef19d3cb
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
454 additions
and
0 deletions
+454
-0
mmdet3d/core/evaluation/scannet_utils/__init__.py
mmdet3d/core/evaluation/scannet_utils/__init__.py
+0
-0
mmdet3d/core/evaluation/scannet_utils/eval.py
mmdet3d/core/evaluation/scannet_utils/eval.py
+400
-0
mmdet3d/datasets/scannet_dataset.py
mmdet3d/datasets/scannet_dataset.py
+54
-0
No files found.
mmdet3d/core/evaluation/scannet_utils/__init__.py
0 → 100644
View file @
f8f05baf
mmdet3d/core/evaluation/scannet_utils/eval.py
0 → 100644
View file @
f8f05baf
import
numpy
as
np
import
torch
from
mmdet3d.ops.iou3d
import
iou3d_cuda
def
voc_ap
(
rec
,
prec
,
use_07_metric
=
False
):
""" ap = voc_ap(rec, prec, [use_07_metric])
Compute VOC AP given precision and recall.
If use_07_metric is true, uses the
VOC 07 11 point method (default:False).
"""
if
use_07_metric
:
# 11 point metric
ap
=
0.
for
t
in
np
.
arange
(
0.
,
1.1
,
0.1
):
if
np
.
sum
(
rec
>=
t
)
==
0
:
p
=
0
else
:
p
=
np
.
max
(
prec
[
rec
>=
t
])
ap
=
ap
+
p
/
11.
else
:
# correct AP calculation
# first append sentinel values at the end
mrec
=
np
.
concatenate
(([
0.
],
rec
,
[
1.
]))
mpre
=
np
.
concatenate
(([
0.
],
prec
,
[
0.
]))
# compute the precision envelope
for
i
in
range
(
mpre
.
size
-
1
,
0
,
-
1
):
mpre
[
i
-
1
]
=
np
.
maximum
(
mpre
[
i
-
1
],
mpre
[
i
])
# to calculate area under PR curve, look for points
# where X axis (recall) changes value
i
=
np
.
where
(
mrec
[
1
:]
!=
mrec
[:
-
1
])[
0
]
# and sum (\Delta recall) * prec
ap
=
np
.
sum
((
mrec
[
i
+
1
]
-
mrec
[
i
])
*
mpre
[
i
+
1
])
return
ap
def
boxes3d_to_bevboxes_lidar_torch
(
boxes3d
):
"""
:param boxes3d: (N, 7) [x, y, z, w, l, h, ry] in LiDAR coords
:return:
boxes_bev: (N, 5) [x1, y1, x2, y2, ry]
"""
boxes_bev
=
boxes3d
.
new
(
torch
.
Size
((
boxes3d
.
shape
[
0
],
5
)))
cu
,
cv
=
boxes3d
[:,
0
],
boxes3d
[:,
1
]
half_l
,
half_w
=
boxes3d
[:,
4
]
/
2
,
boxes3d
[:,
3
]
/
2
boxes_bev
[:,
0
],
boxes_bev
[:,
1
]
=
cu
-
half_w
,
cv
-
half_l
boxes_bev
[:,
2
],
boxes_bev
[:,
3
]
=
cu
+
half_w
,
cv
+
half_l
boxes_bev
[:,
4
]
=
boxes3d
[:,
6
]
return
boxes_bev
def
boxes_iou3d_gpu
(
boxes_a
,
boxes_b
):
"""
:param boxes_a: (N, 7) [x, y, z, w, l, h, ry] in LiDAR
:param boxes_b: (M, 7) [x, y, z, h, w, l, ry]
:return:
ans_iou: (M, N)
"""
boxes_a_bev
=
boxes3d_to_bevboxes_lidar_torch
(
boxes_a
)
boxes_b_bev
=
boxes3d_to_bevboxes_lidar_torch
(
boxes_b
)
# height overlap
boxes_a_height_max
=
(
boxes_a
[:,
2
]
+
boxes_a
[:,
5
]).
view
(
-
1
,
1
)
boxes_a_height_min
=
boxes_a
[:,
2
].
view
(
-
1
,
1
)
boxes_b_height_max
=
(
boxes_b
[:,
2
]
+
boxes_b
[:,
5
]).
view
(
1
,
-
1
)
boxes_b_height_min
=
boxes_b
[:,
2
].
view
(
1
,
-
1
)
# bev overlap
overlaps_bev
=
boxes_a
.
new_zeros
(
torch
.
Size
((
boxes_a
.
shape
[
0
],
boxes_b
.
shape
[
0
])))
# (N, M)
iou3d_cuda
.
boxes_overlap_bev_gpu
(
boxes_a_bev
.
contiguous
(),
boxes_b_bev
.
contiguous
(),
overlaps_bev
)
max_of_min
=
torch
.
max
(
boxes_a_height_min
,
boxes_b_height_min
)
min_of_max
=
torch
.
min
(
boxes_a_height_max
,
boxes_b_height_max
)
overlaps_h
=
torch
.
clamp
(
min_of_max
-
max_of_min
,
min
=
0
)
# 3d iou
overlaps_3d
=
overlaps_bev
*
overlaps_h
vol_a
=
(
boxes_a
[:,
3
]
*
boxes_a
[:,
4
]
*
boxes_a
[:,
5
]).
view
(
-
1
,
1
)
vol_b
=
(
boxes_b
[:,
3
]
*
boxes_b
[:,
4
]
*
boxes_b
[:,
5
]).
view
(
1
,
-
1
)
iou3d
=
overlaps_3d
/
torch
.
clamp
(
vol_a
+
vol_b
-
overlaps_3d
,
min
=
1e-6
)
return
iou3d
def
get_iou_gpu
(
bb1
,
bb2
):
""" Compute IoU of two bounding boxes.
** Define your bod IoU function HERE **
"""
bb1
=
torch
.
from_numpy
(
bb1
).
float
().
cuda
()
bb2
=
torch
.
from_numpy
(
bb2
).
float
().
cuda
()
iou3d
=
boxes_iou3d_gpu
(
bb1
,
bb2
)
return
iou3d
.
cpu
().
numpy
()
def
eval_det_cls
(
pred
,
gt
,
ovthresh
=
None
,
use_07_metric
=
False
,
get_iou_func
=
get_iou_gpu
):
""" Generic functions to compute precision/recall for object detection
for a single class.
Input:
pred: map of {img_id: [(bbox, score)]} where bbox is numpy array
gt: map of {img_id: [bbox]}
ovthresh: a list, iou threshold
use_07_metric: bool, if True use VOC07 11 point method
Output:
rec: numpy array of length nd
prec: numpy array of length nd
ap: scalar, average precision
"""
# construct gt objects
class_recs
=
{}
# {img_id: {'bbox': bbox list, 'det': matched list}}
npos
=
0
for
img_id
in
gt
.
keys
():
bbox
=
np
.
array
(
gt
[
img_id
])
det
=
[[
False
]
*
len
(
bbox
)
for
i
in
ovthresh
]
npos
+=
len
(
bbox
)
class_recs
[
img_id
]
=
{
'bbox'
:
bbox
,
'det'
:
det
}
# pad empty list to all other imgids
for
img_id
in
pred
.
keys
():
if
img_id
not
in
gt
:
class_recs
[
img_id
]
=
{
'bbox'
:
np
.
array
([]),
'det'
:
[]}
# construct dets
image_ids
=
[]
confidence
=
[]
BB
=
[]
ious
=
[]
for
img_id
in
pred
.
keys
():
cur_num
=
len
(
pred
[
img_id
])
if
cur_num
==
0
:
continue
BB_cur
=
np
.
zeros
((
cur_num
,
7
))
# hard code
box_idx
=
0
for
box
,
score
in
pred
[
img_id
]:
image_ids
.
append
(
img_id
)
confidence
.
append
(
score
)
BB
.
append
(
box
)
BB_cur
[
box_idx
]
=
box
box_idx
+=
1
gt_cur
=
class_recs
[
img_id
][
'bbox'
].
astype
(
float
)
if
len
(
gt_cur
)
>
0
:
# calculate iou in each image
iou_cur
=
get_iou_gpu
(
BB_cur
,
gt_cur
)
for
i
in
range
(
cur_num
):
ious
.
append
(
iou_cur
[
i
])
else
:
for
i
in
range
(
cur_num
):
ious
.
append
(
np
.
zeros
(
1
))
confidence
=
np
.
array
(
confidence
)
BB
=
np
.
array
(
BB
)
# (nd,4 or 8,3 or 6)
# sort by confidence
sorted_ind
=
np
.
argsort
(
-
confidence
)
BB
=
BB
[
sorted_ind
,
...]
image_ids
=
[
image_ids
[
x
]
for
x
in
sorted_ind
]
ious
=
[
ious
[
x
]
for
x
in
sorted_ind
]
# go down dets and mark TPs and FPs
nd
=
len
(
image_ids
)
tp_thresh
=
[
np
.
zeros
(
nd
)
for
i
in
ovthresh
]
fp_thresh
=
[
np
.
zeros
(
nd
)
for
i
in
ovthresh
]
for
d
in
range
(
nd
):
R
=
class_recs
[
image_ids
[
d
]]
ovmax
=
-
np
.
inf
BBGT
=
R
[
'bbox'
].
astype
(
float
)
cur_iou
=
ious
[
d
]
if
BBGT
.
size
>
0
:
# compute overlaps
for
j
in
range
(
BBGT
.
shape
[
0
]):
# iou = get_iou_main(get_iou_func, (bb, BBGT[j,...]))
iou
=
cur_iou
[
j
]
if
iou
>
ovmax
:
ovmax
=
iou
jmax
=
j
for
iou_idx
,
thresh
in
enumerate
(
ovthresh
):
if
ovmax
>
thresh
:
if
not
R
[
'det'
][
iou_idx
][
jmax
]:
tp_thresh
[
iou_idx
][
d
]
=
1.
R
[
'det'
][
iou_idx
][
jmax
]
=
1
else
:
fp_thresh
[
iou_idx
][
d
]
=
1.
else
:
fp_thresh
[
iou_idx
][
d
]
=
1.
ret
=
[]
for
iou_idx
,
thresh
in
enumerate
(
ovthresh
):
# compute precision recall
fp
=
np
.
cumsum
(
fp_thresh
[
iou_idx
])
tp
=
np
.
cumsum
(
tp_thresh
[
iou_idx
])
rec
=
tp
/
float
(
npos
)
# avoid divide by zero in case the first detection matches a difficult
# ground truth
prec
=
tp
/
np
.
maximum
(
tp
+
fp
,
np
.
finfo
(
np
.
float64
).
eps
)
ap
=
voc_ap
(
rec
,
prec
,
use_07_metric
)
ret
.
append
((
rec
,
prec
,
ap
))
return
ret
def
eval_det_cls_wrapper
(
arguments
):
pred
,
gt
,
ovthresh
,
use_07_metric
,
get_iou_func
=
arguments
ret
=
eval_det_cls
(
pred
,
gt
,
ovthresh
,
use_07_metric
,
get_iou_func
)
return
ret
def
eval_det_multiprocessing
(
pred_all
,
gt_all
,
ovthresh
=
None
,
use_07_metric
=
False
,
get_iou_func
=
get_iou_gpu
):
""" Generic functions to compute precision/recall for object detection
for multiple classes.
Input:
pred_all: map of {img_id: [(classname, bbox, score)]}
gt_all: map of {img_id: [(classname, bbox)]}
ovthresh: a list, iou threshold
use_07_metric: bool, if true use VOC07 11 point method
Output:
rec: {classname: rec}
prec: {classname: prec_all}
ap: {classname: scalar}
"""
pred
=
{}
# map {classname: pred}
gt
=
{}
# map {classname: gt}
for
img_id
in
pred_all
.
keys
():
for
classname
,
bbox
,
score
in
pred_all
[
img_id
]:
if
classname
not
in
pred
:
pred
[
classname
]
=
{}
if
img_id
not
in
pred
[
classname
]:
pred
[
classname
][
img_id
]
=
[]
if
classname
not
in
gt
:
gt
[
classname
]
=
{}
if
img_id
not
in
gt
[
classname
]:
gt
[
classname
][
img_id
]
=
[]
pred
[
classname
][
img_id
].
append
((
bbox
,
score
))
for
img_id
in
gt_all
.
keys
():
for
classname
,
bbox
in
gt_all
[
img_id
]:
if
classname
not
in
gt
:
gt
[
classname
]
=
{}
if
img_id
not
in
gt
[
classname
]:
gt
[
classname
][
img_id
]
=
[]
gt
[
classname
][
img_id
].
append
(
bbox
)
ret_values
=
[]
args
=
[(
pred
[
classname
],
gt
[
classname
],
ovthresh
,
use_07_metric
,
get_iou_func
)
for
classname
in
gt
.
keys
()
if
classname
in
pred
]
rec
=
[{}
for
i
in
ovthresh
]
prec
=
[{}
for
i
in
ovthresh
]
ap
=
[{}
for
i
in
ovthresh
]
for
arg
in
args
:
ret_values
.
append
(
eval_det_cls_wrapper
(
arg
))
for
i
,
classname
in
enumerate
(
gt
.
keys
()):
for
iou_idx
,
thresh
in
enumerate
(
ovthresh
):
if
classname
in
pred
:
rec
[
iou_idx
][
classname
],
prec
[
iou_idx
][
classname
],
ap
[
iou_idx
][
classname
]
=
ret_values
[
i
][
iou_idx
]
else
:
rec
[
iou_idx
][
classname
]
=
0
prec
[
iou_idx
][
classname
]
=
0
ap
[
iou_idx
][
classname
]
=
0
# print(classname, ap[classname])
return
rec
,
prec
,
ap
class
APCalculator
(
object
):
''' Calculating Average Precision '''
def
__init__
(
self
,
ap_iou_thresh
=
None
,
class2type_map
=
None
):
"""
Args:
ap_iou_thresh: a list, which contains float between 0 and 1.0
IoU threshold to judge whether a prediction is positive.
class2type_map: [optional] dict {class_int:class_name}
"""
self
.
ap_iou_thresh
=
ap_iou_thresh
self
.
class2type_map
=
class2type_map
self
.
reset
()
def
step
(
self
,
det_infos
,
gt_infos
):
""" Accumulate one batch of prediction and groundtruth.
Args:
batch_pred_map_cls: a list of lists
[[(pred_cls, pred_box_params, score),...],...]
batch_gt_map_cls: a list of lists
[[(gt_cls, gt_box_params),...],...]
should have the same length with batch_pred_map_cls
(batch_size)
"""
# cache pred infos
for
batch_pred_map_cls
in
det_infos
:
for
i
in
range
(
len
(
batch_pred_map_cls
)):
self
.
pred_map_cls
[
self
.
scan_cnt
]
=
batch_pred_map_cls
[
i
]
self
.
scan_cnt
+=
1
# cacge gt infos
self
.
scan_cnt
=
0
for
gt_info
in
gt_infos
:
cur_gt
=
list
()
for
n
in
range
(
gt_info
[
'gt_num'
]):
cur_gt
.
append
((
gt_info
[
'class'
][
n
],
gt_info
[
'gt_boxes_upright_depth'
][
n
]))
self
.
gt_map_cls
[
self
.
scan_cnt
]
=
cur_gt
self
.
scan_cnt
+=
1
def
compute_metrics
(
self
):
""" Use accumulated predictions and groundtruths to compute Average Precision.
"""
recs
,
precs
,
aps
=
eval_det_multiprocessing
(
self
.
pred_map_cls
,
self
.
gt_map_cls
,
ovthresh
=
self
.
ap_iou_thresh
,
get_iou_func
=
get_iou_gpu
)
ret
=
[]
for
i
,
iou_thresh
in
enumerate
(
self
.
ap_iou_thresh
):
ret_dict
=
{}
rec
,
_
,
ap
=
recs
[
i
],
precs
[
i
],
aps
[
i
]
for
key
in
sorted
(
ap
.
keys
()):
clsname
=
self
.
class2type_map
[
key
]
if
self
.
class2type_map
else
str
(
key
)
ret_dict
[
'%s Average Precision %d'
%
(
clsname
,
iou_thresh
*
100
)]
=
ap
[
key
]
ret_dict
[
'mAP%d'
%
(
iou_thresh
*
100
)]
=
np
.
mean
(
list
(
ap
.
values
()))
rec_list
=
[]
for
key
in
sorted
(
ap
.
keys
()):
clsname
=
self
.
class2type_map
[
key
]
if
self
.
class2type_map
else
str
(
key
)
try
:
ret_dict
[
'%s Recall %d'
%
(
clsname
,
iou_thresh
*
100
)]
=
rec
[
key
][
-
1
]
rec_list
.
append
(
rec
[
key
][
-
1
])
except
KeyError
:
ret_dict
[
'%s Recall %d'
%
(
clsname
,
iou_thresh
*
100
)]
=
0
rec_list
.
append
(
0
)
ret_dict
[
'AR%d'
%
(
iou_thresh
*
100
)]
=
np
.
mean
(
rec_list
)
ret
.
append
(
ret_dict
)
return
ret
def
reset
(
self
):
self
.
gt_map_cls
=
{}
# {scan_id: [(classname, bbox)]}
self
.
pred_map_cls
=
{}
# {scan_id: [(classname, bbox, score)]}
self
.
scan_cnt
=
0
def
boxes3d_depth_to_lidar
(
boxes3d
,
mid_to_bottom
=
True
):
""" Flip X-right,Y-forward,Z-up to X-forward,Y-left,Z-up
:param boxes3d_depth: (N, 7) [x, y, z, w, l, h, r] in depth coords
:return:
boxes3d_lidar: (N, 7) [x, y, z, l, h, w, r] in LiDAR coords
"""
boxes3d_lidar
=
boxes3d
.
copy
()
boxes3d_lidar
[...,
[
0
,
1
,
2
,
3
,
4
,
5
]]
=
boxes3d_lidar
[...,
[
1
,
0
,
2
,
4
,
3
,
5
]]
boxes3d_lidar
[...,
1
]
*=
-
1
if
mid_to_bottom
:
boxes3d_lidar
[...,
2
]
-=
boxes3d_lidar
[...,
5
]
/
2
return
boxes3d_lidar
def
scannet_eval
(
gt_annos
,
dt_annos
,
metric
,
class2type
):
for
gt_anno
in
gt_annos
:
if
gt_anno
[
'gt_num'
]
!=
0
:
# convert to lidar coor for evaluation
bbox_lidar_bottom
=
boxes3d_depth_to_lidar
(
gt_anno
[
'gt_boxes_upright_depth'
],
mid_to_bottom
=
True
)
gt_anno
[
'gt_boxes_upright_depth'
]
=
np
.
pad
(
bbox_lidar_bottom
,
((
0
,
0
),
(
0
,
1
)),
'constant'
)
ap_iou_thresholds
=
metric
.
AP_IOU_THRESHHOLDS
ap_calculator
=
APCalculator
(
ap_iou_thresholds
,
class2type
)
ap_calculator
.
step
(
dt_annos
,
gt_annos
)
result_str
=
str
()
result_str
+=
'mAP'
metrics_dict
=
{}
metrics
=
ap_calculator
.
compute_metrics
()
for
i
,
iou_threshold
in
enumerate
(
ap_iou_thresholds
):
metrics_tmp
=
metrics
[
i
]
metrics_dict
.
update
(
metrics_tmp
)
result_str
+=
'(%.2f):%s '
%
(
iou_threshold
,
metrics_dict
[
'mAP%d'
%
(
iou_threshold
*
100
)])
return
result_str
,
metrics_dict
mmdet3d/datasets/scannet_dataset.py
View file @
f8f05baf
import
copy
import
os
import
os.path
as
osp
...
...
@@ -172,5 +173,58 @@ class ScannetDataset(torch_data.dataset):
pool
=
np
.
where
(
self
.
flag
==
self
.
flag
[
idx
])[
0
]
return
np
.
random
.
choice
(
pool
)
def
generate_annotations
(
self
,
output
):
'''
transfer input_dict & pred_dicts to anno format
which is needed by AP calculator
return annos: a tuple (batch_pred_map_cls,batch_gt_map_cls)
batch_pred_map_cls is a list: i=0,1..bs-1
pred_list_i:[(pred_sem_cls,
box_params, box_score)_j]
j=0,1..num_pred_obj -1
batch_gt_map_cls is a list: i=0,1..bs-1
gt_list_i: [(sem_cls_label, box_params)_j]
j=0,1..num_gt_obj -1
'''
result
=
[]
bs
=
len
(
output
)
for
i
in
range
(
bs
):
pred_list_i
=
list
()
pred_boxes
=
output
[
i
]
box3d_depth
=
pred_boxes
[
'box3d_lidar'
]
if
box3d_depth
is
not
None
:
label_preds
=
pred_boxes
.
get
[
'label_preds'
]
scores
=
pred_boxes
[
'scores'
].
detach
().
cpu
().
numpy
()
label_preds
=
label_preds
.
detach
().
cpu
().
numpy
()
num_proposal
=
box3d_depth
.
shape
[
0
]
for
j
in
range
(
num_proposal
):
bbox_lidar
=
box3d_depth
[
j
]
# [7] in lidar
bbox_lidar_bottom
=
bbox_lidar
.
copy
()
pred_list_i
.
append
(
(
label_preds
[
j
],
bbox_lidar_bottom
,
scores
[
j
]))
result
.
append
(
pred_list_i
)
else
:
result
.
append
(
pred_list_i
)
return
result
def
format_results
(
self
,
outputs
):
results
=
[]
for
output
in
outputs
:
result
=
self
.
generate_annotations
(
output
)
results
.
append
(
result
)
return
results
def
evaluate
(
self
,
results
,
metric
=
None
,
logger
=
None
,
pklfile_prefix
=
None
):
results
=
self
.
format_results
(
results
)
from
mmdet3d.core.evaluation.scannet_utils.eval
import
scannet_eval
assert
(
'AP_IOU_THRESHHOLDS'
in
metric
)
gt_annos
=
[
copy
.
deepcopy
(
info
[
'annos'
])
for
info
in
self
.
scannet_infos
]
ap_result_str
,
ap_dict
=
scannet_eval
(
gt_annos
,
results
)
return
ap_dict
def
__len__
(
self
):
return
len
(
self
.
scannet_infos
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment