Skip to content

Commit 811d806

Browse files
authored
Release/v1.1.0 (#19)
* add signals model * remove old code & update precommit * update versions & changelog * rename model file * checks on gps and video durations * fix signal draw in output video * fix signal draw in output video * remove python 3.9 support * update docs * update manual
1 parent dadc289 commit 811d806

20 files changed

+124
-481
lines changed

.pre-commit-config.yaml

+3-11
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@ default_language_version:
33

44
repos:
55
- repo: https://github.com/pycqa/isort
6-
rev: 5.13.2
6+
rev: 6.0.0
77
hooks:
88
- id: isort
99
args: ["--profile", "black", "--filter-files"]
1010

1111
- repo: https://github.com/psf/black
12-
rev: 24.10.0
12+
rev: 25.1.0
1313
hooks:
1414
- id: black
1515
args: ["--config", ".code_quality/pyproject_black.toml"]
1616

1717
- repo: https://github.com/PyCQA/bandit
18-
rev: 1.7.10
18+
rev: 1.8.2
1919
hooks:
2020
- id: bandit
2121
args:
@@ -35,11 +35,3 @@ repos:
3535
hooks:
3636
- id: prettier
3737
types: [yaml]
38-
39-
- repo: https://github.com/PyCQA/docformatter
40-
rev: v1.7.5
41-
hooks:
42-
- id: docformatter
43-
additional_dependencies: [tomli]
44-
args: [--in-place, --config]
45-
exclude: ^(tests|notebooks|docs|models)/

README.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<div align="center">
22
<h1>Pavimentados</h1>
33

4-
[User Manual](docs/manual_202410.pdf)
4+
[User Manual](docs/manual.pdf)
55

66
![analytics image (flat)](https://raw.githubusercontent.com/vitr/google-analytics-beacon/master/static/badge-flat.gif)
77
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=EL-BID_pavimentados&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=EL-BID_pavimentados)
@@ -16,7 +16,7 @@ Pavimentados is a tool that allows the identification of pavement faults located
1616
This library provides an environment to use computer vision models developed to detect different elements.
1717
The detected elements are then used to generate metrics that aid the process of planning road maintenance.
1818

19-
The model files can be downloaded from this [link](https://github.com/EL-BID/pavimentados/raw/refs/heads/main/models/model_20240818.tar.gz).
19+
The model files can be downloaded from this [link](https://github.com/EL-BID/pavimentados/raw/refs/heads/release/v1.1.0/models/model.tar.gz).
2020

2121
> **Important changes**: Unlike the previous version, this new version does not include traffic sign detection. We hope to be able to
2222
> include it again in future versions.
@@ -42,7 +42,7 @@ pip install pavimentados
4242
```
4343

4444
Next,
45-
* [download the model](https://github.com/EL-BID/pavimentados/raw/refs/heads/main/models/model_20240818.tar.gz) from this link
45+
* [download the model](https://github.com/EL-BID/pavimentados/raw/refs/heads/release/v1.1.0/models/model.tar.gz) from this link
4646

4747
* Decompress it using the following command
4848
```bash
@@ -141,6 +141,7 @@ In the `results` object you will find the following:
141141
1. table_summary_sections: DataFrame with summary table by sections.
142142
2. data_resulting: DataFrame with results per frame.
143143
3. data_resulting_fails: DataFrame with results by unique faults encountered.
144+
4. signals_summary : DataFrame with results by signal.
144145

145146
To see more details about the results please refer to [this page](docs%2Fresults.md).
146147

docs/CHANGELOG.md

+11
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.1.0] - 2025-01-29
9+
### Added
10+
- Support for traffic signals detection with a new YoloV11 model.
11+
- Checks on video and gps durations.
12+
### Changed
13+
- Upgraded ultralytics to 8.3.0.
14+
- Upgraded documentation.
15+
### Removed
16+
- Onnx dependencies.
17+
- Python 3.9 support.
18+
819
## [1.0.5] - 2024-12-13
920
### Added
1021
- Add GPX format in gps sources.

docs/MODELS.md

+22
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,25 @@ The model returns the following information:
2020
- Probability with which that detection is considered
2121

2222
> **Note**: This model was retrained using the folowing base model: [RoadDamageDetection](https://github.com/oracl4/RoadDamageDetection).
23+
24+
## Detection of signals
25+
---
26+
27+
This model is based on Yolo version 11, which allows to identify objects or elements within an image.
28+
It was retrained from a base model to detect 10 types of signals:
29+
30+
1. PREVENCION
31+
2. REGLAMENTARIA
32+
3. INFORMATIVA
33+
4. CURVA
34+
5. PERMITE
35+
6. OBRA
36+
7. PROHIBICION
37+
8. ADVERTENCIA
38+
9. STOP
39+
10. CEDAPASO
40+
41+
The model returns the following information:
42+
- Class of the identified object
43+
- Location within the image
44+
- Probability with which that detection is considered

docs/manual.pdf

2.25 MB
Binary file not shown.

docs/manual_202410.pdf

-1.08 MB
Binary file not shown.

docs/results.md

+19-4
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
As a result of processing a video or images, the library returns the following datasets:
55

6-
76
## data_resulting
7+
88
Information about the individual detections made on each image / photogram.
99

1010
| Field | Type | Description | Example |
@@ -27,9 +27,9 @@ Information about the individual detections made on each image / photogram.
2727
| total_area | int | Total area of the analyzed image. | 2073600 |
2828
| fail_id_section | int | Identifier of the failure. | 0 |
2929

30-
3130
## data_resulting_fails
32-
Information about each of the failures. A failure is the aggregation of several detections that are considered part of
31+
32+
Information about each of the failures. A failure is the aggregation of several detections that are considered part of
3333
the same problem (failures have a single type and can have many detections).
3434

3535
| Field | Type | Description | Example |
@@ -47,8 +47,8 @@ the same problem (failures have a single type and can have many detections).
4747
| width | float | Average width of faults measured in pixels. | 789.3660466811236 |
4848
| boxes | int | Number of bounding boxes representing the failure. | 19 |
4949

50-
5150
## table_summary_sections
51+
5252
Information about what is detected every 100 meters.
5353

5454
| Field | Type | Description | Example Value |
@@ -65,3 +65,18 @@ Information about what is detected every 100 meters.
6565
| end_longitude | float | Ending longitude of the section. | -77.15626066666667 |
6666
| end_latitude | float | Ending latitude of the section. | -11.7830935 |
6767
| section_distance | float | Distance of the section in meters. | 106.6830640804603 |
68+
69+
## signals_summary
70+
71+
Information about signals detected in each frame.
72+
73+
| Field | Type | Description | Example |
74+
|--------------------|--------|----------------------------------------------------------|------------------------------|
75+
| fotogram | int | Frame number of the video. | 52 |
76+
| position_boxes | string | Position of the bounding box in the frame. | left |
77+
| score | float | Confidence score of the detection. | 0.40509921312332153 |
78+
| latitude | float | Latitude of the location. | 10.380599907222223 |
79+
| longitude | float | Longitude of the location. | -84.36906742202616 |
80+
| boxes | string | Bounding box coordinates in the format [x1, y1, x2, y2]. | [0.540, 0.205, 0.550, 0.219] |
81+
| signal_class_names | string | Name of the signal class. | ADVERTENCIA |
82+
| ID | int | Unique identifier of the detection. | 0 |

models/model.tar.gz

54.1 MB
Binary file not shown.

models/model_20240818.tar.gz

-19.8 MB
Binary file not shown.

notebooks/models_config.json

+5
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,10 @@
33
"yolo_threshold": 0.20,
44
"yolo_iou": 0.45,
55
"yolo_max_detections": 100
6+
},
7+
"signal_model": {
8+
"yolo_threshold": 0.40,
9+
"yolo_iou": 0.45,
10+
"yolo_max_detections": 100
611
}
712
}

pavimentados/analyzers/calculators.py

+9-50
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
stack_columns_dataset,
1414
)
1515

