Proyecto de titulacion - Ingenieria en Ciencias de la Computacion
Universidad del Azuay
Autor: Bryan Alexander Juca Salinas
Director: Ing. Marcos Patricio Orellana Cordero
Este repositorio contiene un prototipo de vision computacional para analizar imagenes de estacionamientos y clasificar el estado de los vehiculos segun su orientacion y posicion dentro de las plazas. El sistema esta pensado para trabajar con capturas fijas de camaras del campus universitario y generar artefactos que luego se usan en una etapa de evaluacion o inferencia.
El flujo principal es un pipeline offline de entrenamiento y calibracion. A partir de un dataset de imagenes, el sistema:
- Define regiones de interes del estacionamiento.
- Limpia imagenes con vehiculos fuera de la zona valida.
- Detecta vehiculos dentro de la ROI.
- Agrupa posiciones de vehiculos por plaza mediante DBSCAN.
- Detecta y agrupa placas vehiculares.
- Genera poligonos Convex Hull para delimitar cada plaza.
- Calcula el eje principal de orientacion mediante PCA.
- Calcula umbrales robustos con MAD para clasificar orientaciones.
- Usa los artefactos generados para clasificar nuevas imagenes en el flujo de tiempo real.
- Python
- Ultralytics YOLO
- OpenCV
- NumPy
- Pandas
- Scikit-learn
- SciPy
- Shapely
- Matplotlib
- PyYAML
El desarrollo sigue una estructura compatible con CRISP-DM aterrizado en el formato SPEM en cuatro fases:
- Comprension del problema: deteccion de vehiculos mal ubicados en plazas de estacionamiento.
- Comprension de datos: analisis de capturas por camara, ROI, plazas y condiciones visuales.
- Preparacion de datos: limpieza de imagenes, filtrado por ROI y extraccion de centroides.
- Modelado: deteccion con YOLO, clustering con DBSCAN, Convex Hull, PCA y MAD.
- Evaluacion: generacion de resultados, comparacion de estados y apoyo con herramientas de anotacion.
tesis_yolo_parqueo/
|-- config/
| `-- defaults.yaml # Parametros globales del pipeline
|-- src/
| |-- pipeline_runner.py # Menu principal del pipeline
| |-- path_resolver.py # Resolucion de rutas por workspace
| |-- pipeline_config.py # Carga de configuracion YAML
| |-- 1def_roi/ # Definicion de ROI principal y negativa
| |-- 2clear_dataset/ # Limpieza de dataset por ROI negativa
| |-- 3detection/ # Deteccion de vehiculos y placas
| |-- 4clustering/ # Clustering de vehiculos y placas
| |-- 5convexhull/ # Generacion de zonas por plaza
| |-- 6pca/ # Calculo de eje PCA por plaza
| |-- 7mad/ # Calculo de umbrales MAD
| |-- test_real_time.py # Flujo de clasificacion sobre imagenes
| `-- test_real_time_auto.py
|-- workspaces/
| |-- cam1/
| |-- cam2/
| `-- ... # Espacios de trabajo por camara
|-- Evaluacion/ # Herramientas y resultados de evaluacion
|-- evaluacion_web/ # Interfaz web de apoyo para evaluacion
|-- resultados/ # Resultados consolidados
|-- analisis_pipeline_principal.txt # Documentacion tecnica del pipeline offline
|-- analisis_pipeline_test_real_time.txt
|-- requirements.txt
`-- README.md
Cada camara o experimento se organiza dentro de workspaces/<nombre_workspace>/. El runner detecta el workspace a partir de la ruta enviada en --dataset-dir.
workspaces/
`-- cam2/
|-- config/
| `-- pipeline.yaml # Overrides opcionales
|-- data/ # Imagenes del dataset
|-- rois/
| |-- roi.json # ROI principal
| `-- roi_negativa.json # ROI para limpieza
|-- results_detection/
| `-- centroids_bbox_ROI.csv
|-- clustering_cars/
| |-- 1centroids_bbox_dbscan_scaled_filtered.csv
| |-- 2clusters_without_noise.csv
| `-- 3clusters_con_plaza_id.csv
|-- clustering_plates/
| |-- plates_centroids_v1l.csv
| |-- 1plates_centroids_v1l_dbscan.csv
| `-- 2plates_centroids_v1l_dbscan_plaza.csv
|-- artifacts/
| `-- zonas_convex.pkl
|-- PCA/
| |-- pca_projection_errors_by_point.csv
| `-- pca_correct_by_plaza.png
`-- MAD/
|-- orientacion_clasificada.csv
`-- grafico_orientacion_placas.png
Se recomienda crear un entorno virtual antes de instalar las dependencias.
python -m venv venv
.\venv\Scripts\activate
pip install -r requirements.txtSi se desea regenerar el archivo de dependencias despues de instalar nuevas librerias:
pip freeze > requirements.txtLos pesos de los modelos no se incluyen en el repositorio por su tamano. Deben colocarse en la carpeta models/ en la raiz del proyecto.
Modelos usados por el codigo:
models/yolo12l.pt: deteccion de vehiculos.models/license-plate-finetune-v1l.pt: deteccion de placas vehiculares.
La configuracion se carga por jerarquia. Los valores mas especificos sobrescriben a los generales:
- Valores internos definidos en el codigo.
config/defaults.yaml, configuracion global del proyecto.workspaces/<camara>/config/pipeline.yaml, configuracion especifica por workspace.
Parametros globales actuales:
clustering:
cars:
dbscan:
scale_x: 13.8
scale_y: 155.0
eps: 1.45
min_samples: 35
outlier_std_factor: 2.0
plates:
dbscan:
eps: 75.0
min_samples: 10El punto de entrada principal es:
python src\pipeline_runner.py --dataset-dir workspaces\cam2\dataTambien se puede enviar una subcarpeta del dataset, siempre que este dentro de workspaces/<camara>/.
python src\pipeline_runner.py --dataset-dir workspaces\cam2\data\capturas_CAM2_originalEl runner abre un menu interactivo con las siguientes opciones principales:
- Ver estado del workspace.
- Definir ROI principal.
- Definir ROI negativa.
- Ejecutar limpieza con ROI negativa.
- Ejecutar clustering de vehiculos.
- Ejecutar clustering de placas.
- Generar Convex Hull.
- Calcular PCA.
- Calcular MAD.
- Ejecutar pipeline offline completo.
- Ejecutar prueba en tiempo real.
El pipeline offline genera los artefactos necesarios para que el flujo de tiempo real pueda clasificar nuevas imagenes. La secuencia tecnica es:
| Paso | Script | Salida principal |
|---|---|---|
| 1 | src/1def_roi/def_ROI.py |
rois/roi.json |
| 2 | src/1def_roi/def_negativeROI.py |
rois/roi_negativa.json |
| 3 | src/2clear_dataset/detect_outliers.py y delet_outliers.py |
data/blacklist_images.txt, data/deleted_images/ |
| 4 | src/3detection/get_centroids_ROI.py |
results_detection/centroids_bbox_ROI.csv |
| 5 | src/4clustering/cars/1detect_clusters.py |
clustering_cars/1centroids_bbox_dbscan_scaled_filtered.csv |
| 6 | src/4clustering/cars/2del_noise.py |
clustering_cars/2clusters_without_noise.csv |
| 7 | src/4clustering/cars/3order_by_plazaID.py |
clustering_cars/3clusters_con_plaza_id.csv |
| 8 | src/3detection/detect_plates.py |
clustering_plates/plates_centroids_v1l.csv |
| 9 | src/4clustering/plates/1apply_DBSCAN_plates.py |
clustering_plates/1plates_centroids_v1l_dbscan.csv |
| 10 | src/4clustering/plates/2asignar_plazaID.py |
clustering_plates/2plates_centroids_v1l_dbscan_plaza.csv |
| 11 | src/5convexhull/convexhull.py |
artifacts/zonas_convex.pkl |
| 12 | src/6pca/PCA_plateCentroids_bboxes.py |
PCA/pca_projection_errors_by_point.csv |
| 13 | src/7mad/mad.py |
MAD/orientacion_clasificada.csv |
Los centroides de los vehiculos se escalan antes de aplicar DBSCAN. Esto permite usar una metrica eliptica: mas permisiva en el eje X y mas estricta en el eje Y.
X_scaled[:, 0] = X[:, 0] / scale_x
X_scaled[:, 1] = X[:, 1] / scale_y
DBSCAN(eps=1.45, min_samples=35)
Despues se eliminan outliers internos de cada cluster usando el criterio:
distancia > media_distancias + 2 * desviacion_estandar
Cada cluster de vehiculos se ordena por su coordenada X media. El cluster mas a la izquierda recibe plaza_id=1, el siguiente plaza_id=2, y asi sucesivamente.
Para cada plaza se genera un poligono Convex Hull usando los centroides de vehiculos. Luego se aplica un buffer adaptativo:
buffer_px = 1.5 * std(distancias_al_centroide)
El resultado se guarda en artifacts/zonas_convex.pkl.
El PCA calcula el eje principal esperado para cada plaza usando centroides de vehiculos y placas. El vector se normaliza con la convencion Y >= 0, es decir, apunta hacia abajo en la imagen, donde normalmente se ubican las placas respecto al vehiculo.
El vector se almacena en zonas_convex.pkl como v_pca_plates.
El modulo MAD calcula umbrales robustos para separar las orientaciones en tres clases:
normalmal_posicionadomal_estacionado
Formulas usadas:
angulos_norm = min(theta, 180 - theta)
mediana = median(angulos_norm)
mad = median(|angulos_norm - mediana|)
sigma_r = 1.4826 * mad
T1 = mediana + 1.0 * sigma_r
T2 = mediana + 3.0 * sigma_r
Contiene las detecciones de vehiculos dentro de la ROI. Incluye nombre de imagen, centroide, bounding box, cluster y plaza asignada.
Contiene los centroides de vehiculos agrupados por plaza despues de eliminar ruido.
Contiene las placas detectadas, su cluster y su plaza_id asociado.
Diccionario serializado por plaza. Cada entrada contiene:
{
plaza_id: {
"raw_polygon": Polygon,
"polygon": Polygon,
"centroid": np.array([x, y]),
"buffer_px": float,
"v_pca_plates": np.array([vx, vy])
}
}Archivo de umbrales por plaza y tipo de punto. Es usado por el flujo de clasificacion para decidir si una orientacion es Bien Estacionado, Estacionamiento Irregular o mal estacionada.
El repositorio incluye una carpeta Evaluacion/ con scripts y archivos de apoyo para generar muestras, anotar imagenes y comparar resultados. Tambien existe evaluacion_web/, una interfaz web usada para apoyar el proceso de evaluacion manual.
Algunos resultados consolidados se encuentran en resultados/, incluyendo conteos por estado y graficos de barras.
- Los datasets completos y modelos entrenados pueden no están incluidos por el tamaño.
- Las imagenes de entrada deben estar dentro de un workspace en
workspaces/<camara>/data. - Para usar GPU, los scripts de deteccion estan configurados con
device=0. Si no se dispone de GPU compatible, puede ser necesario cambiarlo a"cpu"en los scripts correspondientes. - El archivo
analisis_pipeline_principal.txtcontiene una descripcion tecnica mas detallada del pipeline offline.