Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Several bugs fixed, Typos corrected, Docker Supported #10

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Start with a base image containing Miniconda installed
FROM continuumio/miniconda3

# Set the working directory in the Docker container to /workspace/tum-traffic-dataset-dev-kit
WORKDIR /workspace/tum-traffic-dataset-dev-kit

# Install any necessary system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
git \
wget \
libncurses5-dev \
libncursesw5-dev \
libsm6 \
libxext6 \
libgl1-mesa-glx \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# Create a new Conda environment with Python 3.8 named tum-traffic-dataset-dev-kit
RUN conda create -n tum-traffic-dataset-dev-kit python=3.8 -y
SHELL ["conda", "run", "-n", "tum-traffic-dataset-dev-kit", "/bin/bash", "-c"]

# Install Pytorch env
RUN conda install pytorch==2.1.0 torchvision==0.16.0 torchaudio==2.1.0 pytorch-cuda=11.8 -c pytorch -c nvidia

# Copy the application's requirements file and install any additional requirements
COPY requirements_docker.txt .
RUN pip install -r requirements_docker.txt

# Install additional tools
RUN conda install -c conda-forge jupyterlab -y && \
conda install -c conda-forge fvcore && \
conda install -c conda-forge iopath && \
conda install conda-forge::ros-sensor-msgs && \
conda install pytorch3d -c pytorch3d

# Install specific Python packages via pip
RUN pip install -U pip && \
pip install -v "git+https://github.com/klintan/pypcd.git"

RUN pip install -v numpy==1.19.5

# Copy the rest of your application's source code from the local file system to the filesystem of the container
COPY . /workspace/tum-traffic-dataset-dev-kit/

# Set an environment variable to ensure Python scripts find the modules
ENV PYTHONPATH "${PYTHONPATH}:/workspace/tum-traffic-dataset-dev-kit"

# Expose the port the app runs on
EXPOSE 8888

# Set the default command to execute when creating a new container
CMD ["bash"]
45 changes: 38 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,20 @@ Add dev kit's root directory to `PYTHONPATH`:
export PYTHONPATH=$PYTHONPATH:/home/<USERNAME>/tum-traffic-dataset-dev-kit/
```

### 🐳 Using Dev-Kit with Docker

We provide a Dockerfile to build an image. Make sure your docker version is >= 19.03.

```bash
# Building a Docker Image
docker build -t tum-traffic-dataset-dev-kit .

# Run the Docker container, assuming you've set the DATA_DIR environment variable here
docker run --gpus all --shm-size=8g -it -v ${DATA_DIR}:/workspace/tum-traffic-dataset-dev-kit/data tum-traffic-dataset-dev-kit
```

Make sure your DATA_DIR environment variable points to the directory(volume name) containing the dataset. Additionally, the Docker commands above assume that your machine has GPU support and is configured with the appropriate NVIDIA driver and CUDA version.

## 📃 Dataset Structure
#### 1) TUM Traffic A9 Highway Dataset (`TUMTraf-A9`)
The TUM Traffic A9 Highway Dataset (`TUMTraf-A9`) contains 5 subsets (`s00` to `s04`) and is structured in the following way:
Expand Down Expand Up @@ -225,9 +239,10 @@ python tum-traffic-dataset-dev-kit/src/visualization/visualize_image_with_3d_box
--output_folder_path_visualization <OUTPUT_FOLDER_PATH> \
--detections_coordinate_system_origin [s110_base,s110_lidar_ouster_south] \
--labels_coordinate_system_origin [s110_base,s110_lidar_ouster_south]
--file_path_calibration_data <CALIB_FILE_PATH>
```

| Visualization south2 in camera:`--viz_mode box3d,point_cloud` | Visualization south1 camera: `--vis_mode box2d,box3d,mask,track_history` |
| Visualization south2 in camera:`--viz_mode box3d,point_cloud` | Visualization south1 camera: `--viz_mode box2d,box3d,mask,track_history` |
|---------------------------------------------------------------|------------------------------------------------------------------------|
<p float="left">
<img src="img/1651673053_741453105_s110_camera_basler_south2_8mm.jpg" width="49%" />
Expand All @@ -247,7 +262,7 @@ python tum-traffic-dataset-dev-kit/src/visualization/visualize_point_cloud_with_