16-
first_aggregation_dict = {"ind": "count"} # , "perc_area": "sum"
16+
first_aggregation_dict = {"ind": "count"}
1717

1818
second_aggregation_dict = {
1919
"distances": "sum",
@@ -162,31 +162,8 @@ def generate_final_results_signal(
162162
gps_obj,
163163
classes_names_yolo_signal=None,
164164
):
165-
# Reemplazo de clases
166-
# signals_class_mapping = config["signals_class_mapping"]
167-
168-
signal_base_predictions = results_obj["signal_base_predictions"]
169-
final_signal_classes = results_obj["final_signal_classes"]
170-
state_predictions = results_obj["state_predictions"]
171-
172-
# signal_base_predictions = [
173-
# [signals_class_mapping[item] if item in signals_class_mapping.keys() else item for item in sublist]
174-
# for sublist in results_obj["signal_base_predictions"]
175-
# ]
176-
# final_signal_classes = [
177-
# [signals_class_mapping[item] if item in signals_class_mapping.keys() else item for item in sublist]
178-
# for sublist in results_obj["final_signal_classes"]
179-
# ]
180-
# state_predictions = [
181-
# [signals_class_mapping[item] if item in signals_class_mapping.keys() else item for item in sublist]
182-
# for sublist in results_obj["state_predictions"]
183-
# ]
184-
185165
BOXES_SIGNAL = results_obj["boxes_signal"]
186166
CLASSES_SIGNAL = results_obj["classes_signal"]
187-
SIGNAL_CLASSES_siames = final_signal_classes
188-
SIGNAL_CLASSES_BASE = signal_base_predictions
189-
SIGNAL_STATE = state_predictions
190167
SCORES_SIGNAL = results_obj["scores_signal"]
191168
final_latitude = gps_obj.gps_df.latitude.values
192169
final_longitude = gps_obj.gps_df.longitude.values
@@ -195,11 +172,11 @@ def generate_final_results_signal(
195172
# Nos dice el cuadrante.
196173
def position(center):
197174
if center < 0.33:
198-
return 0
175+
return "left"
199176
elif center < 0.66:
200-
return 1
177+
return "center"
201178
else:
202-
return 2
179+
return "right"
203180

204181
position_boxes = [[position((box[0] + box[2]) / 2) for box in BOXES_SIGNAL[f]] for f in range(len(BOXES_SIGNAL))]
205182

@@ -210,9 +187,6 @@ def position(center):
210187
f,
211188
position_boxes[f][i],
212189
SCORES_SIGNAL[f][i],
213-
SIGNAL_STATE[f][i],
214-
SIGNAL_CLASSES_siames[f][i],
215-
SIGNAL_CLASSES_BASE[f][i],
216190
int(CLASSES_SIGNAL[f][i]),
217191
final_latitude[f],
218192
final_longitude[f],
@@ -226,34 +200,19 @@ def position(center):
226200
"fotogram",
227201
"position_boxes",
228202
"score",
229-
"signal_state",
230-
"signal_class_siames",
231-
"signal_class_base",
232203
"signal_class",
233204
"latitude",
234205
"longitude",
235206
"boxes",
236207
],
237208
)
238209

