Commit 41b18fd8 authored by zhe chen's avatar zhe chen
Browse files

Use pre-commit to reformat code


Use pre-commit to reformat code
parent ff20ea39
# ==============================================================================
# Binaries and/or source for the following packages or projects
# Binaries and/or source for the following packages or projects
# are presented under one or more of the following open source licenses:
# preprocess.py The OpenLane-V2 Dataset Authors Apache License, Version 2.0
#
......
......@@ -2,39 +2,44 @@
## Download
The files mentioned below can also be downloaded via [OpenDataLab](https://opendatalab.com/OpenLane-V2/download).
It is recommended to use provided [command line interface](https://opendatalab.com/OpenLane-V2/cli) for acceleration.
| Subset | Split | Google Drive <img src="https://ssl.gstatic.com/docs/doclist/images/drive_2022q3_32dp.png" alt="Google Drive" width="18"/> | Baidu Yun <img src="https://nd-static.bdstatic.com/m-static/v20-main/favicon-main.ico" alt="Baidu Yun" width="18"/> | md5 | Size |
| --- | --- | --- | --- | --- | --- |
| subset_A | sample |[sample](https://drive.google.com/file/d/1Ni-L6u1MGKJRAfUXm39PdBIxdk_ntdc6/view?usp=share_link) | [sample](https://pan.baidu.com/s/1ncqwDtuihKTBZROL5vdCAQ?pwd=psev) | 21c607fa5a1930275b7f1409b25042a0 | ~300M |
| subset_A | all | [info](https://drive.google.com/file/d/1t47lNF4H3WhSsAqgsl9lSLIeO0p6n8p4/view?usp=share_link) | [info](https://pan.baidu.com/s/1uXpX4hqlMJLm0W6l12dJ-A?pwd=6rzj) |95bf28ccf22583d20434d75800be065d | ~8.8G |
| | train | [image_0](https://drive.google.com/file/d/1jio4Gj3dNlXmSzebO6D7Uy5oz4EaTNTq/view?usp=share_link) | [image_0](https://pan.baidu.com/s/12aV4CoT8znEY12q4M8XFiw?pwd=m204) | 8ade7daeec1b64f8ab91a50c81d812f6 | ~14.0G |
| | | [image_1](https://drive.google.com/file/d/1IgnvZ2UljL49AzNV6CGNGFLQo6tjNFJq/view?usp=share_link) | [image_1](https://pan.baidu.com/s/1SArnlA2_Om9o0xcGd6-EwA?pwd=khx8) | c78e776f79e2394d2d5d95b7b5985e0f | ~14.3G |
| | | [image_2](https://drive.google.com/file/d/1ViEsK5hukjMGfOm_HrCiQPkGArWrT91o/view?usp=share_link) | [image_2](https://pan.baidu.com/s/1ZghG7gwJqFrGxCEcUffp8A?pwd=0xgm) | 4bf09079144aa54cb4dcd5ff6e00cf79 | ~14.2G |
| | | [image_3](https://drive.google.com/file/d/1r3NYauV0JIghSmEihTxto0MMoyoh4waK/view?usp=share_link) | [image_3](https://pan.baidu.com/s/1ogwmXwS9u-B9nhtHlBTz5g?pwd=sqeg) | fd9e64345445975f462213b209632aee | ~14.4G |
| | | [image_4](https://drive.google.com/file/d/1aBe5yxNBew11YRRu-srQNwc5OloyKP4r/view?usp=share_link) | [image_4](https://pan.baidu.com/s/1tMAmUcZH2SzCiJoxwgk87w?pwd=i1au) | ae07e48c88ea2c3f6afbdf5ff71e9821 | ~14.5G |
| | | [image_5](https://drive.google.com/file/d/1Or-Nmsq4SU24KNe-cn9twVYVprYPUd_y/view?usp=share_link) | [image_5](https://pan.baidu.com/s/1sRyrhcSz-izW2U5x3UACSA?pwd=nzxx) | df62c1f6e6b3fb2a2a0868c78ab19c92 | ~14.2G |
| | | [image_6](https://drive.google.com/file/d/1mSWU-2nMzCO5PGF7yF9scoPntWl7ItfZ/view?usp=share_link) | [image_6](https://pan.baidu.com/s/1P3zn_L6EIGUHb43qWOJYWg?pwd=4wei) | 7bff1ce30329235f8e0f25f6f6653b8f | ~14.4G |
| | val | [image_7](https://drive.google.com/file/d/19N5q-zbjE2QWngAT9xfqgOR3DROTAln0/view?usp=share_link) | [image_7](https://pan.baidu.com/s/1rRkPWg-zG2ygsbMhwXjPKg?pwd=qsvb) | c73af4a7aef2692b96e4e00795120504 | ~21.0G |
| | test | [image_8](https://drive.google.com/file/d/1CvT9w0q8vPldfaajI5YsAqM0ZINT1vJv/view?usp=share_link) | [image_8](https://pan.baidu.com/s/10zjKeuAw350fwTYAeuSLxg?pwd=99ch) | fb2f61e7309e0b48e2697e085a66a259 | ~21.2G |
| subset_B | coming soon | - | - | - | - |
The files mentioned below can also be downloaded via [OpenDataLab](https://opendatalab.com/OpenLane-V2/download). It is
recommended to use provided [command line interface](https://opendatalab.com/OpenLane-V2/cli) for acceleration.
| Subset | Split | Google Drive <img src="https://ssl.gstatic.com/docs/doclist/images/drive_2022q3_32dp.png" alt="Google Drive" width="18"/> | Baidu Yun <img src="https://nd-static.bdstatic.com/m-static/v20-main/favicon-main.ico" alt="Baidu Yun" width="18"/> | md5 | Size |
| -------- | ----------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | -------------------------------- | ------ |
| subset_A | sample | [sample](https://drive.google.com/file/d/1Ni-L6u1MGKJRAfUXm39PdBIxdk_ntdc6/view?usp=share_link) | [sample](https://pan.baidu.com/s/1ncqwDtuihKTBZROL5vdCAQ?pwd=psev) | 21c607fa5a1930275b7f1409b25042a0 | ~300M |
| subset_A | all | [info](https://drive.google.com/file/d/1t47lNF4H3WhSsAqgsl9lSLIeO0p6n8p4/view?usp=share_link) | [info](https://pan.baidu.com/s/1uXpX4hqlMJLm0W6l12dJ-A?pwd=6rzj) | 95bf28ccf22583d20434d75800be065d | ~8.8G |
| | train | [image_0](https://drive.google.com/file/d/1jio4Gj3dNlXmSzebO6D7Uy5oz4EaTNTq/view?usp=share_link) | [image_0](https://pan.baidu.com/s/12aV4CoT8znEY12q4M8XFiw?pwd=m204) | 8ade7daeec1b64f8ab91a50c81d812f6 | ~14.0G |
| | | [image_1](https://drive.google.com/file/d/1IgnvZ2UljL49AzNV6CGNGFLQo6tjNFJq/view?usp=share_link) | [image_1](https://pan.baidu.com/s/1SArnlA2_Om9o0xcGd6-EwA?pwd=khx8) | c78e776f79e2394d2d5d95b7b5985e0f | ~14.3G |
| | | [image_2](https://drive.google.com/file/d/1ViEsK5hukjMGfOm_HrCiQPkGArWrT91o/view?usp=share_link) | [image_2](https://pan.baidu.com/s/1ZghG7gwJqFrGxCEcUffp8A?pwd=0xgm) | 4bf09079144aa54cb4dcd5ff6e00cf79 | ~14.2G |
| | | [image_3](https://drive.google.com/file/d/1r3NYauV0JIghSmEihTxto0MMoyoh4waK/view?usp=share_link) | [image_3](https://pan.baidu.com/s/1ogwmXwS9u-B9nhtHlBTz5g?pwd=sqeg) | fd9e64345445975f462213b209632aee | ~14.4G |
| | | [image_4](https://drive.google.com/file/d/1aBe5yxNBew11YRRu-srQNwc5OloyKP4r/view?usp=share_link) | [image_4](https://pan.baidu.com/s/1tMAmUcZH2SzCiJoxwgk87w?pwd=i1au) | ae07e48c88ea2c3f6afbdf5ff71e9821 | ~14.5G |
| | | [image_5](https://drive.google.com/file/d/1Or-Nmsq4SU24KNe-cn9twVYVprYPUd_y/view?usp=share_link) | [image_5](https://pan.baidu.com/s/1sRyrhcSz-izW2U5x3UACSA?pwd=nzxx) | df62c1f6e6b3fb2a2a0868c78ab19c92 | ~14.2G |
| | | [image_6](https://drive.google.com/file/d/1mSWU-2nMzCO5PGF7yF9scoPntWl7ItfZ/view?usp=share_link) | [image_6](https://pan.baidu.com/s/1P3zn_L6EIGUHb43qWOJYWg?pwd=4wei) | 7bff1ce30329235f8e0f25f6f6653b8f | ~14.4G |
| | val | [image_7](https://drive.google.com/file/d/19N5q-zbjE2QWngAT9xfqgOR3DROTAln0/view?usp=share_link) | [image_7](https://pan.baidu.com/s/1rRkPWg-zG2ygsbMhwXjPKg?pwd=qsvb) | c73af4a7aef2692b96e4e00795120504 | ~21.0G |
| | test | [image_8](https://drive.google.com/file/d/1CvT9w0q8vPldfaajI5YsAqM0ZINT1vJv/view?usp=share_link) | [image_8](https://pan.baidu.com/s/10zjKeuAw350fwTYAeuSLxg?pwd=99ch) | fb2f61e7309e0b48e2697e085a66a259 | ~21.2G |
| subset_B | coming soon | - | - | - | - |
For files in Google Drive, you can use the following command by replacing `[FILE_ID]` and `[FILE_NAME]` accordingly:
```sh
wget --load-cookies /tmp/cookies.txt "https://docs.google.com/uc?export=download&confirm=$(wget --quiet --save-cookies /tmp/cookies.txt --keep-session-cookies --no-check-certificate 'https://docs.google.com/uc?export=download&id=[FILE_ID]' -O- | sed -rn 's/.*confirm=([0-9A-Za-z_]+).*/\1\n/p')&id=[FILE_ID]" -O [FILE_NAME]
```
## Preprocess
The dataset is preprocessed into pickle files representing different collections, which then be used for training models or evaluation:
The dataset is preprocessed into pickle files representing different collections, which then be used for training models
or evaluation:
```sh
cd data
python OpenLane-V2/preprocess.py
python OpenLane-V2/preprocess.py
```
## Hierarchy
The hierarchy of folder `OpenLane-V2/` is described below:
```
└── OpenLane-V2
├── train
......@@ -61,12 +66,14 @@ The hierarchy of folder `OpenLane-V2/` is described below:
```
- `[segment_id]` specifies a sequence of frames, and `[timestamp]` specifies a single frame in a sequence.
- `image/` contains images captured by various cameras, and `info/` contains meta data and annotations of a single frame.
- `image/` contains images captured by various cameras, and `info/` contains meta data and annotations of a single
frame.
- `data_dict_[xxx].json` notes the split of train / val / test under the subset of data.
## Meta Data
The json files under the `info/` folder contain meta data and annotations for each frame.
Each file is formatted as follows:
The json files under the `info/` folder contain meta data and annotations for each frame. Each file is formatted as
follows:
```
{
......@@ -84,19 +91,20 @@ Each file is formatted as follows:
'intrinsic': <dict> -- intrinsic parameters of the camera
},
...
}
}
'pose': <dict> -- ego pose
'annotation': <dict> -- anntations for the current frame
}
```
## Annotations
For a single frame, annotations are formatted as follow:
```
{
'lane_centerline': [ (n lane centerlines in the current frame)
{
{
'id': <int> -- unique ID in the current frame
'points': <float> [n, 3] -- 3D coordiate
'confidence': <float> -- confidence, only for prediction
......@@ -104,7 +112,7 @@ For a single frame, annotations are formatted as follow:
...
],
'traffic_element': [ (k traffic elements in the current frame)
{
{
'id': <int> -- unique ID in the current frame
'category': <int> -- traffic element category
1: 'traffic_light',
......@@ -133,11 +141,11 @@ For a single frame, annotations are formatted as follow:
}
```
- `id` is the identifier of a lane centerline or traffic element and is consistent in a sequence.
For predictions, it can be randomly assigned but unique in a single frame.
- `topology_lclc` and `topology_lcte` are adjacent matrices, where row and column are sorted according to the order of the lists `lane_centerline` and `traffic_element`.
It is a MUST to keep the ordering the same for correct evaluation.
For ground truth, only 0 or 1 is a valid boolean value for an element in the matrix.
For predictions, the value varies from 0 to 1, representing the confidence of the predicted relationship.
- #lane_centerline and #traffic_element are not required to be equal between ground truth and predictions.
In the process of evaluation, a matching of ground truth and predictions is determined.
- `id` is the identifier of a lane centerline or traffic element and is consistent in a sequence. For predictions, it
can be randomly assigned but unique in a single frame.
- `topology_lclc` and `topology_lcte` are adjacent matrices, where row and column are sorted according to the order of
the lists `lane_centerline` and `traffic_element`. It is a MUST to keep the ordering the same for correct evaluation.
For ground truth, only 0 or 1 is a valid boolean value for an element in the matrix. For predictions, the value varies
from 0 to 1, representing the confidence of the predicted relationship.
- # lane_centerline and #traffic_element are not required to be equal between ground truth and predictions.
In the process of evaluation, a matching of ground truth and predictions is determined.
......@@ -2,10 +2,11 @@
## Criterion
The road structure cognition task is defined as inputting the surrounding view images, reconstructing the high-precision map of the self-vehicle, and outputting the recognition result of the direction of the self-vehicle.
The specific expansion is to input the surrounding view images of the vehicle, HDMap; the output is the lane centerlines, the traffic signs, the topology of the lane centerlines, and the correspondence between lanes centerlines and traffic signs.
Below are examples of visualizing annotations and relationships between different elements on 2D images.
The road structure cognition task is defined as inputting the surrounding view images, reconstructing the high-precision
map of the self-vehicle, and outputting the recognition result of the direction of the self-vehicle. The specific
expansion is to input the surrounding view images of the vehicle, HDMap; the output is the lane centerlines, the traffic
signs, the topology of the lane centerlines, and the correspondence between lanes centerlines and traffic signs. Below
are examples of visualizing annotations and relationships between different elements on 2D images.
![image](https://user-images.githubusercontent.com/47048022/209953048-f8ded0da-6005-45b7-8e3d-501dbd422058.png)
![image](https://user-images.githubusercontent.com/47048022/209954207-7b8a1b5a-8243-41d5-91fe-f2de5949107e.png)
# Devkit
Here we describe the API provided by the OpenLane-V2 devkit.
## openlanev2.dataset
### Collection
is a collection of frames.
#### `Collection.get_frame_via_identifier(identifier : tuple) -> Frame`
is a collection of frames.
#### `Collection.get_frame_via_identifier(identifier : tuple) -> Frame`
Returns a frame with the given identifier (split, segment_id, timestamp).
#### `Collection.get_frame_via_index(index : int) -> (tuple, Frame)`
Returns a frame with the given index.
### Frame
is a data structure containing meta data of a frame.
#### `Frame.get_camera_list() -> list`
#### `Frame.get_camera_list() -> list`
Retuens a list of camera names.
#### `Frame.get_pose() -> dict`
Retuens the pose of ego vehicle.
#### `Frame.get_image_path(camera : str) -> str`
Retuens the image path given a camera.
#### `Frame.get_rgb_image(camera : str) -> np.ndarray`
Retuens the RGB image given a camera.
#### `Frame.get_intrinsic(camera : str) -> dict`
Retuens the intrinsic given a camera.
#### `Frame.get_extrinsic(camera : str) -> dict`
Retuens the extrinsic given a camera.
#### `Frame.get_annotations() -> dict`
Retuens annotations of the current frame.
#### `Frame.get_annotations_lane_centerlines() -> dict`
Retuens lane centerline annotations of the current frame.
#### `Frame.get_annotations_traffic_elements() -> dict`
Retuens traffic element annotations of the current frame.
#### `Frame.get_annotations_topology_lclc() -> list`
Retuens the adjacent matrix of topology_lclc.
#### `Frame.get_annotations_topology_lcte() -> list`
Retuens the adjacent matrix of topology_lcte.
## openlanev2.evaluation
#### `evaluate(ground_truth, predictions) -> dict`
Given the ground truth and predictions, which are formatted dict or the path to pickle storing the dict that ground truth is preprocessed pickle file and predictions are formatted as described [here](./submission.md#format), this function returns a dict storing all metrics defined by our task.
Given the ground truth and predictions, which are formatted dict or the path to pickle storing the dict that ground
truth is preprocessed pickle file and predictions are formatted as described [here](./submission.md#format), this
function returns a dict storing all metrics defined by our task.
## openlanev2.io
This subpackage wraps all IO operations of the OpenLane-V2 devkit.
It can be modified for different IO operations.
This subpackage wraps all IO operations of the OpenLane-V2 devkit. It can be modified for different IO operations.
## openlanev2.preprocessing
#### `collect(root_path : str, data_dict : dict, collection : str, point_interval : int = 1) -> None`
Given a data_dict storing identifiers of frames, this function collects meta data the frames and stores it into a pickle file for efficient IO for the following operations.
Given a data_dict storing identifiers of frames, this function collects meta data the frames and stores it into a pickle
file for efficient IO for the following operations.
#### `check_results(results : dict) -> None`
Check format of results.
## openlanev2.visualization
This subpackage provides tools for visualization. Please refer to the [tutorial](../tutorial.ipynb) for examples.
# Metrics
## OpenLane-V2 Score
To evaluate performances on different aspects of the task, several metrics are adopted:
- $\text{DET}_{l}$ for mAP on directed lane centerlines,
- $\text{DET}_{t}$ for mAP on traffic elements,
- $\text{TOP}_{ll}$ for mAP on topology among lane centerlines,
- $\text{TOP}_{lt}$ for mAP on topology between lane centerlines and traffic elements.
- $\\text{DET}\_{l}$ for mAP on directed lane centerlines,
- $\\text{DET}\_{t}$ for mAP on traffic elements,
- $\\text{TOP}\_{ll}$ for mAP on topology among lane centerlines,
- $\\text{TOP}\_{lt}$ for mAP on topology between lane centerlines and traffic elements.
We consolidate the above metrics by computing an average of them, resulting in the **OpenLane-V2 Score (OLS)**.
### Lane Centerline
We adopt the average precision (AP) but define a match of lane centerlines by considering the discrete Frechet distance in the 3D space.
The mAP for lane centerlines is averaged over match thresholds of $\\{1.0, 2.0, 3.0\\}$ on the similarity measure.
We adopt the average precision (AP) but define a match of lane centerlines by considering the discrete Frechet distance
in the 3D space. The mAP for lane centerlines is averaged over match thresholds of $\\{1.0, 2.0, 3.0\\}$ on the
similarity measure.
### Traffic Element
Similarly, we use AP to evaluate the task of traffic element detection.
We consider IoU distance as the affinity measure with a match threshold of $0.75$.
Besides, traffic elements have their own attribute.
For instance, a traffic light can be red or green, which indicates the drivable state of the lane.
Therefore, the mAP is then averaged over attributes.
Similarly, we use AP to evaluate the task of traffic element detection. We consider IoU distance as the affinity measure
with a match threshold of $0.75$. Besides, traffic elements have their own attribute. For instance, a traffic light can
be red or green, which indicates the drivable state of the lane. Therefore, the mAP is then averaged over attributes.
### Topology
The topology metrics estimate the goodness of the relationship among lane centerlines and the relationship between lane centerlines and traffic elements.
To formulate the task of topology prediction as a link prediction problem, we first determine a match of ground truth and predicted vertices (lane centerlines and traffic elements) in the relationship graph.
We choose Frechet and IoU distance for the lane centerline and traffic element respectively.
Also, the metric is average over different recalls.
We adopt mAP from link prediction, which is defined as a mean of APs over all vertices.
Two vertices are regarded as connected if the predicted confidence of the edge is greater than $0.5$.
The AP of a vertex is obtained by ranking all predicted edges and calculating the accumulative mean of the precisions:
The topology metrics estimate the goodness of the relationship among lane centerlines and the relationship between lane
centerlines and traffic elements. To formulate the task of topology prediction as a link prediction problem, we first
determine a match of ground truth and predicted vertices (lane centerlines and traffic elements) in the relationship
graph. We choose Frechet and IoU distance for the lane centerline and traffic element respectively. Also, the metric is
average over different recalls.
$$
mAP = \frac{1}{|V|} \sum_{v \in V} \frac{\sum_{\hat{n} \in \hat{N}(v)} P(\hat{n}) \mathbb{1}(\hat{n} \in N(v))}{|N(v)|},
$$
We adopt mAP from link prediction, which is defined as a mean of APs over all vertices. Two vertices are regarded as
connected if the predicted confidence of the edge is greater than $0.5$. The AP of a vertex is obtained by ranking all
predicted edges and calculating the accumulative mean of the precisions:
$$ mAP = \\frac{1}{|V|} \\sum\_{v \\in V} \\frac{\\sum\_{\\hat{n} \\in \\hat{N}(v)} P(\\hat{n}) \\mathbb{1}(\\hat{n}
\\in N(v))}{|N(v)|}, $$
where $N(v)$ denotes ordered list of neighbors of vertex $v$ ranked by confidence and $P(v)$ is the precision of the $i$-th vertex $v$ in the ordered list.
where $N(v)$ denotes ordered list of neighbors of vertex $v$ ranked by confidence and $P(v)$ is the precision of the
$i$-th vertex $v$ in the ordered list.
Given ground truth and predicted connectivity of lane centerlines, the mAP is calculated on $G^{l} = (V^{l}, E^{l})$ and $\hat{G}^{l} = (\hat{V}^{l}, \hat{E}^{l})$.
As the given graphs are directed, e.g., the ending point of a lane centerline is connected to the starting point of the next lane centerline, we take the mean of mAP over graphs with only in-going or out-going edges.
Given ground truth and predicted connectivity of lane centerlines, the mAP is calculated on $G^{l} = (V^{l}, E^{l})$ and
$\\hat{G}^{l} = (\\hat{V}^{l}, \\hat{E}^{l})$. As the given graphs are directed, e.g., the ending point of a lane
centerline is connected to the starting point of the next lane centerline, we take the mean of mAP over graphs with only
in-going or out-going edges.
To evaluate the predicted topology between lane centerlines and traffic elements, we ignore the relationship among lane centerlines.
The relationship among traffic elements is also not taken into consideration.
Thus this can be seen as a link prediction problem on a bipartite undirected graph that $G = (V^{l} \cup V^{t}, E)$ and $\hat{G} = (\hat{V}^{l} \cup \hat{V}^{t}, \hat{E})$.
To evaluate the predicted topology between lane centerlines and traffic elements, we ignore the relationship among lane
centerlines. The relationship among traffic elements is also not taken into consideration. Thus this can be seen as a
link prediction problem on a bipartite undirected graph that $G = (V^{l} \\cup V^{t}, E)$ and $\\hat{G} = (\\hat{V}^{l}
\\cup \\hat{V}^{t}, \\hat{E})$.
## Distances
To measure the similarity between ground truth and predicted instances, we adopt Frechet and IoU distances for directed curves and 2D bounding boxes respectively.
To measure the similarity between ground truth and predicted instances, we adopt Frechet and IoU distances for directed
curves and 2D bounding boxes respectively.
### Frechet Distance
Discrete Frechet distance measures the geometric similarity of two ordered lists of points.
Given a pair of curves, namely a ground truth $v = (p_1, ..., p_n)$ and a prediction $\hat{v} = (\hat{p}_1, ..., \hat{p}_k)$, a coupling $L$ is a sequence of distinct pairs between $v$ and $\hat{v}$:
Discrete Frechet distance measures the geometric similarity of two ordered lists of points. Given a pair of curves,
namely a ground truth $v = (p_1, ..., p_n)$ and a prediction $\\hat{v} = (\\hat{p}\_1, ..., \\hat{p}\_k)$, a coupling
$L$ is a sequence of distinct pairs between $v$ and $\\hat{v}$:
$$
(p_{a_1} \ , \ \hat{p}_{b_1} \ ), ..., (p_{a_m} \ , \ \hat{p}_{b_m} \ ),
$$
(p\_{a_1} \\ , \\ \\hat{p}_{b_1} \\ ), ..., (p_{a_m} \\ , \\ \\hat{p}\_{b_m} \\ ), $$
where $a_1, ..., a_m$ and $b_1, ..., b_m$ are nondecreasing surjection such that $1 = a_1 \leq a_i \leq a_j \leq a_m = n$ and $1 = b_1 \leq b_i \leq b_j \leq b_m = k$ for all $i < j$. Then the norm $||L||$ of a coupling $L$ is the distance of the most dissimilar pair in $L$ that:
where $a_1, ..., a_m$ and $b_1, ..., b_m$ are nondecreasing surjection such that $1 = a_1 \\leq a_i \\leq a_j \\leq a_m
= n$ and $1 = b_1 \\leq b_i \\leq b_j \\leq b_m = k$ for all $i \< j$. Then the norm $||L||$ of a coupling $L$ is the
distance of the most dissimilar pair in $L$ that:
$$
||L|| = \mathop{max}_{i=1, ..., m} D(p_{a_i} \ , \ \hat{p}_{b_i} \ ).
$$
$$ ||L|| = \\mathop{max}_{i=1, ..., m} D(p_{a_i} \\ , \\ \\hat{p}\_{b_i} \\ ). $$
The Frechet distance of a pair of curves is the minimum norm of all possible coupling that:
$$
D_{Frechet}(v, \hat{v}) = min\\{||L|| \ | \ for \ all \ possible \ coupling \ L\\}.
$$
$$ D\_{Frechet}(v, \\hat{v}) = min\\{||L|| \\ | \\ for \\ all \\ possible \\ coupling \\ L\\}. $$
### IoU Distance
To preserve consistency to the distance mentioned above, we modify the common IoU (Intersection over Union) measure that:
$$
D_{IoU}(X, \hat{X}) = 1 - \frac{|X \cap \hat{X}|}{|X \cup \hat{X}|},
$$
To preserve consistency to the distance mentioned above, we modify the common IoU (Intersection over Union) measure
that:
$$ D\_{IoU}(X, \\hat{X}) = 1 - \\frac{|X \\cap \\hat{X}|}{|X \\cup \\hat{X}|}, $$
where $X$ and $\hat{X}$ is the ground truth and predicted bounding box respectively.
where $X$ and $\\hat{X}$ is the ground truth and predicted bounding box respectively.
......@@ -3,13 +3,17 @@
## `subset_A`
### Temporal Consistency
![image](https://user-images.githubusercontent.com/29263416/228440318-f24136e5-7a26-4b28-bb74-6a448c900756.png)
### Instance Distribution
![image](https://user-images.githubusercontent.com/29263416/228441160-19d399c8-548c-4bef-8909-06ffcd0c027b.png)
### Centerline Property
![image](https://user-images.githubusercontent.com/29263416/228442761-5895e5b4-6d3a-4b90-8dbb-4ecfab98190e.png)
### Topology Distribution
![image](https://user-images.githubusercontent.com/29263416/228443434-a74085dc-28f8-400d-99a2-f0c67b49bf66.png)
# Submission
## Format
The submitted results are required to be stored in a pickle file, which is a dict of identifier and [formatted predictions](../data/README.md#annotations) of a frame:
The submitted results are required to be stored in a pickle file, which is a dict of identifier
and [formatted predictions](../data/README.md#annotations) of a frame:
```
{
......@@ -16,15 +18,19 @@ The submitted results are required to be stored in a pickle file, which is a dic
'traffic_element': ...
'topology_lclc': ...
'topology_lcte': ...
},
},
...
}
}
```
*: For validation, `from iso3166 import countries; countries.get(str)` can be used.
\*: For validation, `from iso3166 import countries; countries.get(str)` can be used.
## Steps
1. Create a team on [EvalAI](https://eval.ai/web/challenges/challenge-page/1925).
2. Click the 'Participate' tag, then choose a team for participation.
3. Choose the phase 'Test Phase (CVPR 2023 Autonomous Driving Challenge)' and upload the file formatted as mentioned above.
4. Check if the submitted file is valid, which is indicated by the 'Status' under the tag of 'My Submissions'. A valid submission would provide performance scores.
3. Choose the phase 'Test Phase (CVPR 2023 Autonomous Driving Challenge)' and upload the file formatted as mentioned
above.
4. Check if the submitted file is valid, which is indicated by the 'Status' under the tag of 'My Submissions'. A valid
submission would provide performance scores.
from .collection import Collection
from .frame import Frame
\ No newline at end of file
# ==============================================================================
# Binaries and/or source for the following packages or projects
# Binaries and/or source for the following packages or projects
# are presented under one or more of the following open source licenses:
# collection.py The OpenLane-V2 Dataset Authors Apache License, Version 2.0
#
......@@ -20,16 +20,17 @@
# limitations under the License.
# ==============================================================================
from .frame import Frame
from ..io import io
from .frame import Frame
class Collection:
r"""
A collection of frames.
"""
def __init__(self, data_root : str, meta_root : str, collection : str) -> None:
def __init__(self, data_root: str, meta_root: str, collection: str) -> None:
r"""
Parameters
----------
......@@ -47,7 +48,7 @@ class Collection:
self.frames = {k: Frame(data_root, v) for k, v in meta.items()}
self.keys = list(self.frames.keys())
def get_frame_via_identifier(self, identifier : tuple) -> Frame:
def get_frame_via_identifier(self, identifier: tuple) -> Frame:
r"""
Returns a frame with the given identifier (split, segment_id, timestamp).
......@@ -64,7 +65,7 @@ class Collection:
"""
return self.frames[identifier]
def get_frame_via_index(self, index : int) -> (tuple, Frame):
def get_frame_via_index(self, index: int) -> (tuple, Frame):
r"""
Returns a frame with the given index.
......
# ==============================================================================
# Binaries and/or source for the following packages or projects
# Binaries and/or source for the following packages or projects
# are presented under one or more of the following open source licenses:
# frame.py The OpenLane-V2 Dataset Authors Apache License, Version 2.0
#
......@@ -31,7 +31,8 @@ class Frame:
A data structure containing meta data of a frame.
"""
def __init__(self, root_path : str, meta : dict) -> None:
def __init__(self, root_path: str, meta: dict) -> None:
r"""
Parameters
----------
......@@ -67,7 +68,7 @@ class Frame:
"""
return self.meta['pose']
def get_image_path(self, camera : str) -> str:
def get_image_path(self, camera: str) -> str:
r"""
Retuens the image path given a camera.
......@@ -83,7 +84,7 @@ class Frame:
"""
return f'{self.root_path}/{self.meta["sensor"][camera]["image_path"]}'
def get_rgb_image(self, camera : str) -> np.ndarray:
def get_rgb_image(self, camera: str) -> np.ndarray:
r"""
Retuens the RGB image given a camera.
......@@ -100,7 +101,7 @@ class Frame:
image_path = self.get_image_path(camera)
return cv2.cvtColor(io.cv2_imread(image_path), cv2.COLOR_BGR2RGB)
def get_intrinsic(self, camera : str) -> dict:
def get_intrinsic(self, camera: str) -> dict:
r"""
Retuens the intrinsic given a camera.
......@@ -116,7 +117,7 @@ class Frame:
"""
return self.meta['sensor'][camera]['intrinsic']
def get_extrinsic(self, camera : str) -> dict:
def get_extrinsic(self, camera: str) -> dict:
r"""
Retuens the extrinsic given a camera.
......@@ -193,7 +194,7 @@ class Frame:
-------
list
[#lane_centerline, #traffic_element].
"""
result = self.get_annotations()
return result['topology_lcte'] if result is not None else result
from .evaluate import evaluate
\ No newline at end of file
# ==============================================================================
# Binaries and/or source for the following packages or projects
# Binaries and/or source for the following packages or projects
# are presented under one or more of the following open source licenses:
# distance.py The OpenLane-V2 Dataset Authors Apache License, Version 2.0
#
......@@ -25,7 +25,8 @@ from scipy.spatial.distance import cdist, euclidean
from similaritymeasures import frechet_dist
def pairwise(xs: list, ys: list, distance_function: callable, mask: np.ndarray = None, relax: bool = False) -> np.ndarray:
def pairwise(xs: list, ys: list, distance_function: callable, mask: np.ndarray = None,
relax: bool = False) -> np.ndarray:
r"""
Calculate pairwise distance.
......@@ -58,6 +59,7 @@ def pairwise(xs: list, ys: list, distance_function: callable, mask: np.ndarray =
result[i][j] = distance_function(x, y) * relaxation_factor
return result
def chamfer_distance(gt: np.ndarray, pred: np.ndarray) -> float:
r"""
Calculate Chamfer distance.
......@@ -118,6 +120,7 @@ def frechet_distance(gt: np.ndarray, pred: np.ndarray) -> float:
return frechet_dist(pred, gt, p=2)
def iou_distance(gt: np.ndarray, pred: np.ndarray) -> float:
r"""
Calculate IoU distance,
......@@ -144,6 +147,7 @@ def iou_distance(gt: np.ndarray, pred: np.ndarray) -> float:
bymax = min(pred[1][1], gt[1][1])
inter = max((bxmax - bxmin), 0) * max((bymax - bymin), 0)
union = (pred[1][0] - pred[0][0]) * (pred[1][1] - pred[0][1]) + (gt[1][0] - gt[0][0]) * (gt[1][1] - gt[0][1]) - inter
union = (pred[1][0] - pred[0][0]) * (pred[1][1] - pred[0][1]) + (gt[1][0] - gt[0][0]) * (
gt[1][1] - gt[0][1]) - inter
return 1 - inter / union
# ==============================================================================
# Binaries and/or source for the following packages or projects
# Binaries and/or source for the following packages or projects
# are presented under one or more of the following open source licenses:
# evaluate.py The OpenLane-V2 Dataset Authors Apache License, Version 2.0
#
......@@ -23,12 +23,12 @@
import numpy as np
from tqdm import tqdm
from .f_score import f1
from .distance import pairwise, chamfer_distance, frechet_distance, iou_distance
from ..io import io
from ..preprocessing import check_results
from ..utils import TRAFFIC_ELEMENT_ATTRIBUTE
from .distance import (chamfer_distance, frechet_distance, iou_distance,
pairwise)
from .f_score import f1
THRESHOLDS_FRECHET = [1.0, 2.0, 3.0]
THRESHOLDS_IOU = [0.75]
......@@ -50,7 +50,7 @@ def _pr_curve(recalls, precisions):
-------
float
average precision
Notes
-----
Adapted from https://github.com/Mrmoore98/VectorMapNet_code/blob/mian/plugin/datasets/evaluation/precision_recall/average_precision_gen.py#L12
......@@ -71,6 +71,7 @@ def _pr_curve(recalls, precisions):
return ap[0]
def _tpfp(gts, preds, confidences, distance_matrix, distance_threshold):
r"""
Generate lists of tp and fp on given distance threshold.
......@@ -129,9 +130,10 @@ def _tpfp(gts, preds, confidences, distance_matrix, distance_threshold):
fp[i] = 1
else:
fp[i] = 1
return tp, fp, idx_match_gt
def _inject(num_gt, pred, tp, idx_match_gt, confidence, distance_threshold, object_type):
r"""
Inject tp matching into predictions.
......@@ -174,6 +176,7 @@ def _inject(num_gt, pred, tp, idx_match_gt, confidence, distance_threshold, obje
pred[f'{object_type}_{distance_threshold}_confidence'] = confidence
pred[f'{object_type}_{distance_threshold}_confidence_thresholds'] = confidence_thresholds
def _AP(gts, preds, distance_matrixs, distance_threshold, object_type, filter, inject):
r"""
Calculate AP on given distance threshold.
......@@ -213,10 +216,10 @@ def _AP(gts, preds, distance_matrixs, distance_threshold, object_type, filter, i
filtered_distance_matrix = filtered_distance_matrix[[filter(gt) for gt in gts[token][object_type]], :]
filtered_distance_matrix = filtered_distance_matrix[:, [filter(pred) for pred in preds[token][object_type]]]
tp, fp, idx_match_gt = _tpfp(
gts=gt,
preds=pred,
confidences=confidence,
distance_matrix=filtered_distance_matrix,
gts=gt,
preds=pred,
confidences=confidence,
distance_matrix=filtered_distance_matrix,
distance_threshold=distance_threshold,
)
tps.append(tp)
......@@ -249,6 +252,7 @@ def _AP(gts, preds, distance_matrixs, distance_threshold, object_type, filter, i
precisions = tps / np.maximum((tps + fps), eps)
return _pr_curve(recalls=recalls, precisions=precisions)
def _mAP_over_threshold(gts, preds, distance_matrixs, distance_thresholds, object_type, filter, inject):
r"""
Calculate mAP over distance thresholds.
......@@ -277,15 +281,16 @@ def _mAP_over_threshold(gts, preds, distance_matrixs, distance_thresholds, objec
"""
return np.asarray([_AP(
gts=gts,
preds=preds,
gts=gts,
preds=preds,
distance_matrixs=distance_matrixs,
distance_threshold=distance_threshold,
distance_threshold=distance_threshold,
object_type=object_type,
filter=filter,
inject=inject,
) for distance_threshold in distance_thresholds])
def _average_precision_per_vertex(gts, preds, confidences):
r"""
Calculate average precision for a vertex.
......@@ -334,7 +339,8 @@ def _average_precision_per_vertex(gts, preds, confidences):
precisions = tp / np.maximum((tp + fp), eps)
return np.dot(precisions, rel) / num_gts
def _AP_directerd(gts, preds):
r"""
Calculate average precision on the given adjacent matrixs,
......@@ -375,6 +381,7 @@ def _AP_directerd(gts, preds):
return acc
def _AP_undirecterd(gts, preds):
r"""
Calculate average precision on the given adjacent matrixs,
......@@ -403,7 +410,7 @@ def _AP_undirecterd(gts, preds):
confidence = pred[pred > THRESHOLD_RELATIONSHIP_CONFIDENCE]
pred = indices[pred > THRESHOLD_RELATIONSHIP_CONFIDENCE]
acc.append(_average_precision_per_vertex(gt, pred, confidence))
indices = np.arange(gts.shape[0])
for gt, pred in zip(gts.T, preds.T):
gt = indices[gt.astype(bool)]
......@@ -413,6 +420,7 @@ def _AP_undirecterd(gts, preds):
return acc
def _mAP_topology_lclc(gts, preds, distance_thresholds):
r"""
Calculate mAP on topology among lane centerlines.
......@@ -441,7 +449,8 @@ def _mAP_topology_lclc(gts, preds, distance_thresholds):
idx_match_gt = preds[token][f'lane_centerline_{distance_threshold}_idx_match_gt']
confidence = preds[token][f'lane_centerline_{distance_threshold}_confidence']
confidence_thresholds = preds[token][f'lane_centerline_{distance_threshold}_confidence_thresholds']
gt_pred = {m: i for i, (m, c) in enumerate(zip(idx_match_gt, confidence)) if c >= confidence_thresholds[r] and not np.isnan(m)}
gt_pred = {m: i for i, (m, c) in enumerate(zip(idx_match_gt, confidence)) if
c >= confidence_thresholds[r] and not np.isnan(m)}
gts_topology_lclc = gts[token]['topology_lclc']
if 0 in gts_topology_lclc.shape:
......@@ -452,12 +461,14 @@ def _mAP_topology_lclc(gts, preds, distance_thresholds):
for j in range(preds_topology_lclc.shape[1]):
if i in gt_pred and j in gt_pred:
preds_topology_lclc[i][j] = preds_topology_lclc_unmatched[gt_pred[i]][gt_pred[j]]
preds_topology_lclc[np.isnan(preds_topology_lclc)] = 1 - gts_topology_lclc[np.isnan(preds_topology_lclc)]
preds_topology_lclc[np.isnan(preds_topology_lclc)] = 1 - gts_topology_lclc[
np.isnan(preds_topology_lclc)]
acc.append(_AP_directerd(gts=gts_topology_lclc, preds=preds_topology_lclc))
return np.hstack(acc).mean()
def _mAP_topology_lcte(gts, preds, distance_thresholds):
r"""
Calculate mAP on topology between lane centerlines and traffic elements.
......@@ -484,17 +495,23 @@ def _mAP_topology_lcte(gts, preds, distance_thresholds):
for token in gts.keys():
preds_topology_lcte_unmatched = preds[token]['topology_lcte']
idx_match_gt_lane_centerline = preds[token][f'lane_centerline_{distance_threshold_lane_centerline}_idx_match_gt']
confidence_lane_centerline = preds[token][f'lane_centerline_{distance_threshold_lane_centerline}_confidence']
confidence_thresholds_lane_centerline = preds[token][f'lane_centerline_{distance_threshold_lane_centerline}_confidence_thresholds']
idx_match_gt_lane_centerline = preds[token][
f'lane_centerline_{distance_threshold_lane_centerline}_idx_match_gt']
confidence_lane_centerline = preds[token][
f'lane_centerline_{distance_threshold_lane_centerline}_confidence']
confidence_thresholds_lane_centerline = preds[token][
f'lane_centerline_{distance_threshold_lane_centerline}_confidence_thresholds']
gt_pred_lane_centerline = {
m: i for i, (m, c) in enumerate(zip(idx_match_gt_lane_centerline, confidence_lane_centerline)) \
if c >= confidence_thresholds_lane_centerline[r] and not np.isnan(m)
}
idx_match_gt_traffic_element = preds[token][f'traffic_element_{distance_threshold_traffic_element}_idx_match_gt']
confidence_traffic_element = preds[token][f'traffic_element_{distance_threshold_traffic_element}_confidence']
confidence_thresholds_traffic_element = preds[token][f'traffic_element_{distance_threshold_traffic_element}_confidence_thresholds']
idx_match_gt_traffic_element = preds[token][
f'traffic_element_{distance_threshold_traffic_element}_idx_match_gt']
confidence_traffic_element = preds[token][
f'traffic_element_{distance_threshold_traffic_element}_confidence']
confidence_thresholds_traffic_element = preds[token][
f'traffic_element_{distance_threshold_traffic_element}_confidence_thresholds']
gt_pred_traffic_element = {
m: i for i, (m, c) in enumerate(zip(idx_match_gt_traffic_element, confidence_traffic_element)) \
if c >= confidence_thresholds_traffic_element[r] and not np.isnan(m)
......@@ -508,13 +525,16 @@ def _mAP_topology_lcte(gts, preds, distance_thresholds):
for i in range(preds_topology_lcte.shape[0]):
for j in range(preds_topology_lcte.shape[1]):
if i in gt_pred_lane_centerline and j in gt_pred_traffic_element:
preds_topology_lcte[i][j] = preds_topology_lcte_unmatched[gt_pred_lane_centerline[i]][gt_pred_traffic_element[j]]
preds_topology_lcte[np.isnan(preds_topology_lcte)] = 1 - gts_topology_lcte[np.isnan(preds_topology_lcte)]
preds_topology_lcte[i][j] = preds_topology_lcte_unmatched[gt_pred_lane_centerline[i]][
gt_pred_traffic_element[j]]
preds_topology_lcte[np.isnan(preds_topology_lcte)] = 1 - gts_topology_lcte[
np.isnan(preds_topology_lcte)]
acc.append(_AP_undirecterd(gts=gts_topology_lcte, preds=preds_topology_lcte))
return np.hstack(acc).mean()
def evaluate(ground_truth, predictions, verbose=True):
r"""
Evaluate the road structure cognition task.
......@@ -536,7 +556,7 @@ def evaluate(ground_truth, predictions, verbose=True):
One of pred_path and pred_dict must be None,
these two arguments provide flexibility for formatting the results only.
"""
"""
if isinstance(ground_truth, str):
ground_truth = io.pickle_load(ground_truth)
......@@ -546,7 +566,7 @@ def evaluate(ground_truth, predictions, verbose=True):
else:
if isinstance(predictions, str):
predictions = io.pickle_load(predictions)
check_results(predictions) # check results format
check_results(predictions) # check results format
predictions = predictions['results']
gts = {}
......@@ -565,7 +585,7 @@ def evaluate(ground_truth, predictions, verbose=True):
assert set(gts.keys()) == set(preds.keys()), '#frame differs'
"""
calculate distances between gts and preds
calculate distances between gts and preds
"""
distance_matrixs = {
......@@ -574,7 +594,6 @@ def evaluate(ground_truth, predictions, verbose=True):
}
for token in tqdm(gts.keys(), desc='calculating distances:', ncols=80, disable=not verbose):
mask = pairwise(
[gt['points'] for gt in gts[token]['lane_centerline']],
[pred['points'] for pred in preds[token]['lane_centerline']],
......@@ -610,33 +629,33 @@ def evaluate(ground_truth, predictions, verbose=True):
"""
metrics['OpenLane-V2 Score']['DET_l'] = _mAP_over_threshold(
gts=gts,
preds=preds,
distance_matrixs=distance_matrixs['frechet'],
gts=gts,
preds=preds,
distance_matrixs=distance_matrixs['frechet'],
distance_thresholds=THRESHOLDS_FRECHET,
object_type='lane_centerline',
filter=lambda _: True,
inject=True, # save tp for eval on graph
inject=True, # save tp for eval on graph
).mean()
metrics['OpenLane-V2 Score']['DET_t'] = np.hstack([_mAP_over_threshold(
gts=gts,
preds=preds,
distance_matrixs=distance_matrixs['iou'],
distance_thresholds=THRESHOLDS_IOU,
gts=gts,
preds=preds,
distance_matrixs=distance_matrixs['iou'],
distance_thresholds=THRESHOLDS_IOU,
object_type='traffic_element',
filter=lambda x: x['attribute'] == idx,
inject=False,
) for idx in TRAFFIC_ELEMENT_ATTRIBUTE.values()]).mean()
_mAP_over_threshold(
gts=gts,
preds=preds,
distance_matrixs=distance_matrixs['iou'],
distance_thresholds=THRESHOLDS_IOU,
gts=gts,
preds=preds,
distance_matrixs=distance_matrixs['iou'],
distance_thresholds=THRESHOLDS_IOU,
object_type='traffic_element',
filter=lambda _: True,
inject=True, # save tp for eval on graph
inject=True, # save tp for eval on graph
)
metrics['OpenLane-V2 Score']['TOP_ll'] = _mAP_topology_lclc(gts, preds, THRESHOLDS_FRECHET)
metrics['OpenLane-V2 Score']['TOP_lt'] = _mAP_topology_lcte(
......
# ==============================================================================
# Binaries and/or source for the following packages or projects
# Binaries and/or source for the following packages or projects
# are presented under one or more of the following open source licenses:
# f_score.py The OpenLane-V2 Dataset Authors Apache License, Version 2.0
#
......@@ -24,7 +24,7 @@
# ==============================================================================
"""
Description: This code is to evaluate 3D lane detection. The optimal matching between ground-truth set and predicted
Description: This code is to evaluate 3D lane detection. The optimal matching between ground-truth set and predicted
set of lanes are sought via solving a min cost flow.
Evaluation metrics includes:
F-scores
......@@ -34,10 +34,9 @@ Evaluation metrics includes:
z error far (0 - 100 m)
"""
import numpy as np
from scipy.interpolate import interp1d
from ortools.graph import pywrapgraph
from scipy.interpolate import interp1d
def resample_laneline_in_x(input_lane, steps, out_vis=False):
......@@ -51,16 +50,16 @@ def resample_laneline_in_x(input_lane, steps, out_vis=False):
"""
# at least two points are included
assert(input_lane.shape[0] >= 2)
assert (input_lane.shape[0] >= 2)
x_min = np.min(input_lane[:, 0])-5
x_max = np.max(input_lane[:, 0])+5
x_min = np.min(input_lane[:, 0]) - 5
x_max = np.max(input_lane[:, 0]) + 5
if input_lane.shape[1] < 3:
input_lane = np.concatenate([input_lane, np.zeros([input_lane.shape[0], 1], dtype=np.float32)], axis=1)
f_y = interp1d(input_lane[:, 0], input_lane[:, 1], fill_value="extrapolate")
f_z = interp1d(input_lane[:, 0], input_lane[:, 2], fill_value="extrapolate")
f_y = interp1d(input_lane[:, 0], input_lane[:, 1], fill_value='extrapolate')
f_z = interp1d(input_lane[:, 0], input_lane[:, 2], fill_value='extrapolate')
y_values = f_y(steps)
z_values = f_z(steps)
......@@ -70,6 +69,7 @@ def resample_laneline_in_x(input_lane, steps, out_vis=False):
return y_values, z_values, output_visibility.astype(np.float32) + 1e-9
return y_values, z_values
def SolveMinCostFlow(adj_mat, cost_mat):
"""
Solving an Assignment Problem with MinCostFlow"
......@@ -87,16 +87,20 @@ def SolveMinCostFlow(adj_mat, cost_mat):
cnt_nonzero_col = int(np.sum(np.sum(adj_mat, axis=0) > 0))
# prepare directed graph for the flow
start_nodes = np.zeros(cnt_1, dtype=np.int32).tolist() +\
np.repeat(np.array(range(1, cnt_1+1)), cnt_2).tolist() + \
[i for i in range(cnt_1+1, cnt_1 + cnt_2 + 1)]
end_nodes = [i for i in range(1, cnt_1+1)] + \
np.repeat(np.array([i for i in range(cnt_1+1, cnt_1 + cnt_2 + 1)]).reshape([1, -1]), cnt_1, axis=0).flatten().tolist() + \
start_nodes = np.zeros(cnt_1, dtype=np.int32).tolist() + \
np.repeat(np.array(range(1, cnt_1 + 1)), cnt_2).tolist() + \
[i for i in range(cnt_1 + 1, cnt_1 + cnt_2 + 1)]
end_nodes = [i for i in range(1, cnt_1 + 1)] + \
np.repeat(np.array([i for i in range(cnt_1 + 1, cnt_1 + cnt_2 + 1)]).reshape([1, -1]), cnt_1,
axis=0).flatten().tolist() + \
[cnt_1 + cnt_2 + 1 for i in range(cnt_2)]
capacities = np.ones(cnt_1, dtype=np.int32).tolist() + adj_mat.flatten().astype(np.int32).tolist() + np.ones(cnt_2, dtype=np.int32).tolist()
costs = (np.zeros(cnt_1, dtype=np.int32).tolist() + cost_mat.flatten().astype(np.int32).tolist() + np.zeros(cnt_2, dtype=np.int32).tolist())
capacities = np.ones(cnt_1, dtype=np.int32).tolist() + adj_mat.flatten().astype(np.int32).tolist() + np.ones(cnt_2,
dtype=np.int32).tolist()
costs = (np.zeros(cnt_1, dtype=np.int32).tolist() + cost_mat.flatten().astype(np.int32).tolist() + np.zeros(cnt_2,
dtype=np.int32).tolist())
# Define an array of supplies at each node.
supplies = [min(cnt_nonzero_row, cnt_nonzero_col)] + np.zeros(cnt_1 + cnt_2, dtype=np.int32).tolist() + [-min(cnt_nonzero_row, cnt_nonzero_col)]
supplies = [min(cnt_nonzero_row, cnt_nonzero_col)] + np.zeros(cnt_1 + cnt_2, dtype=np.int32).tolist() + [
-min(cnt_nonzero_row, cnt_nonzero_col)]
# supplies = [min(cnt_1, cnt_2)] + np.zeros(cnt_1 + cnt_2, dtype=np.int).tolist() + [-min(cnt_1, cnt_2)]
source = 0
sink = cnt_1 + cnt_2 + 1
......@@ -118,7 +122,7 @@ def SolveMinCostFlow(adj_mat, cost_mat):
for arc in range(min_cost_flow.NumArcs()):
# Can ignore arcs leading out of source or into sink.
if min_cost_flow.Tail(arc)!=source and min_cost_flow.Head(arc)!=sink:
if min_cost_flow.Tail(arc) != source and min_cost_flow.Head(arc) != sink:
# Arcs in the solution have a flow value of 1. Their start and end nodes
# give an assignment of worker to task.
......@@ -128,16 +132,17 @@ def SolveMinCostFlow(adj_mat, cost_mat):
# min_cost_flow.Tail(arc)-1,
# min_cost_flow.Head(arc)-cnt_1-1,
# min_cost_flow.UnitCost(arc)))
match_results.append([min_cost_flow.Tail(arc)-1,
min_cost_flow.Head(arc)-cnt_1-1,
match_results.append([min_cost_flow.Tail(arc) - 1,
min_cost_flow.Head(arc) - cnt_1 - 1,
min_cost_flow.UnitCost(arc)])
else:
print('There was an issue with the min cost flow input.')
return match_results
class LaneEval(object):
def __init__(self):
def __init__(self):
self.x_samples = np.linspace(-50, 50, num=100, endpoint=False)
self.dist_th = 1.5
self.ratio_th = 0.75
......@@ -158,20 +163,21 @@ class LaneEval(object):
"""
r_lane, p_lane, c_lane = 0., 0., 0.
gt_lanes = [lane for lane in gt_lanes if lane.shape[0] > 1]
# only consider those pred lanes overlapping with sampling range
pred_category = [pred_category[k] for k, lane in enumerate(pred_lanes)
if lane[0, 0] < self.x_samples[-1] and lane[-1, 0] > self.x_samples[0]]
pred_lanes = [lane for lane in pred_lanes if lane[0, 0] < self.x_samples[-1] and lane[-1, 0] > self.x_samples[0]]
if lane[0, 0] < self.x_samples[-1] and lane[-1, 0] > self.x_samples[0]]
pred_lanes = [lane for lane in pred_lanes if
lane[0, 0] < self.x_samples[-1] and lane[-1, 0] > self.x_samples[0]]
pred_category = [pred_category[k] for k, lane in enumerate(pred_lanes) if lane.shape[0] > 1]
pred_lanes = [lane for lane in pred_lanes if lane.shape[0] > 1]
# only consider those gt lanes overlapping with sampling range
gt_category = [gt_category[k] for k, lane in enumerate(gt_lanes)
if lane[0, 0] < self.x_samples[-1] and lane[-1, 0] > self.x_samples[0]]
if lane[0, 0] < self.x_samples[-1] and lane[-1, 0] > self.x_samples[0]]
gt_lanes = [lane for lane in gt_lanes if lane[0, 0] < self.x_samples[-1] and lane[-1, 0] > self.x_samples[0]]
gt_category = [gt_category[k] for k, lane in enumerate(gt_lanes) if lane.shape[0] > 1]
......@@ -187,7 +193,8 @@ class LaneEval(object):
for i in range(cnt_gt):
min_x = np.min(np.array(gt_lanes[i])[:, 0])
max_x = np.max(np.array(gt_lanes[i])[:, 0])
y_values, z_values, visibility_vec = resample_laneline_in_x(np.array(gt_lanes[i]), self.x_samples, out_vis=True)
y_values, z_values, visibility_vec = resample_laneline_in_x(np.array(gt_lanes[i]), self.x_samples,
out_vis=True)
gt_lanes[i] = np.vstack([y_values, z_values]).T
gt_visibility_mat[i, :] = np.logical_and(self.x_samples >= min_x, self.x_samples <= max_x)
gt_visibility_mat[i, :] = np.logical_and(gt_visibility_mat[i, :], visibility_vec)
......@@ -198,7 +205,8 @@ class LaneEval(object):
# pred_lane = prune_3d_lane_by_range(np.array(pred_lanes[i]), self.x_min, self.x_max)
min_x = np.min(np.array(pred_lanes[i])[:, 0])
max_x = np.max(np.array(pred_lanes[i])[:, 0])
y_values, z_values, visibility_vec = resample_laneline_in_x(np.array(pred_lanes[i]), self.x_samples, out_vis=True)
y_values, z_values, visibility_vec = resample_laneline_in_x(np.array(pred_lanes[i]), self.x_samples,
out_vis=True)
pred_lanes[i] = np.vstack([y_values, z_values]).T
pred_visibility_mat[i, :] = np.logical_and(self.x_samples >= min_x, self.x_samples <= max_x)
pred_visibility_mat[i, :] = np.logical_and(pred_visibility_mat[i, :], visibility_vec)
......@@ -230,7 +238,7 @@ class LaneEval(object):
both_visible_indices = np.logical_and(gt_visibility_mat[i, :] >= 0.5, pred_visibility_mat[j, :] >= 0.5)
both_invisible_indices = np.logical_and(gt_visibility_mat[i, :] < 0.5, pred_visibility_mat[j, :] < 0.5)
other_indices = np.logical_not(np.logical_or(both_visible_indices, both_invisible_indices))
euclidean_dist = np.sqrt(y_dist ** 2 + z_dist ** 2)
euclidean_dist[both_invisible_indices] = 0
euclidean_dist[other_indices] = self.dist_th
......@@ -242,7 +250,7 @@ class LaneEval(object):
# using num_match_mat as cost does not work?
# make sure cost is not set to 0 when it's smaller than 1
cost_ = np.sum(euclidean_dist)
if cost_<1 and cost_>0:
if cost_ < 1 and cost_ > 0:
cost_ = 1
else:
cost_ = (cost_).astype(int)
......@@ -272,8 +280,9 @@ class LaneEval(object):
p_lane += 1
match_pred_ids.append(pred_i)
if pred_category != []:
if pred_category[pred_i] == gt_category[gt_i] or (pred_category[pred_i]==20 and gt_category[gt_i]==21):
c_lane += 1 # category matched num
if pred_category[pred_i] == gt_category[gt_i] or (
pred_category[pred_i] == 20 and gt_category[gt_i] == 21):
c_lane += 1 # category matched num
return r_lane, p_lane, c_lane, cnt_gt, cnt_pred, match_num
def bench_one_submit(self, gts, preds):
......@@ -329,7 +338,7 @@ class LaneEval(object):
# N to N matching of lanelines
r_lane, p_lane, c_lane, cnt_gt, cnt_pred, match_num = self.bench(pred_lanes,
pred_category,
pred_category,
gt_lanes,
gt_category,
)
......@@ -344,18 +353,18 @@ class LaneEval(object):
laneline_z_error_close = np.array(laneline_z_error_close)
laneline_z_error_far = np.array(laneline_z_error_far)
if np.sum(laneline_stats[:, 3])!= 0:
if np.sum(laneline_stats[:, 3]) != 0:
R_lane = np.sum(laneline_stats[:, 0]) / (np.sum(laneline_stats[:, 3]))
else:
R_lane = np.sum(laneline_stats[:, 0]) / (np.sum(laneline_stats[:, 3]) + 1e-6) # recall = TP / (TP+FN)
R_lane = np.sum(laneline_stats[:, 0]) / (np.sum(laneline_stats[:, 3]) + 1e-6) # recall = TP / (TP+FN)
if np.sum(laneline_stats[:, 4]) != 0:
P_lane = np.sum(laneline_stats[:, 1]) / (np.sum(laneline_stats[:, 4]))
else:
P_lane = np.sum(laneline_stats[:, 1]) / (np.sum(laneline_stats[:, 4]) + 1e-6) # precision = TP / (TP+FP)
P_lane = np.sum(laneline_stats[:, 1]) / (np.sum(laneline_stats[:, 4]) + 1e-6) # precision = TP / (TP+FP)
if np.sum(laneline_stats[:, 5]) != 0:
C_lane = np.sum(laneline_stats[:, 2]) / (np.sum(laneline_stats[:, 5]))
else:
C_lane = np.sum(laneline_stats[:, 2]) / (np.sum(laneline_stats[:, 5]) + 1e-6) # category_accuracy
C_lane = np.sum(laneline_stats[:, 2]) / (np.sum(laneline_stats[:, 5]) + 1e-6) # category_accuracy
if R_lane + P_lane != 0:
F_lane = 2 * R_lane * P_lane / (R_lane + P_lane)
else:
......@@ -365,4 +374,5 @@ class LaneEval(object):
return output_stats[0]
f1 = LaneEval()
# ==============================================================================
# Binaries and/or source for the following packages or projects
# Binaries and/or source for the following packages or projects
# are presented under one or more of the following open source licenses:
# io.py The OpenLane-V2 Dataset Authors Apache License, Version 2.0
#
......@@ -20,10 +20,11 @@
# limitations under the License.
# ==============================================================================
import os
import cv2
import json
import os
import pickle
import cv2
import numpy as np
......@@ -31,12 +32,13 @@ class IO:
r"""
Wrapping io in openlanev2,
can be modified for different file systems.
"""
def __init__(self) -> None:
pass
def os_listdir(self, path : str) -> list:
def os_listdir(self, path: str) -> list:
r"""
Parameters
----------
......@@ -47,9 +49,9 @@ class IO:
list
"""
return os.listdir(path)
return os.listdir(path)
def cv2_imread(self, path : str) -> np.ndarray:
def cv2_imread(self, path: str) -> np.ndarray:
r"""
Parameters
----------
......@@ -60,9 +62,9 @@ class IO:
np.ndarray
"""
return cv2.imread(path)
return cv2.imread(path)
def json_load(self, path : str) -> dict:
def json_load(self, path: str) -> dict:
r"""
Parameters
----------
......@@ -77,7 +79,7 @@ class IO:
result = json.load(f)
return result
def pickle_dump(self, path : str, obj : object) -> None:
def pickle_dump(self, path: str, obj: object) -> None:
r"""
Parameters
----------
......@@ -88,7 +90,7 @@ class IO:
with open(path, 'wb') as f:
pickle.dump(obj, f)
def pickle_load(self, path : str) -> object:
def pickle_load(self, path: str) -> object:
r"""
Parameters
----------
......@@ -103,4 +105,5 @@ class IO:
result = pickle.load(f)
return result
io = IO()
from .collect import collect
from .check import check_results
\ No newline at end of file
# ==============================================================================
# Binaries and/or source for the following packages or projects
# Binaries and/or source for the following packages or projects
# are presented under one or more of the following open source licenses:
# check.py The OpenLane-V2 Dataset Authors Apache License, Version 2.0
#
......@@ -20,12 +20,13 @@
# limitations under the License.
# ==============================================================================
from functools import reduce
import numpy as np
from iso3166 import countries
from functools import reduce
def check_results(results : dict) -> None:
def check_results(results: dict) -> None:
r"""
Check format of results.
......@@ -93,35 +94,42 @@ def check_results(results : dict) -> None:
points = instance['points']
if not isinstance(points, np.ndarray):
raise Exception(f'Type of value in key [results/{token}/predictions/{key}/{instance["id"]}] should be np.ndarray')
raise Exception(
f'Type of value in key [results/{token}/predictions/{key}/{instance["id"]}] should be np.ndarray')
points = np.array(points)
if key == 'lane_centerline' and not (points.ndim == 2 and points.shape[1] == 3):
raise Exception(f'Shape of points in instance [results/{token}/predictions/{key}/{instance["id"]}] should be (#points, 3) but not {points.shape}')
raise Exception(
f'Shape of points in instance [results/{token}/predictions/{key}/{instance["id"]}] should be (#points, 3) but not {points.shape}')
if key == 'traffic_element' and not (points.ndim == 2 and points.shape == (2, 2)):
raise Exception(f'Shape of points in instance [results/{token}/predictions/{key}/{instance["id"]}] should be (2, 2) but not {points.shape}')
raise Exception(
f'Shape of points in instance [results/{token}/predictions/{key}/{instance["id"]}] should be (2, 2) but not {points.shape}')
ids[key] = [instance['id'] for instance in predictions[key]]
ids_check = reduce(lambda x, y: x + y, ids.values(), [])
if len(set(ids_check)) != len(ids_check):
raise Exception(f'IDs are not unique in [results/{token}/predictions]')
if 'topology_lclc' not in predictions:
raise Exception(f'Miss key [results/{token}/predictions/topology_lclc].')
topology_lclc = predictions['topology_lclc']
if not isinstance(topology_lclc, np.ndarray):
raise Exception(f'Type of value in key [results/{token}/predictions/topology_lclc] should be np.ndarray')
topology_lclc = np.array(topology_lclc)
if not (topology_lclc.ndim == 2 and topology_lclc.shape[0] == len(ids['lane_centerline']) and topology_lclc.shape[1] == len(ids['lane_centerline'])):
raise Exception(f'Shape of adjacent matrix of [results/{token}/predictions/topology_lclc] should be (#lane_centerline, #lane_centerline) but not {topology_lclc.shape}')
if not (topology_lclc.ndim == 2 and topology_lclc.shape[0] == len(ids['lane_centerline']) and
topology_lclc.shape[1] == len(ids['lane_centerline'])):
raise Exception(
f'Shape of adjacent matrix of [results/{token}/predictions/topology_lclc] should be (#lane_centerline, #lane_centerline) but not {topology_lclc.shape}')
if 'topology_lcte' not in predictions:
raise Exception(f'Miss key [results/{token}/predictions/topology_lcte].')
topology_lcte = predictions['topology_lcte']
if not isinstance(topology_lcte, np.ndarray):
raise Exception(f'Type of value in key [results/{token}/predictions/topology_lcte] should be np.ndarray')
topology_lcte = np.array(topology_lcte)
if not (topology_lcte.ndim == 2 and topology_lcte.shape[0] == len(ids['lane_centerline']) and topology_lcte.shape[1] == len(ids['traffic_element'])):
raise Exception(f'Shape of adjacent matrix of [results/{token}/predictions/topology_lcte] should be (#lane_centerline, #traffic_element) but not {topology_lcte.shape}')
if not (topology_lcte.ndim == 2 and topology_lcte.shape[0] == len(ids['lane_centerline']) and
topology_lcte.shape[1] == len(ids['traffic_element'])):
raise Exception(
f'Shape of adjacent matrix of [results/{token}/predictions/topology_lcte] should be (#lane_centerline, #traffic_element) but not {topology_lcte.shape}')
return valid
# ==============================================================================
# Binaries and/or source for the following packages or projects
# Binaries and/or source for the following packages or projects
# are presented under one or more of the following open source licenses:
# collect.py The OpenLane-V2 Dataset Authors Apache License, Version 2.0
#
......@@ -26,7 +26,7 @@ from tqdm import tqdm
from ..io import io
def collect(root_path : str, data_dict : dict, collection : str, point_interval : int = 1) -> None:
def collect(root_path: str, data_dict: dict, collection: str, point_interval: int = 1) -> None:
r"""
Load meta data of data in data_dict,
and store in a .pkl with split as file name.
......@@ -43,14 +43,15 @@ def collect(root_path : str, data_dict : dict, collection : str, point_interval
not subsampling as default.
"""
data_list = [(split, segment_id, timestamp.split('.')[0]) \
data_list = [
(split, segment_id, timestamp.split('.')[0]) \
for split, segment_ids in data_dict.items() \
for segment_id, timestamps in segment_ids.items() \
for timestamp in timestamps
for segment_id, timestamps in segment_ids.items() \
for timestamp in timestamps
]
meta = {
(split, segment_id, timestamp): io.json_load(f'{root_path}/{split}/{segment_id}/info/{timestamp}.json') \
for split, segment_id, timestamp in tqdm(data_list, desc=f'collecting {collection}', ncols=80)
for split, segment_id, timestamp in tqdm(data_list, desc=f'collecting {collection}', ncols=80)
}
for identifier, frame in meta.items():
......@@ -64,10 +65,14 @@ def collect(root_path : str, data_dict : dict, collection : str, point_interval
if 'annotation' not in frame:
continue
for i, lane_centerline in enumerate(frame['annotation']['lane_centerline']):
meta[identifier]['annotation']['lane_centerline'][i]['points'] = np.array(lane_centerline['points'][::point_interval], dtype=np.float32)
meta[identifier]['annotation']['lane_centerline'][i]['points'] = np.array(
lane_centerline['points'][::point_interval], dtype=np.float32)
for i, traffic_element in enumerate(frame['annotation']['traffic_element']):
meta[identifier]['annotation']['traffic_element'][i]['points'] = np.array(traffic_element['points'], dtype=np.float32)
meta[identifier]['annotation']['topology_lclc'] = np.array(meta[identifier]['annotation']['topology_lclc'], dtype=np.int8)
meta[identifier]['annotation']['topology_lcte'] = np.array(meta[identifier]['annotation']['topology_lcte'], dtype=np.int8)
meta[identifier]['annotation']['traffic_element'][i]['points'] = np.array(traffic_element['points'],
dtype=np.float32)
meta[identifier]['annotation']['topology_lclc'] = np.array(meta[identifier]['annotation']['topology_lclc'],
dtype=np.int8)
meta[identifier]['annotation']['topology_lcte'] = np.array(meta[identifier]['annotation']['topology_lcte'],
dtype=np.int8)
io.pickle_dump(f'{root_path}/{collection}.pkl', meta)
# ==============================================================================
# Binaries and/or source for the following packages or projects
# Binaries and/or source for the following packages or projects
# are presented under one or more of the following open source licenses:
# utils.py The OpenLane-V2 Dataset Authors Apache License, Version 2.0
#
......@@ -21,19 +21,19 @@
# ==============================================================================
TRAFFIC_ELEMENT_ATTRIBUTE = {
'unknown': 0,
'red': 1,
'green': 2,
'yellow': 3,
'go_straight': 4,
'turn_left': 5,
'turn_right': 6,
'no_left_turn': 7,
'no_right_turn': 8,
'u_turn': 9,
'no_u_turn': 10,
'slight_left': 11,
'slight_right': 12,
'unknown': 0,
'red': 1,
'green': 2,
'yellow': 3,
'go_straight': 4,
'turn_left': 5,
'turn_right': 6,
'no_left_turn': 7,
'no_right_turn': 8,
'u_turn': 9,
'no_u_turn': 10,
'slight_left': 11,
'slight_right': 12,
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment