Skip to content

Commit 2fdb814

Browse files
arjunsureshpgmpablo157321rod409mrmhodak
authored
Automotive reference implementation (mlcommons#1954)
* Automotive reference implementation sketch * WIP: automotive reference implementation * WIP add segmentation, dataset, and util functions to reference * WIP: reference implementation with issues during post processing * WIP update dockerfile remove pdb breaks * WIP: reference implementation that runs samples * Update README.md with initial docker runs * WIP: add accuracy checker to reference * Fix: set lidar detector to evaluation mode * [Automated Commit] Format Codebase * Update README.md --------- Co-authored-by: Pablo Gonzalez <[email protected]> Co-authored-by: Radoyeh Shojaei <[email protected]> Co-authored-by: arjunsuresh <[email protected]> Co-authored-by: Miro <[email protected]>
1 parent be6ff52 commit 2fdb814

33 files changed

+4629
-0
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
## Reference implementation fo automotive 3D detection benchmark
2+
3+
## TODO: Instructions for dataset download after it is uploaded somewhere appropriate
4+
5+
## TODO: Instructions for checkpoints downloads after it is uploaded somewhere appropriate
6+
7+
## Running with docker
8+
```
9+
docker build -t auto_inference -f dockerfile.gpu .
10+
11+
docker run --gpus=all -it -v <directory to inference repo>/inference/:/inference -v <directory to waymo dataset>/waymo:/waymo --rm auto_inference
12+
13+
cd /inference/automotive/3d-object-detection
14+
python main.py --dataset waymo --dataset-path /waymo/kitti_format/ --lidar-path <checkpoint_path>/pp_ep36.pth --segmentor-path <checkpoint_path>/best_deeplabv3plus_resnet50_waymo_os16.pth --mlperf_conf /inference/mlperf.conf
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
"""
2+
Tool to calculate accuracy for loadgen accuracy output found in mlperf_log_accuracy.json
3+
We assume that loadgen's query index is in the same order as
4+
the images in coco's annotations/instances_val2017.json.
5+
"""
6+
7+
from __future__ import division
8+
from __future__ import print_function
9+
from __future__ import unicode_literals
10+
11+
import argparse
12+
import json
13+
import os
14+
15+
import numpy as np
16+
from waymo import Waymo
17+
from tools.evaluate import do_eval
18+
# pylint: disable=missing-docstring
19+
CLASSES = Waymo.CLASSES
20+
LABEL2CLASSES = {v: k for k, v in CLASSES.items()}
21+
22+
23+
def get_args():
24+
"""Parse commandline."""
25+
parser = argparse.ArgumentParser()
26+
parser.add_argument(
27+
"--mlperf-accuracy-file",
28+
required=True,
29+
help="path to mlperf_log_accuracy.json")
30+
parser.add_argument(
31+
"--waymo-dir",
32+
required=True,
33+
help="waymo dataset directory")
34+
parser.add_argument(
35+
"--verbose",
36+
action="store_true",
37+
help="verbose messages")
38+
parser.add_argument(
39+
"--output-file",
40+
default="openimages-results.json",
41+
help="path to output file")
42+
parser.add_argument(
43+
"--use-inv-map",
44+
action="store_true",
45+
help="use inverse label map")
46+
args = parser.parse_args()
47+
return args
48+
49+
50+
def main():
51+
args = get_args()
52+
53+
with open(args.mlperf_accuracy_file, "r") as f:
54+
results = json.load(f)
55+
56+
detections = {}
57+
image_ids = set()
58+
seen = set()
59+
no_results = 0
60+
61+
val_dataset = Waymo(
62+
data_root=args.waymo_dir,
63+
split='val',
64+
painted=True,
65+
cam_sync=False)
66+
67+
for j in results:
68+
idx = j['qsl_idx']
69+
# de-dupe in case loadgen sends the same image multiple times
70+
if idx in seen:
71+
continue
72+
seen.add(idx)
73+
74+
# reconstruct from mlperf accuracy log
75+
# what is written by the benchmark is an array of float32's:
76+
# id, box[0], box[1], box[2], box[3], score, detection_class
77+
# note that id is a index into instances_val2017.json, not the actual
78+
# image_id
79+
data = np.frombuffer(bytes.fromhex(j['data']), np.float32)
80+
81+
for i in range(0, len(data), 14):
82+
dimension = [float(x) for x in data[i:i + 3]]
83+
location = [float(x) for x in data[i + 3:i + 6]]
84+
rotation_y = float(data[i + 6])
85+
bbox = [float(x) for x in data[i + 7:i + 11]]
86+
label = int(data[i + 11])
87+
score = float(data[i + 12])
88+
image_idx = int(data[i + 13])
89+
if image_idx not in detections:
90+
detections[image_idx] = {
91+
'name': [],
92+
'dimensions': [],
93+
'location': [],
94+
'rotation_y': [],
95+
'bbox': [],
96+
'score': []
97+
}
98+
99+
detections[image_idx]['name'].append(LABEL2CLASSES[label])
100+
detections[image_idx]['dimensions'].append(dimension)
101+
detections[image_idx]['location'].append(location)
102+
detections[image_idx]['rotation_y'].append(rotation_y)
103+
detections[image_idx]['bbox'].append(bbox)
104+
detections[image_idx]['score'].append(score)
105+
image_ids.add(image_idx)
106+
107+
with open(args.output_file, "w") as fp:
108+
json.dump(detections, fp, sort_keys=True, indent=4)
109+
format_results = {}
110+
for key in detections.keys():
111+
format_results[key] = {k: np.array(v)
112+
for k, v in detections[key].items()}
113+
map_stats = do_eval(
114+
format_results,
115+
val_dataset.data_infos,
116+
CLASSES,
117+
cam_sync=False)
118+
119+
print(map_stats)
120+
if args.verbose:
121+
print("found {} results".format(len(results)))
122+
print("found {} images".format(len(image_ids)))
123+
print("found {} images with no results".format(no_results))
124+
print("ignored {} dupes".format(len(results) - len(seen)))
125+
126+
127+
if __name__ == "__main__":
128+
main()
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"""
2+
abstract backend class
3+
"""
4+
5+
6+
class Backend:
7+
def __init__(self):
8+
self.inputs = []
9+
self.outputs = []
10+
11+
def version(self):
12+
raise NotImplementedError("Backend:version")
13+
14+
def name(self):
15+
raise NotImplementedError("Backend:name")
16+
17+
def load(self, model_path, inputs=None, outputs=None):
18+
raise NotImplementedError("Backend:load")
19+
20+
def predict(self, feed):
21+
raise NotImplementedError("Backend:predict")
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import torch
2+
import backend
3+
4+
5+
class BackendDebug(backend.Backend):
6+
def __init__(self, image_size=[3, 1024, 1024], **kwargs):
7+
super(BackendDebug, self).__init__()
8+
self.image_size = image_size
9+
10+
def version(self):
11+
return torch.__version__
12+
13+
def name(self):
14+
return "debug-SUT"
15+
16+
def image_format(self):
17+
return "NCHW"
18+
19+
def load(self):
20+
return self
21+
22+
def predict(self, prompts):
23+
images = []
24+
return images
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
from typing import Optional, List, Union
2+
import os
3+
import torch
4+
import logging
5+
import backend
6+
from collections import namedtuple
7+
from model.painter import Painter
8+
from model.pointpillars import PointPillars
9+
import numpy as np
10+
from tools.process import keep_bbox_from_image_range
11+
from waymo import Waymo
12+
13+
14+
logging.basicConfig(level=logging.INFO)
15+
log = logging.getLogger("backend-pytorch")
16+
17+
18+
def change_calib_device(calib, cuda):
19+
result = {}
20+
if cuda:
21+
device = 'cuda'
22+
else:
23+
device = 'cpu'
24+
result['R0_rect'] = calib['R0_rect'].to(device=device, dtype=torch.float)
25+
for i in range(5):
26+
result['P' + str(i)] = calib['P' + str(i)
27+
].to(device=device, dtype=torch.float)
28+
result['Tr_velo_to_cam_' +
29+
str(i)] = calib['Tr_velo_to_cam_' +
30+
str(i)].to(device=device, dtype=torch.float)
31+
return result
32+
33+
34+
class BackendDeploy(backend.Backend):
35+
def __init__(
36+
self,
37+
segmentor_path,
38+
lidar_detector_path,
39+
data_path
40+
):
41+
super(BackendDeploy, self).__init__()
42+
self.segmentor_path = segmentor_path
43+
self.lidar_detector_path = lidar_detector_path
44+
# self.segmentation_classes = 18
45+
self.detection_classes = 3
46+
self.data_root = data_path
47+
CLASSES = Waymo.CLASSES
48+
self.LABEL2CLASSES = {v: k for k, v in CLASSES.items()}
49+
50+
def version(self):
51+
return torch.__version__
52+
53+
def name(self):
54+
return "python-SUT"
55+
56+
def load(self):
57+
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
58+
PaintArgs = namedtuple(
59+
'PaintArgs', [
60+
'training_path', 'model_path', 'cam_sync'])
61+
painting_args = PaintArgs(
62+
os.path.join(
63+
self.data_root,
64+
'training'),
65+
self.segmentor_path,
66+
False)
67+
self.painter = Painter(painting_args)
68+
self.segmentor = self.painter.model
69+
model = PointPillars(
70+
nclasses=self.detection_classes,
71+
painted=True).to(
72+
device=device)
73+
model.eval()
74+
checkpoint = torch.load(self.lidar_detector_path)
75+
model.load_state_dict(checkpoint["model_state_dict"])
76+
self.lidar_detector = model
77+
78+
return self
79+
80+
def predict(self, inputs):
81+
# TODO: implement predict
82+
dimensions, locations, rotation_y, box2d, class_labels, class_scores, ids = [
83+
], [], [], [], [], [], []
84+
with torch.inference_mode():
85+
device = torch.device(
86+
"cuda:0" if torch.cuda.is_available() else "cpu")
87+
format_results = {}
88+
model_input = inputs[0]
89+
batched_pts = model_input['pts']
90+
scores_from_cam = []
91+
for i in range(len(model_input['images'])):
92+
segmentation_score = self.segmentor(
93+
model_input['images'][i].to(device))[0]
94+
scores_from_cam.append(
95+
self.painter.get_score(segmentation_score).cpu())
96+
points = self.painter.augment_lidar_class_scores_both(
97+
scores_from_cam, batched_pts, model_input['calib_info'])
98+
batch_results = self.lidar_detector(
99+
batched_pts=[points.to(device=device)], mode='val')
100+
for j, result in enumerate(batch_results):
101+
format_result = {
102+
'class': [],
103+
'truncated': [],
104+
'occluded': [],
105+
'alpha': [],
106+
'bbox': [],
107+
'dimensions': [],
108+
'location': [],
109+
'rotation_y': [],
110+
'score': [],
111+
'idx': -1
112+
}
113+
114+
calib_info = model_input['calib_info']
115+
image_info = model_input['image_info']
116+
idx = model_input['image_info']['image_idx']
117+
118+
calib_info = change_calib_device(calib_info, False)
119+
result_filter = keep_bbox_from_image_range(
120+
result, calib_info, 5, image_info, False)
121+
122+
lidar_bboxes = result_filter['lidar_bboxes']
123+
labels, scores = result_filter['labels'], result_filter['scores']
124+
bboxes2d, camera_bboxes = result_filter['bboxes2d'], result_filter['camera_bboxes']
125+
for lidar_bbox, label, score, bbox2d, camera_bbox in \
126+
zip(lidar_bboxes, labels, scores, bboxes2d, camera_bboxes):
127+
format_result['class'].append(label.item())
128+
format_result['truncated'].append(0.0)
129+
format_result['occluded'].append(0)
130+
alpha = camera_bbox[6] - \
131+
np.arctan2(camera_bbox[0], camera_bbox[2])
132+
format_result['alpha'].append(alpha.item())
133+
format_result['bbox'].append(bbox2d.tolist())
134+
format_result['dimensions'].append(camera_bbox[3:6])
135+
format_result['location'].append(camera_bbox[:3])
136+
format_result['rotation_y'].append(camera_bbox[6].item())
137+
format_result['score'].append(score.item())
138+
format_results['idx'] = idx
139+
140+
# write_label(format_result, os.path.join(saved_submit_path, f'{idx:06d}.txt'))
141+
142+
if len(format_result['dimensions']) > 0:
143+
format_result['dimensions'] = torch.stack(
144+
format_result['dimensions'])
145+
format_result['location'] = torch.stack(
146+
format_result['location'])
147+
dimensions.append(format_result['dimensions'])
148+
locations.append(format_result['location'])
149+
rotation_y.append(format_result['rotation_y'])
150+
class_labels.append(format_result['class'])
151+
class_scores.append(format_result['score'])
152+
box2d.append(format_result['bbox'])
153+
ids.append(format_results['idx'])
154+
# return Boxes, Classes, Scores # Change to desired output
155+
return dimensions, locations, rotation_y, box2d, class_labels, class_scores, ids

0 commit comments

Comments
 (0)