239-
df["signal_class_siames_names"] = df["signal_class_siames"].values
240210
df["signal_class_names"] = df["signal_class"].apply(lambda x: classes_names_yolo_signal[x])
241-
242-
yolo_classes_to_keep = []
243-
244-
x = tuple(zip(df["signal_class_siames_names"].values, df["signal_class_names"].values))
245-
246-
final_classes = []
247-
for i in range(len(x)):
248-
if x[i][1] in yolo_classes_to_keep:
249-
final_classes.append(x[i][1])
250-
else:
251-
final_classes.append(x[i][0])
252-
253-
df["final_classes"] = final_classes
211+
df["signal_class_names"] = df["signal_class_names"].apply(lambda x: config["signals_class_mapping"][x])
212+
df.drop(columns=["signal_class"], inplace=True)
254213
df["ID"] = range(len(df))
255214

256-
df = df.sort_values(["final_classes", "position_boxes", "fotogram"]).reset_index(drop=True)
215+
df = df.sort_values(["signal_class_names", "position_boxes", "fotogram"]).reset_index(drop=True)
257216

258217
# cantidad de fotogramas a sacar repetidas.
259218
N_fotogram = 5
@@ -262,7 +221,7 @@ def position(center):
262221

263222
for i in range(len(df) - 1, 0, -1):
264223
if (
265-
(df.loc[i - 1, "final_classes"] == df.loc[i, "final_classes"])
224+
(df.loc[i - 1, "signal_class_names"] == df.loc[i, "signal_class_names"])
266225
& (df.loc[i - 1, "position_boxes"] == df.loc[i, "position_boxes"])
267226
& (
268227
(np.abs(df.loc[i - 1, "fotogram"] - df.loc[i, "fotogram"]) <= N_fotogram)
@@ -271,7 +230,7 @@ def position(center):
271230
<= meters_dist
272231
)
273232
)
274-
& (df.loc[i, "final_classes"] != "OTRO")
233+
& (df.loc[i, "signal_class_names"] != "OTRO")
275234
):
276235
df.loc[i - 1, "ID"] = df.loc[i, "ID"]
277236

