Skip to content

Commit 289885a

Browse files
committed
new
0 parents  commit 289885a

File tree

395 files changed

+212611
-0
lines changed

Some content is hidden

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

395 files changed

+212611
-0
lines changed

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 fpthink
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# 3D Siamese Voxel-to-BEV Tracker for Sparse Point Clouds
2+
3+
## Introduction
4+
5+
This repository is released for V2B in our [NeurIPS 2021 paper (poster)](https://arxiv.org/pdf/2111.04426.pdf).
6+
7+
**Note**: In order to make the code structure clearer and more reasonable, we refactored the entire project. If you are more familiar with [P2B](https://github.com/HaozheQi/P2B) and the code of our previously published version, you can continue to refer to the code of the [first version](https://drive.google.com/file/d/1yPjC3hES0pU4pzbQsUv9hbCNYu0p4XUo/view?usp=sharing).
8+
9+
## Environment settings
10+
* Create an environment for v2b
11+
```
12+
conda create -n V2B python=3.7
13+
conda activate V2B
14+
```
15+
16+
* Install pytorch and torchvision
17+
```
18+
conda install pytorch==1.4.0 torchvision==0.5.0 cudatoolkit=10.0
19+
```
20+
21+
* Install dependencies.
22+
```
23+
pip install -r requirements.txt
24+
```
25+
26+
* Build `_ext` module.
27+
```
28+
cd V2B_main/lib/pointops
29+
python setup.py install
30+
cd ../../
31+
```
32+
33+
## Data preparation
34+
### [KITTI dataset](https://projet.liris.cnrs.fr/imagine/pub/proceedings/CVPR2012/data/papers/424_O3C-04.pdf)
35+
* Download the [velodyne](http://www.cvlibs.net/download.php?file=data_tracking_velodyne.zip), [calib](http://www.cvlibs.net/download.php?file=data_tracking_calib.zip) and [label_02](http://www.cvlibs.net/download.php?file=data_tracking_label_2.zip) from [KITTI Tracking](http://www.cvlibs.net/datasets/kitti/eval_tracking.php). Unzip the downloaded files and place them under the same parent folder.
36+
37+
### [nuScenes dataset](https://openaccess.thecvf.com/content_CVPR_2020/papers/Caesar_nuScenes_A_Multimodal_Dataset_for_Autonomous_Driving_CVPR_2020_paper.pdf)
38+
* Download the Full dataset (v1.0) from [nuScenes](https://www.nuscenes.org/).
39+
40+
Note that base on the offical code [nuscenes-devkit](https://github.com/nutonomy/nuscenes-devkit), we modify and use it to convert nuScenes format to KITTI format. It requires metadata from nuScenes-lidarseg. Thus, you should replace *category.json* and *lidarseg.json* in the Full dataset (v1.0). We provide these two json files in the nuscenes_json folder.
41+
42+
Executing the following code to convert nuScenes format to KITTI format
43+
```
44+
cd nuscenes-devkit-master/python-sdk/nuscenes/scripts
45+
python export_kitti.py --nusc_dir=<nuScenes dataset path> --nusc_kitti_dir=<output dir> --split=<dataset split>
46+
```
47+
48+
Note that the parameter of "split" should be "train_track" or "val". In our paper, we use the model trained on the KITTI dataset to evaluate the generalization of the model on the nuScenes dataset.
49+
50+
### [Waymo open dataset](https://openaccess.thecvf.com/content_CVPR_2020/papers/Sun_Scalability_in_Perception_for_Autonomous_Driving_Waymo_Open_Dataset_CVPR_2020_paper.pdf)
51+
* We follow the benchmark created by [LiDAR-SOT](https://github.com/TuSimple/LiDAR_SOT) based on the waymo open dataset. You can download and process the waymo dataset as guided by [their code](https://github.com/TuSimple/LiDAR_SOT), and use our code to test model performance on this benchmark.
52+
* The benchmark they built have many things that we don't use, but the following processing results are necessary:
53+
```
54+
[waymo_sot]
55+
[benchmark]
56+
[validation]
57+
[vehicle]
58+
bench_list.json
59+
easy.json
60+
medium.json
61+
hard.json
62+
[pedestrian]
63+
bench_list.json
64+
easy.json
65+
medium.json
66+
hard.json
67+
[pc]
68+
[raw_pc]
69+
Here are some segment.npz files containing raw point cloud data
70+
[gt_info]
71+
Here are some segment.npz files containing tracklet and bbox data
72+
```
73+
74+
**Node**: After you get the dataset, please modify the path variable ```data_dir&val_data_dir``` about the dataset under configuration file ```V2B_main/utils/options```.
75+
76+
## Evaluation
77+
78+
Train a new model:
79+
```
80+
python main.py --which_dataset KITTI/NUSCENES --category_name category_name
81+
```
82+
83+
Test a model:
84+
```
85+
python main.py --which_dataset KITTI/NUSCENES/WAYMO --category_name category_name --train_test test
86+
```
87+
For more preset parameters or command debugging parameters, please refer to the relevant code and change it according to your needs.
88+
89+
**Recommendations**:
90+
- We have provided some pre-trained models under ```V2B_main/results``` folder, you can use and test them directly.
91+
- Since both kitti and waymo are datasets constructed from 64-line LiDAR, nuScenes is a 32-line LiDAR. We recommend you: train your model on KITTI and verify the generalization ability of your model on waymo. Train on nuScenes or simply skip this dataset. We do not recommend that you verify the generalization ability of your model on nuScenes.
92+
93+
## Todo
94+
95+
```
96+
1. Provide visualization codes.
97+
2. Provide test results on waymo open dataset so that you can use the results directly in your paper.
98+
```
99+
100+
## Citation
101+
102+
If you find the code or trained models useful, please consider citing:
103+
104+
```
105+
@inproceedings{hui2021v2b,
106+
title={3D Siamese Voxel-to-BEV Tracker for Sparse Point Clouds},
107+
author={Hui, Le and Wang, Lingpeng and Cheng, Mingmei and Xie, Jin and Yang, Jian},
108+
booktitle={NeurIPS},
109+
year={2021}
110+
}
111+
```
112+
113+
## Acknowledgements
114+
115+
- Thank Qi for his implementation of [P2B](https://github.com/HaozheQi/P2B).
116+
- Thank Pang for the [3D-SOT benchmark](https://arxiv.org/pdf/2103.06028.pdf) based on the waymo open dataset.
117+
118+
## License
119+
This repository is released under MIT License.
120+
Binary file not shown.
249 Bytes
Binary file not shown.
Binary file not shown.
Binary file not shown.

V2B_main/datasets/__init__.py

Whitespace-only changes.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

V2B_main/datasets/base_dataset.py

+193
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import os
2+
import pickle
3+
4+
import numpy as np
5+
import pandas as pd
6+
from pyquaternion import Quaternion
7+
8+
from torch.utils.data import Dataset
9+
10+
from utils.data_classes import PointCloud, BoundingBox
11+
12+
class kittiDataset():
13+
def __init__(self, path, which_dataset):
14+
self.which_dataset = which_dataset
15+
self.KITTI_Folder = path
16+
self.KITTI_velo = os.path.join(self.KITTI_Folder, "velodyne")
17+
self.KITTI_label = os.path.join(self.KITTI_Folder, "label_02")
18+
self.KITTI_calib = os.path.join(self.KITTI_Folder, "calib")
19+
20+
def getSceneID(self, split):
21+
if self.which_dataset.upper() == 'NUSCENES':
22+
if "TRAIN" in split.upper():
23+
# Training SET
24+
if "TINY" in split.upper():
25+
sceneID = [0]
26+
else:
27+
sceneID = list(range(0, 350))
28+
elif "VALID" in split.upper():
29+
# Validation Set
30+
if "TINY" in split.upper():
31+
sceneID = [0]
32+
else:
33+
sceneID = list(range(0, 10))
34+
elif "TEST" in split.upper():
35+
# Testing Set
36+
if "TINY" in split.upper():
37+
sceneID = [0]
38+
else:
39+
sceneID = list(range(0, 150))
40+
else:
41+
# KITTI dataset
42+
if "TRAIN" in split.upper():
43+
# Training SET
44+
if "TINY" in split.upper():
45+
sceneID = list(range(0 ,1))
46+
else:
47+
sceneID = list(range(0, 17))
48+
elif "VALID" in split.upper():
49+
# Validation Set
50+
if "TINY" in split.upper():
51+
sceneID = list(range(0, 1))
52+
else:
53+
sceneID = list(range(17, 19))
54+
elif "TEST" in split.upper():
55+
# Testing Set
56+
if "TINY" in split.upper():
57+
sceneID = list(range(0, 1))
58+
else:
59+
sceneID = list(range(19, 21))
60+
else:
61+
# Full Dataset
62+
sceneID = list(range(21))
63+
return sceneID
64+
65+
def getListOfAnno(self, sceneID, category_name="Car"):
66+
list_of_scene = [
67+
path for path in os.listdir(self.KITTI_velo)
68+
if os.path.isdir(os.path.join(self.KITTI_velo, path)) and
69+
int(path) in sceneID
70+
]
71+
72+
list_of_tracklet_anno = []
73+
for scene in list_of_scene:
74+
# read the label file
75+
label_file = os.path.join(self.KITTI_label, scene + ".txt")
76+
if self.which_dataset.upper()=='NUSCENES':
77+
df = pd.read_csv(
78+
label_file,
79+
sep=' ',
80+
names=[
81+
"frame", "track_id", "type", "truncated", "occluded",
82+
"alpha", "bbox_left", "bbox_top", "bbox_right",
83+
"bbox_bottom", "height", "width", "length", "x", "y", "z",
84+
"rotation_y","score",'num_lidar_pts','is_key_frame'
85+
])
86+
else:
87+
df = pd.read_csv(
88+
label_file,
89+
sep=' ',
90+
names=[
91+
"frame", "track_id", "type", "truncated", "occluded",
92+
"alpha", "bbox_left", "bbox_top", "bbox_right",
93+
"bbox_bottom", "height", "width", "length", "x", "y", "z",
94+
"rotation_y", "score"
95+
])
96+
97+
df = df[df["type"] == category_name]
98+
# insert the scene dim
99+
df.insert(loc=0, column="scene", value=scene)
100+
for track_id in df.track_id.unique():
101+
df_tracklet = df[df["track_id"] == track_id]
102+
df_tracklet = df_tracklet.reset_index(drop=True)
103+
tracklet_anno = [anno for index, anno in df_tracklet.iterrows()]
104+
list_of_tracklet_anno.append(tracklet_anno)
105+
106+
return list_of_tracklet_anno
107+
108+
def read_calib_file(self, filepath):
109+
"""Read in a calibration file and parse into a dictionary."""
110+
data = {}
111+
with open(filepath, 'r') as f:
112+
for line in f.readlines():
113+
values = line.split()
114+
115+
try:
116+
ind = values[0].find(':')
117+
if ind != -1:
118+
data[values[0][:ind]] = np.array(
119+
[float(x) for x in values[1:]]).reshape(3, 4)
120+
else:
121+
data[values[0]] = np.array(
122+
[float(x) for x in values[1:]]).reshape(3, 4)
123+
except ValueError:
124+
data[values[0]] = np.array(
125+
[float(x) for x in values[1:]]).reshape(3, 3)
126+
return data
127+
128+
def getBBandPC(self, anno):
129+
calib_path = os.path.join(self.KITTI_calib, anno['scene'] + ".txt")
130+
calib = self.read_calib_file(calib_path)
131+
# get the Tr_velo_cam matrix, which transforms the point cloud from the velo coordinate system to the cam coordinate system
132+
transf_mat = np.vstack((calib["Tr_velo_cam"], np.array([0, 0, 0, 1]))) # 3*4 --> 4*4
133+
PC, bbox = self.getPCandBBfromPandas(anno, transf_mat)
134+
return PC, bbox
135+
136+
def getPCandBBfromPandas(self, box, calib):
137+
center = [box["x"], box["y"] - box["height"] / 2, box["z"]]
138+
size = [box["width"], box["length"], box["height"]]
139+
orientation = Quaternion(axis=[0, 1, 0], radians=box["rotation_y"]) * Quaternion(axis=[1, 0, 0], radians=np.pi / 2)
140+
BB = BoundingBox(center, size, orientation)
141+
142+
try:
143+
# VELODYNE PointCloud
144+
velodyne_path = os.path.join(self.KITTI_velo, box["scene"], f'{box["frame"]:06}.bin')
145+
PC = PointCloud(np.fromfile(velodyne_path, dtype=np.float32).reshape(-1, 4).T)
146+
# use calib(Tr_velo_cam matrix) rotate from the velo coordinate system to the cam coordinate system
147+
PC.transform(calib)
148+
except :
149+
PC = PointCloud(np.array([[0, 0, 0]]).T)
150+
151+
return PC, BB
152+
153+
def load_data(self, path):
154+
file = open(path, "rb")
155+
data = pickle.load(file)
156+
file.close()
157+
return data
158+
159+
def save_data(self, path, data):
160+
file = open(path, "wb")
161+
pickle.dump(data, file)
162+
file.close()
163+
164+
class BaseDataset(Dataset):
165+
def __init__(self, which_dataset, path, split, category_name="Car", offset_BB=np.zeros(1), scale_BB=np.ones(1)):
166+
167+
self.dataset = kittiDataset(path=path, which_dataset=which_dataset)
168+
169+
self.split = split
170+
self.category_name = category_name
171+
172+
self.getBBandPC = self.dataset.getBBandPC
173+
174+
self.sceneID = self.dataset.getSceneID(split=split)
175+
176+
'''every anno include:
177+
"sceneID", "frame", "track_id", "type",
178+
"truncated", "occluded", "alpha",
179+
"bbox_left", "bbox_top", "bbox_right", "bbox_bottom",
180+
"height", "width", "length", "x", "y", "z", "rotation_y"
181+
'''
182+
# list, every object is a tracklet anno
183+
self.list_of_tracklet_anno = self.dataset.getListOfAnno(self.sceneID, category_name)
184+
self.list_of_anno = [
185+
anno for tracklet_anno in self.list_of_tracklet_anno
186+
for anno in tracklet_anno
187+
]
188+
189+
def isTiny(self):
190+
return ("TINY" in self.split.upper())
191+
192+
def __getitem__(self, index):
193+
return self.getitem(index)

V2B_main/datasets/get_v2b_db.py

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from torch.utils.data import DataLoader
2+
from datasets.v2b_dataset import TrainDataset, TestDataset, TestDataset_WOD
3+
4+
def get_dataset(opts, partition, shuffle=False):
5+
loader, db = None, None
6+
7+
if opts.use_tiny:
8+
split = "Tiny_" + partition
9+
else:
10+
split = "Full_" + partition
11+
12+
13+
if partition in ["Train", "Valid"]:
14+
db = TrainDataset(opts, split)
15+
loader = DataLoader(db, batch_size=opts.batch_size, shuffle=shuffle, num_workers=opts.n_workers, pin_memory=True)
16+
else:
17+
# Test dataset
18+
if opts.which_dataset.upper() in ['KITTI', 'NUSCENES']:
19+
db = TestDataset(opts, split)
20+
loader = DataLoader(db, batch_size=1, shuffle=shuffle, num_workers=opts.n_workers, pin_memory=True, collate_fn=lambda x: x)
21+
else:
22+
# waymo test
23+
db = TestDataset_WOD(opts, pc_type='raw_pc')
24+
25+
return loader, db

0 commit comments

Comments
 (0)