Este conjunto de datos está diseñado para el entrenamiento de modelos de detección de objetos en imágenes capturadas por drones sobre cuerpos de agua. Contiene 4,487 imágenes anotadas con cinco categorías de desechos plásticos flotantes: otros plásticos, bolsas de plástico, botellas de plástico, vasos de plástico y envoltorios de plático. Las anotaciones están disponibles en formatos Pascal VOC (XML) y YOLO (TXT), facilitando su uso con frameworks populares como Ultralytics RT-DETR.
Descripción del Conjunto de Datos
| Componente | Detalle |
|---|---|
| Nombre | Datos de detección de residuos acuáticos desde drones |
| Total de imágenes | 4,487 |
| Fuente de captura | Drones con perspectiva aérea (lagos, ríos, mares) |
| Tarea principal | Detección de objetos |
| Formatos de anotación | Pascal VOC (.xml), YOLO (.txt con coordenadas normalizadas) |
| Categorías (5 clases) | other_plastic, plastic_bag, plastic_bottle, plastic_cup, plastic_package |
Estrcutura de Archivso Recomendada
Para organizar el conjunto de datos de manera eficiente, se sugiere la siguiente jerarquía:
dataset_residuos_acuaticos/
├── imagenes/
│ ├── entrenamiento/
│ ├── validacion/
│ └── prueba/
├── anotaciones_voc/ # Archivos XML para Pascal VOC
├── etiquetas_yolo/ # Archivos TXT para YOLO
└── configuracion.yaml # Archivo de configuración para YOLOv8
Ejemplo de archivo de configuración configuracion.yaml:
ruta_entrenamiento: ./imagenes/entrenamiento
ruta_validacion: ./imagenes/validacion
ruta_prueba: ./imagenes/prueba
num_clases: 5
nombres_clases: ['otro_plastico', 'bolsa_plastico', 'botella_plastico', 'vaso_plastico', 'envoltorio_plastico']
Implementación de RT-DETR para Detección en Tiempo Real
A continuación, se detalla un flujo de trabajo completo para entrenar un modelo RT-DETR, incluyendo conversión de datos, entrenamiento y despliegue web.
Estructura del Proyecto
proyecto_deteccion_residuos/
├── datos/
│ ├── imagenes_originales/
│ ├── anotaciones_voc/
│ └── etiquetas_yolo/
├── modelos/
│ └── rtdetr_residuos.pt
├── configuraciones/
│ └── rtdetr_personalizado.yml
├── scripts/
│ ├── entrenar.py
│ ├── inferencia.py
│ └── conversion_formato.py
├── aplicacion_web/
│ ├── vistas.py
│ ├── modelos_datos.py
│ ├── urls.py
│ └── plantillas/
└── dependencias.txt
Archivo de Dependencias
torch==2.0.1
torchvision==0.15.2
ultralytics==8.2.0
opencv-python==4.8.0
numpy==1.24.3
django==4.2.7
pyecharts==2.0.0
lxml==4.9.3
Script de Conversión de VOC a YOLO
Este script transforma las anotaciones XML a formato TXT para YOLO, con lógica modificada y nombres de variables alternativos.
# scripts/conversion_formato.py
import os
from xml.etree import ElementTree as ET
from pathlib import Path
# Mapeo de clases actualizado
etiquetas_clase = ['otro_plastico', 'bolsa_plastico', 'botella_plastico', 'vaso_plastico', 'envoltorio_plastico']
mapa_clases = {etiqueta: idx for idx, etiqueta in enumerate(etiquetas_clase)}
def transformar_voc_a_yolo(origen_xml, destino_txt, carpeta_imagenes):
Path(destino_txt).mkdir(parents=True, exist_ok=True)
for archivo_xml in Path(origen_xml).glob("*.xml"):
arbol = ET.parse(archivo_xml)
raiz = arbol.getroot()
nombre_imagen = raiz.find('filename').text
ruta_imagen = os.path.join(carpeta_imagenes, nombre_imagen)
if not os.path.exists(ruta_imagen):
continue
dimensiones = raiz.find('size')
ancho_img = int(dimensiones.find('width').text)
alto_img = int(dimensiones.find('height').text)
lineas_yolo = []
for objeto in raiz.findall('object'):
nombre_clase = objeto.find('name').text
if nombre_clase not in mapa_clases:
continue
id_clase = mapa_clases[nombre_clase]
cuadro = objeto.find('bndbox')
x_min = int(cuadro.find('xmin').text)
y_min = int(cuadro.find('ymin').text)
x_max = int(cuadro.find('xmax').text)
y_max = int(cuadro.find('ymax').text)
# Cálculo de coordenadas normalizadas
centro_x = (x_min + x_max) / (2 * ancho_img)
centro_y = (y_min + y_max) / (2 * alto_img)
ancho_cuadro = (x_max - x_min) / ancho_img
alto_cuadro = (y_max - y_min) / alto_img
linea = f"{id_clase} {centro_x:.6f} {centro_y:.6f} {ancho_cuadro:.6f} {alto_cuadro:.6f}"
lineas_yolo.append(linea)
nombre_txt = archivo_xml.with_suffix('.txt').name
with open(os.path.join(destino_txt, nombre_txt), 'w') as archivo:
archivo.write('\n'.join(lineas_yolo))
if __name__ == "__main__":
transformar_voc_a_yolo(
origen_xml="datos/anotaciones_voc",
destino_txt="datos/etiquetas_yolo",
carpeta_imagenes="datos/imagenes_originales"
)
print("Proceso de conversión finalizado.")
Configuración de Entrenamiento para RT-DETR
# configuraciones/rtdetr_personalizado.yml
tarea: deteccion
modo: entrenamiento
modelo:
archivo_yaml: "rtdetr-l.yaml"
num_clases: 5
nombres_clases: ['otro_plastico', 'bolsa_plastico', 'botella_plastico', 'vaso_plastico', 'envoltorio_plastico']
datos:
ruta_base: ../datos
subconjunto_entrenamiento: imagenes_originales/entrenamiento
subconjunto_validacion: imagenes_originales/validacion
subconjunto_prueba: imagenes_originales/prueba
parametros_entrenamiento:
epocas: 100
tamano_lote: 16
tamano_imagen: 640
dispositivo: 0 # Cambiar a "cpu" si no hay GPU
trabajadores: 4
optimizador: AdamW
tasa_aprendizaje_inicial: 0.0001
paciencia: 15
guardar_modelo: true
directorio_resultados: ejecuciones/entrenamiento
nombre_experimento: residuos_rtdetr
Script de Entrenamiento
# scripts/entrenar.py
from ultralytics import RTDETR
def iniciar_entrenamiento():
modelo_base = RTDETR("rtdetr-l.pt")
resultados = modelo_base.train(
data="configuraciones/rtdetr_personalizado.yml",
epochs=100,
imgsz=640,
batch=16,
device=0,
project="ejecuciones/entrenamiento",
name="residuos_rtdetr",
exist_ok=True
)
print(f"Entrenamiento completado. Modelo óptimo en: {resultados.save_dir}/weights/best.pt")
if __name__ == "__main__":
iniciar_entrenamiento()
Script de Inferencia para Pruebas
# scripts/inferencia.py
from ultralytics import RTDETR
import cv2
def realizar_inferencia(ruta_modelo, ruta_imagen):
modelo_cargado = RTDETR(ruta_modelo)
predicciones = modelo_cargado.predict(ruta_imagen, conf=0.3, save=True)
return predicciones
if __name__ == "__main__":
realizar_inferencia("ejecuciones/entrenamiento/residuos_rtdetr/weights/best.pt", "prueba.jpg")
Sistema Web con Django
Implementación básica para visualización y registro de detecciones.
Modelo de datos en modelos_datos.py:
# aplicacion_web/modelos_datos.py
from django.db import models
class RegistroDeteccion(models.Model):
imagen_subida = models.ImageField(upload_to='entradas/')
imagen_resultado = models.ImageField(upload_to='salidas/', null=True)
objetos_detectados = models.JSONField()
marca_temporal = models.DateTimeField(auto_now_add=True)
ubicacion_monitoreo = models.CharField(max_length=200, default="Río Principal")
Vista principal en vistas.py:
# aplicacion_web/vistas.py
from django.shortcuts import render
from django.http import JsonResponse
from django.core.files.storage import FileSystemStorage
from .modelos_datos import RegistroDeteccion
from ultralytics import RTDETR
import os
modelo_deteccion = RTDETR("ejecuciones/entrenamiento/residuos_rtdetr/weights/best.pt")
traduccion_clases = ['Otro plástico', 'Bolsa de plástico', 'Botella de plástico', 'Vaso de plástico', 'Envoltorio de plástico']
def pagina_principal(request):
return render(request, 'interfaz_principal.html')
def procesar_imagen(request):
if request.method == "POST":
archivo_imagen = request.FILES['imagen']
gestor = FileSystemStorage()
nombre_guardado = gestor.save(archivo_imagen.name, archivo_imagen)
ruta_entrada = gestor.path(nombre_guardado)
ruta_salida = os.path.join(settings.MEDIA_ROOT, 'salidas', nombre_guardado)
resultados = modelo_deteccion(ruta_entrada)
imagen_anotada = resultados[0].plot()
cv2.imwrite(ruta_salida, imagen_anotada)
detecciones = []
for cuadro in resultados[0].boxes:
id_clase = int(cuadro.cls.item())
confianza = float(cuadro.conf.item())
detecciones.append({
'clase': traduccion_clases[id_clase],
'confianza': round(confianza, 2)
})
RegistroDeteccion.objects.create(
imagen_subida=archivo_imagen,
imagen_resultado=f'salidas/{nombre_guardado}',
objetos_detectados=detecciones
)
return JsonResponse({
'url_resultado': gestor.url(f'salidas/{nombre_guardado}'),
'lista_detecciones': detecciones
})
return JsonResponse({'error': 'Solicitud no válida'})