Skip to content

Commit 3a66e20

Browse files
author
Dan Jia
committed
public release
1 parent 8df1c8c commit 3a66e20

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+13407
-0
lines changed

.gitignore

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
*~
2+
*__pycache__*
3+
*_ext*
4+
*.bag
5+
*.csv
6+
*.cu.o
7+
*.DS_Store
8+
*.gif
9+
*.ipynb_checkpoints
10+
*.pkl
11+
*.png
12+
*.pth
13+
*.pyc
14+
*.so
15+
*.tfevents*
16+
*.yml
17+
18+
.idea/
19+
.vscode/
20+
*.egg-info/
21+
build/
22+
ckpt/
23+
ckpts/
24+
dist/
25+
devel/
26+
experiments/
27+
work_dirs/
28+
29+
data
30+
logs
31+
wandb
32+
work_dirs
33+
work_dirs_tmp

README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Person-MinkUNet
2+
3+
PyTorch implementation of Person-MinkUNet.
4+
Winner of JRDB 3D detection challenge in JRDB-ACT Workshop at CVPR 2021
5+
[`[arXiv]`](https://arxiv.org/abs/2107.06780)
6+
[`[video]`](https://www.youtube.com/watch?v=RnGnONoX9cU)
7+
[`[leaderboard]`](https://jrdb.stanford.edu/leaderboards/detection).
8+
9+
# Prerequisite
10+
11+
- `python>=3.8`
12+
- `torchsparse==1.2.0` [(link)](https://github.com/mit-han-lab/torchsparse)
13+
- `PyTorch==1.6.0`
14+
15+
# Quick start
16+
17+
Download [JackRabbot dataset](https://jrdb.stanford.edu/) under `PROJECT/data/JRDB`.
18+
19+
```
20+
# install lidar_det project
21+
python setup.py develop
22+
23+
# build libraries
24+
cd lib/iou3d
25+
python setup.py develop
26+
27+
cd ../jrdb_det3d_eval
28+
python setup.py develop
29+
```
30+
31+
Run
32+
```
33+
python bin/train.py --cfg PATH_TO_CFG [--ckpt PATH_TO_CKPT] [--evaluation]
34+
```
35+
36+
# Model zoo
37+
38+
| Split | Checkpoint | Config |
39+
|-------|------------|--------|
40+
| train | [ckpt]() | [cfg]() |
41+
| train + val | [ckpt]() | [cfg]() |
42+
43+
# Acknowledge
44+
45+
- torchsparse [(link)](https://github.com/mit-han-lab/torchsparse)
46+
- PointRCNN [(link)](https://github.com/sshaoshuai/PointRCNN/tree/master/lib/utils/iou3d)
47+
48+
# Citation
49+
```
50+
@inproceedings{Jia2021PersonMinkUnet,
51+
title = {{Person-MinkUNet: 3D Person Detection with LiDAR Point Cloud}},
52+
author = {Dan Jia and Bastian Leibe},
53+
booktitle = {Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition Workshops (CVPRW)},
54+
year = {2021}
55+
}
56+
```
57+
58+
59+
60+
61+

bin/train.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import argparse
2+
import yaml
3+
import torch
4+
5+
from lidar_det.dataset import get_dataloader
6+
from lidar_det.pipeline import Pipeline
7+
from lidar_det.model import get_model
8+
9+
10+
def run_training(model, pipeline, cfg):
11+
# main train loop
12+
train_loader = get_dataloader(
13+
split="train", shuffle=True, dataset_cfg=cfg["dataset"], **cfg["dataloader"]
14+
)
15+
val_loader = get_dataloader(
16+
split="val", shuffle=False, dataset_cfg=cfg["dataset"], **cfg["dataloader"]
17+
)
18+
status = pipeline.train(model, train_loader, val_loader)
19+
20+
# # test after training
21+
# if not status:
22+
# test_loader = get_dataloader(
23+
# split="val",
24+
# shuffle=False,
25+
# dataset_cfg=cfg["dataset"],
26+
# num_workers=1,
27+
# batch_size=1,
28+
# )
29+
# pipeline.evaluate(model, test_loader, tb_prefix="VAL")
30+
31+
32+
def run_evaluation(model, pipeline, cfg):
33+
val_loader = get_dataloader(
34+
split="val",
35+
shuffle=False,
36+
dataset_cfg=cfg["dataset"],
37+
num_workers=1,
38+
batch_size=1,
39+
)
40+
pipeline.evaluate(model, val_loader, tb_prefix="VAL", rm_files=False)
41+
42+
# test_loader = get_dataloader(
43+
# split="test",
44+
# shuffle=False,
45+
# dataset_cfg=cfg["dataset"],
46+
# num_workers=1,
47+
# batch_size=1,
48+
# )
49+
# pipeline.evaluate(model, test_loader, tb_prefix="TEST", rm_files=False)
50+
51+
52+
if __name__ == "__main__":
53+
# Run benchmark to select fastest implementation of ops.
54+
torch.backends.cudnn.benchmark = True
55+
56+
parser = argparse.ArgumentParser(description="arg parser")
57+
parser.add_argument(
58+
"--cfg", type=str, required=True, help="configuration of the experiment"
59+
)
60+
parser.add_argument("--ckpt", type=str, required=False, default=None)
61+
parser.add_argument("--cont", default=False, action="store_true")
62+
parser.add_argument("--tmp", default=False, action="store_true")
63+
parser.add_argument("--bs_one", default=False, action="store_true")
64+
parser.add_argument("--evaluation", default=False, action="store_true")
65+
args = parser.parse_args()
66+
67+
with open(args.cfg, "r") as f:
68+
cfg = yaml.safe_load(f)
69+
cfg["pipeline"]["Logger"]["backup_list"].append(args.cfg)
70+
if args.evaluation:
71+
cfg["pipeline"]["Logger"]["tag"] += "_EVAL"
72+
if args.tmp:
73+
cfg["pipeline"]["Logger"]["tag"] = "TMP_" + cfg["pipeline"]["Logger"]["tag"]
74+
if args.bs_one:
75+
cfg["dataloader"]["batch_size"] = 1
76+
77+
cfg["dataset"]["target_mode"] = cfg["model"]["target_mode"]
78+
cfg["dataset"]["num_anchors"] = cfg["model"]["kwargs"]["num_anchors"]
79+
cfg["dataset"]["num_ori_bins"] = cfg["model"]["kwargs"]["num_ori_bins"]
80+
if cfg["dataset"]["name"] == "nuScenes":
81+
nc = len(cfg["dataset"]["included_classes"])
82+
cfg["model"]["kwargs"]["num_classes"] = nc if nc > 0 else 10
83+
cfg["model"]["nuscenes"] = True
84+
cfg["model"]["kwargs"]["input_dim"] = 3 + len(
85+
cfg["dataset"]["additional_features"]
86+
)
87+
else:
88+
cfg["model"]["kwargs"]["num_classes"] = 1
89+
cfg["model"]["nuscenes"] = False
90+
cfg["model"]["kwargs"]["input_dim"] = 3
91+
92+
model = get_model(cfg["model"])
93+
model.cuda()
94+
95+
pipeline = Pipeline(model, cfg["pipeline"])
96+
97+
if args.ckpt:
98+
pipeline.load_ckpt(model, args.ckpt)
99+
elif args.cont and pipeline.sigterm_ckpt_exists():
100+
pipeline.load_sigterm_ckpt(model)
101+
102+
# training or evaluation
103+
if not args.evaluation:
104+
run_training(model, pipeline, cfg)
105+
else:
106+
run_evaluation(model, pipeline, cfg)
107+
108+
pipeline.close()

lib/iou3d/iou3d/__init__.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import torch
2+
import iou3d_cuda
3+
4+
5+
# From https://github.com/sshaoshuai/PointRCNN/blob/master/lib/utils/iou3d/iou3d_utils.py
6+
7+
8+
def boxes3d_to_bev_torch(boxes3d):
9+
"""
10+
:param boxes3d: (N, 7) [x, y, z, h, w, l, ry]
11+
:return:
12+
boxes_bev: (N, 5) [x1, y1, x2, y2, ry]
13+
"""
14+
boxes_bev = boxes3d.new(torch.Size((boxes3d.shape[0], 5)))
15+
16+
cu, cv = boxes3d[:, 0], boxes3d[:, 2]
17+
half_l, half_w = boxes3d[:, 5] / 2, boxes3d[:, 4] / 2
18+
boxes_bev[:, 0], boxes_bev[:, 1] = cu - half_l, cv - half_w
19+
boxes_bev[:, 2], boxes_bev[:, 3] = cu + half_l, cv + half_w
20+
boxes_bev[:, 4] = boxes3d[:, 6]
21+
return boxes_bev
22+
23+
24+
def boxes_iou_bev(boxes_a, boxes_b):
25+
"""
26+
:param boxes_a: (M, 5)
27+
:param boxes_b: (N, 5)
28+
:return:
29+
ans_iou: (M, N)
30+
"""
31+
32+
ans_iou = torch.cuda.FloatTensor(torch.Size((boxes_a.shape[0], boxes_b.shape[0]))).zero_()
33+
34+
iou3d_cuda.boxes_iou_bev_gpu(boxes_a.contiguous(), boxes_b.contiguous(), ans_iou)
35+
36+
return ans_iou
37+
38+
39+
def boxes_iou3d_gpu(boxes_a, boxes_b):
40+
"""
41+
:param boxes_a: (M, 7) [x, y, z, h, w, l, ry]
42+
:param boxes_b: (N, 7) [x, y, z, h, w, l, ry]
43+
:return:
44+
ans_iou: (M, N)
45+
"""
46+
boxes_a_bev = boxes3d_to_bev_torch(boxes_a)
47+
boxes_b_bev = boxes3d_to_bev_torch(boxes_b)
48+
49+
# bev overlap
50+
overlaps_bev = torch.cuda.FloatTensor(torch.Size((boxes_a.shape[0], boxes_b.shape[0]))).zero_() # (N, M)
51+
iou3d_cuda.boxes_overlap_bev_gpu(boxes_a_bev.contiguous(), boxes_b_bev.contiguous(), overlaps_bev)
52+
53+
# height overlap
54+
boxes_a_height_min = (boxes_a[:, 1] - boxes_a[:, 3]).view(-1, 1)
55+
boxes_a_height_max = boxes_a[:, 1].view(-1, 1)
56+
boxes_b_height_min = (boxes_b[:, 1] - boxes_b[:, 3]).view(1, -1)
57+
boxes_b_height_max = boxes_b[:, 1].view(1, -1)
58+
59+
max_of_min = torch.max(boxes_a_height_min, boxes_b_height_min)
60+
min_of_max = torch.min(boxes_a_height_max, boxes_b_height_max)
61+
overlaps_h = torch.clamp(min_of_max - max_of_min, min=0)
62+
63+
# 3d iou
64+
overlaps_3d = overlaps_bev * overlaps_h
65+
66+
vol_a = (boxes_a[:, 3] * boxes_a[:, 4] * boxes_a[:, 5]).view(-1, 1)
67+
vol_b = (boxes_b[:, 3] * boxes_b[:, 4] * boxes_b[:, 5]).view(1, -1)
68+
69+
iou3d = overlaps_3d / torch.clamp(vol_a + vol_b - overlaps_3d, min=1e-7)
70+
71+
return iou3d
72+
73+
74+
def nms_gpu(boxes, scores, thresh):
75+
"""
76+
:param boxes: (N, 5) [x1, y1, x2, y2, ry]
77+
:param scores: (N)
78+
:param thresh:
79+
:return:
80+
"""
81+
# areas = (x2 - x1) * (y2 - y1)
82+
order = scores.sort(0, descending=True)[1]
83+
84+
boxes = boxes[order].contiguous()
85+
86+
keep = torch.LongTensor(boxes.size(0))
87+
num_out = iou3d_cuda.nms_gpu(boxes, keep, thresh)
88+
return order[keep[:num_out].cuda()].contiguous()
89+
90+
91+
def nms_normal_gpu(boxes, scores, thresh):
92+
"""
93+
:param boxes: (N, 5) [x1, y1, x2, y2, ry]
94+
:param scores: (N)
95+
:param thresh:
96+
:return:
97+
"""
98+
# areas = (x2 - x1) * (y2 - y1)
99+
order = scores.sort(0, descending=True)[1]
100+
101+
boxes = boxes[order].contiguous()
102+
103+
keep = torch.LongTensor(boxes.size(0))
104+
num_out = iou3d_cuda.nms_normal_gpu(boxes, keep, thresh)
105+
return order[keep[:num_out].cuda()].contiguous()
106+
107+
108+
def nms_dist_gpu(boxes, scores, l_ave, w_ave, thresh):
109+
"""
110+
:param boxes: (N, 5) [x1, y1, x2, y2, ry]
111+
:param scores: (N)
112+
:param thresh:
113+
:return:
114+
"""
115+
# areas = (x2 - x1) * (y2 - y1)
116+
order = scores.sort(0, descending=True)[1]
117+
118+
boxes = boxes[order].contiguous()
119+
120+
keep = torch.LongTensor(boxes.size(0))
121+
num_out = iou3d_cuda.nms_dist_gpu(boxes, keep, l_ave, w_ave, thresh)
122+
return order[keep[:num_out].cuda()].contiguous()
123+
124+
125+
if __name__ == '__main__':
126+
pass

0 commit comments

Comments
 (0)