pavimentados/analyzers/gps_sources.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def __init__(self):
2323

2424
def _calculate_seconds_from_start(self):
2525
self.gps_df["seconds_from_start"] = self.gps_df.seconds.values - self.gps_df.seconds.values[0]
26+
self.gps_duration = self.gps_df["seconds_from_start"].max()
2627

2728
def adjust_gps_data(self, number_images):
2829
# Interpolación lineal por cada segundo (corrección de outliers)
@@ -230,7 +231,7 @@ def get_gpsdata_from_exif(self, filename):
230231
try:
231232
data = exif[key].decode() if isinstance(exif[key], bytes) else exif[key]
232233
exif_data[name] = data
233-
except:
234+
except: # noqa: E722
234235
logger.debug(f"Error decoding exif in key: {key}")
235236

236237
if "GPSInfo" in exif_data:
@@ -258,7 +259,7 @@ def load_single_value(self, img_path):
258259
lat = decimal_coords(d["GPSLatitude"], d["GPSLatitudeRef"])
259260
lon = decimal_coords(d["GPSLongitude"], d["GPSLongitudeRef"])
260261
time = dt.datetime.strptime(d["DateTimeOriginal"], "%Y:%m:%d %H:%M:%S")
261-
except:
262+
except: # noqa: E722
262263
raise InvalidGPSData(f"Could not load GPS data from image: {img_path}")
263264

264265
return {"timestamp": time, "longitude": lon, "latitude": lat}

pavimentados/configs/models_general.json

+2-10
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,12 @@
2626
},
2727

2828
"signal_model": {
29-
"enabled": false,
30-
"path": "signal_model/yolov8-signals-240707-2106",
29+
"enabled": true,
30+
"path": "signal_model/yolo11-signals-datasetWOotherysem-250115-2342",
3131
"model_filename": "model.pt",
3232
"classes_filename": "classes.names",
3333
"yolo_threshold": 0.10,
3434
"yolo_iou": 0.45,
3535
"yolo_max_detections": 100
36-
},
37-
38-
"siamese_model": {
39-
"enabled": false,
40-
"path": "siamese_model/siamese_128",
41-
"model_filename": "onnx_siamese_model.onnx",
42-
"embeddings_filename": "embeddings_references.pickle",
43-
"image_size": [128, 128, 3]
4436
}
4537
}

0 commit comments

Comments
 (0)