Bird's Eye View | Side View
:-------------------------:|:-------------------------:
![](img/1688626050_947334296_s110_lidar_ouster_south_and_vehicle_lidar_robosense_registered_point_cloud_bev.jpg) | ![](img/1688626050_947334296_s110_lidar_ouster_south_and_vehicle_lidar_robosense_registered_point_cloud_custom_view.jpg)
![bev](img/1688626050_947334296_s110_lidar_ouster_south_and_vehicle_lidar_robosense_registered_point_cloud_bev.jpg) | ![side_view](img/1688626050_947334296_s110_lidar_ouster_south_and_vehicle_lidar_robosense_registered_point_cloud_custom_view.jpg)

## ✴️️ Data Split

Expand All @@ -272,15 +287,26 @@ python tum-traffic-dataset-dev-kit/src/registration/point_cloud_registration.py
--save_registered_point_clouds \
--output_folder_path_registered_point_clouds <OUTPUT_FOLDER_PATH_POINT_CLOUDS>
```
Example:
```
python tum-traffic-dataset-dev-kit/src/registration/point_cloud_registration.py --folder_path_point_cloud_source "./tum-traffic-dataset-dev-kit/data/tum/a9_dataset_r02_split/train/point_clouds/s110_lidar_ouster_north" \
--folder_path_point_cloud_target "./tum-traffic-dataset-dev-kit/data/tum/a9_dataset_r02_split/train/point_clouds/s110_lidar_ouster_south" \
--save_registered_point_clouds --output_folder_path_registered_point_clouds "./tum-traffic-dataset-dev-kit/data/tum/a9_dataset_r02_reg/s110_lidar_ouster"
```
![registered_point_cloud](./img/registered_point_cloud.png)

## 🧹 Data Cleaning
A LiDAR preprocessing module reduces noise in point cloud scans:

```
python tum-traffic-dataset-dev-kit/src/preprocessing/remove_noise_from_point_clouds.py --input_folder_path_point_clouds <INPUT_FOLDER_PATH_POINT_CLOUDS> \
python tum-traffic-dataset-dev-kit/src/preprocessing/filter_noise_point_cloud.py --input_folder_path_point_clouds <INPUT_FOLDER_PATH_POINT_CLOUDS> \
--output_folder_path_point_clouds <OUTPUT_FOLDER_PATH_POINT_CLOUDS>
```
Example:
```
python tum-traffic-dataset-dev-kit/src/preprocessing/filter_noise_point_cloud.py --input_folder_path_point_clouds "./tum-traffic-dataset-dev-kit/data/tum/a9_dataset_r02_split/train/s110_lidar_ouster_north" \
--output_folder_path_point_clouds "./tum-traffic-dataset-dev-kit/data/tum/a9_dataset_r02_split_no_noise/train/"
```
![noise_removal](./img/outlier_removal.png)

## ⚡ Label Conversion
Expand All @@ -290,22 +316,27 @@ In addition, a data converter/exporter enables you to convert the labels from Op
### OpenLABEL to YOLO
The following script converts the OpenLABEL labels into YOLO labels:
```
python tum-traffic-dataset-dev-kit/src/converter/conversion_openlabel_to_yolo.py --input_folder_path_labels <INPUT_FOLDER_PATH_LABELS> \
python tum-traffic-dataset-dev-kit/src/label_conversion/conversion_openlabel_to_yolo.py --input_folder_path_labels <INPUT_FOLDER_PATH_LABELS> \
--output_folder_path_labels <OUTPUT_FOLDER_PATH_LABELS>
```

### OpenLABEL to KITTI
The following script converts the OpenLABEL labels into KITTI labels:
```
python tum-traffic-dataset-dev-kit/src/converter/conversion_openlabel_to_kitti.py --root-dir <DATASET_ROOT_DIR> \
python tum-traffic-dataset-dev-kit/src/label_conversion/conversion_openlabel_to_kitti.py --root-dir <DATASET_ROOT_DIR> \
--out-dir <OUTPUT_FOLDER_PATH_LABELS> \
--file-name-format [name,num]
```

Example:
```
python tum-traffic-dataset-dev-kit/src/label_conversion/conversion_openlabel_to_kitti.py --root-dir "./tum-traffic-dataset-dev-kit/data/tum/a9_dataset_r02_split/" \
--out-dir "./tum-traffic-dataset-dev-kit/data/tum/a9_dataset_r02_split_kitti/" \
--file-name-format num
```
### OpenLABEL to nuScenes
The following script converts the OpenLABEL labels into nuScenes labels:
```
python tum-traffic-dataset-dev-kit/src/converter/conversion_openlabel_to_nuscenes.py --root-path <DATASET_ROOT_DIR> \
python tum-traffic-dataset-dev-kit/src/label_conversion/conversion_openlabel_to_nuscenes.py --root-path <DATASET_ROOT_DIR> \
--out-dir <OUTPUT_FOLDER_PATH_LABELS>

```
Expand Down
35 changes: 35 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Use the official NVIDIA PyTorch image which includes CUDA and cuDNN.
FROM nvcr.io/nvidia/pytorch:22.04-py3

# Setting up the working directory
WORKDIR /workspace/tum-traffic-dataset-dev-kit

# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
curl \
libgl1-mesa-glx \
libglib2.0-0 \
&& rm -rf /var/lib/apt/lists/*

# Install specific Python packages via pip
RUN pip install -U pip && \
pip install fvcore iopath && \
pip install "git+https://github.com/facebookresearch/pytorch3d.git@stable" && \
pip install "git+https://github.com/klintan/pypcd.git"

# Copy the Python requirements file and install Python dependencies
COPY requirements.txt /workspace/tum-traffic-dataset-dev-kit/
RUN pip install -r requirements.txt

# Copy the rest of the project files into the working directory
COPY . /workspace/tum-traffic-dataset-dev-kit/

# Set the PYTHONPATH environment variable
ENV PYTHONPATH="${PYTHONPATH}:/workspace/tum-traffic-dataset-dev-kit/"

# Expose any ports the app might need
EXPOSE 8888

# Run the Bash Shell when the container starts
CMD ["bash"]
29 changes: 29 additions & 0 deletions requirements_docker.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
numpy>=1.17
opencv-python>=4.5
matplotlib>=3.3
scipy>=1.6
argparse>=1.1
pyyaml>=5.4
pandas>=1.2
tqdm>=4.60
scikit-learn>=0.0
bitarray>=1.6
progressbar
numba>=0.53

cython>=0.29.32
simplejson
scikit-image>=0.19.3
filterpy >=1.4.5
prettytable>=3.7.0
open3d==0.17.0
flask==3.0.0
dash==2.14.2
werkzeug==3.0.1
pycryptodomex==3.19.0
gnupg==2.3.1
rospkg==1.5.0
protobuf==4.23.4
rosnumpy==0.0.5.2
distinctipy==1.3.3
mmcv==1.6.0
13 changes: 11 additions & 2 deletions src/preprocessing/create_train_val_split.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import argparse
import glob
import os
import shutil

from tqdm import tqdm

Expand All @@ -22,14 +23,22 @@ def copy_to_output(
):
for subset in subsets:
for sensor in sensors:
# Determine the input folder path
input_folder_path = os.path.join(dataset_root_folder_path, subset, sensor_modality, sensor)
file_paths_sub_set = sorted(glob.glob(input_folder_path + "/*"))

# Get all files under this path
file_paths_sub_set = sorted(glob.glob(os.path.join(input_folder_path, "*")))

for file_path_sub_set in file_paths_sub_set:
file_name_in_sub_set = os.path.basename(file_path_sub_set)

# If the filenames match, perform a copy
if file_name == file_name_in_sub_set:
input_file_path = os.path.join(input_folder_path, file_name)
output_file_path = os.path.join(output_folder_path, file_name)
os.system(f"cp {input_file_path} {output_file_path}")

# Use shutil.copy which perform same behavior on Linux and Windows to copy files
shutil.copy(input_file_path, output_file_path)
return


Expand Down
99 changes: 49 additions & 50 deletions src/preprocessing/filter_noise_point_cloud.py
Original file line number Diff line number Diff line change
@@ -1,74 +1,73 @@
import open3d as o3d
import numpy as np
import os

from pypcd import pypcd
import shutil
import argparse

from src.utils.point_cloud_registration_utils import read_point_cloud_with_intensity, write_point_cloud_with_intensity

# Description: Remove noise (outliers) from point clouds
# Description: Remove noise (outliers) from point clouds and copy non-point cloud files
# Example: python a9-dataset-dev-kit/src/preprocessing/filter_noise_point_cloud.py --input_folder_path_point_clouds <INPUT_FOLDER_PATH_POINT_CLOUDS> \
# --output_folder_path_point_clouds <OUTPUT_FOLDER_PATH_POINT_CLOUDS> \
# --nb_points 1 \
# --radius 0.4
# --output_folder_path_point_clouds <OUTPUT_FOLDER_PATH_POINT_CLOUDS> \
# --nb_points 1 \
# --radius 0.4

def process_point_cloud(input_path, output_path, nb_points, radius):
point_cloud_array, header = read_point_cloud_with_intensity(input_path)
xyz = point_cloud_array[:, :3]
intensities = point_cloud_array[:, 3]
max_intensity = np.max(intensities)
intensities_norm = intensities / max_intensity
intensities_norm_three_col = np.c_[intensities_norm, intensities_norm, intensities_norm]

pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(xyz)
pcd.colors = o3d.utility.Vector3dVector(intensities_norm_three_col)

print("num points before: ", str(len(pcd.points)))
pcd_filtered, indices_keep = pcd.remove_radius_outlier(nb_points, radius)
print("num points after: ", str(len(pcd.points)))
print("removed ", str(len(pcd.points) - len(indices_keep)), " outliers.")

points_array = np.asarray(pcd_filtered.points)
intensity_array = np.asarray(pcd.colors)
# filter intensity array
intensity_array = intensity_array[indices_keep]
point_cloud_array = np.c_[points_array, intensity_array[:, 0]]
write_point_cloud_with_intensity(output_path,point_cloud_array, header)

# Supports traversing multi-layer folders
def process_directory(input_path, output_path, nb_points, radius):
if not os.path.exists(output_path):
# os.mkdir(output_path)
os.makedirs(output_path, exist_ok=True) # can deal with intermediate folders

for file_name in os.listdir(input_path):
src = os.path.join(input_path, file_name)
dst = os.path.join(output_path, file_name)
if os.path.isdir(src):
process_directory(src, dst, nb_points, radius)
elif src.endswith('.pcd'):
process_point_cloud(src, dst, nb_points, radius)
else:
shutil.copy(src, dst)

if __name__ == "__main__":
argparser = argparse.ArgumentParser(description=__doc__)
argparser.add_argument(
"--input_folder_path_point_clouds", default="point_clouds", type=str, help="Input folder path of point clouds"
)
argparser.add_argument(
"--output_folder_path_point_clouds",
default="output",
type=str,
help="Output folder path of filtered point clouds",
"--output_folder_path_point_clouds", default="output", type=str, help="Output folder path of filtered point clouds"
)
argparser.add_argument(
"--nb_points",
default="1",
type=int,
help="Pick the minimum amount of points that the sphere should contain. Higher nb_points removes more points.",
"--nb_points", default=1, type=int, help="Minimum number of points in sphere"
)
argparser.add_argument(
"--radius",
default="0.4",
type=float,
help="Defines the radius of the sphere that will be used for counting the neighbors.",
"--radius", default=0.4, type=float, help="Radius of sphere to check point density"
)

args = argparser.parse_args()
input_folder_path_point_cloud = args.input_folder_path_point_clouds
output_folder_path_point_cloud = args.output_folder_path_point_clouds
nb_points = args.nb_points
radius = args.radius

if not os.path.exists(output_folder_path_point_cloud):
os.mkdir(output_folder_path_point_cloud)

for file_name in sorted(os.listdir(input_folder_path_point_cloud)):
point_cloud_array, header = read_point_cloud_with_intensity(
os.path.join(args.input_folder_path_point_clouds, file_name))
xyz = point_cloud_array[:, :3]
intensities = point_cloud_array[:, 3]
max_intensity = np.max(intensities)
intensities_norm = np.array(intensities / max_intensity)
intensities_norm_two_col = np.c_[intensities_norm, intensities_norm]
intensities_norm_three_col = np.c_[intensities_norm_two_col, intensities_norm]

pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(xyz)
pcd.colors = o3d.utility.Vector3dVector(intensities_norm_three_col)
process_directory(args.input_folder_path_point_clouds, args.output_folder_path_point_clouds, args.nb_points, args.radius)

print("num points before: ", str(len(pcd.points)))
pcd_filtered, indices_keep = pcd.remove_radius_outlier(nb_points, radius)
print("num points after: ", str(len(pcd.points)))
print("removed ", str(len(pcd.points) - len(indices_keep)), " outliers.")

points_array = np.asarray(pcd_filtered.points)
intensity_array = np.asarray(pcd.colors)
# filter intensity array
intensity_array = intensity_array[indices_keep]
point_cloud_array = np.c_[points_array, intensity_array[:, 0]]
write_point_cloud_with_intensity(os.path.join(args.output_folder_path_point_clouds, file_name),
point_cloud_array, header)