Ver como Markdown

Detección orientada

La detección orientada de objetos localiza cada instancia con un rectángulo rotado en vez de uno alineado a los ejes, así que un objeto inclinado queda bien ceñido en lugar de encerrado en una caja llena de fondo. La clave de la tarea es obb.

Definición

La detección orientada añade un número a una detección: el ángulo. Cada instancia recibe un rectángulo rotado, una clase y una puntuación. La ganancia es el ajuste. Un barco a 45 grados, el tejado de una nave industrial, una fila de camiones aparcados: una caja alineada a los ejes alrededor de cualquiera de ellos es fondo en su mayor parte, y dos cajas vecinas se solapan incluso cuando los objetos no lo hacen. Por eso la tarea es estándar en imágenes aéreas y en análisis de layout de documentos, y por eso el dataset de referencia es DOTA.

obb es la clave canónica de la tarea, y el sufijo -obb del nombre de archivo de un checkpoint la selecciona, así que task= no hace falta al cargar pesos publicados.

predict() rellena result.obb. .xywhr es la forma canónica (N, 5): centro x, centro y, ancho, alto y un ángulo en radianes que da la rotación del lado del ancho alrededor del centro. .conf y .cls llevan la puntuación y el índice de clase dentro de result.names, y .id un id de track cuando hay seguimiento. .xyxyxyxy convierte cada fila en sus cuatro puntos de esquina como (N, 4, 2) píxeles, .xyxyxyxyn normaliza esas esquinas y .xyxy da la caja alineada a los ejes que la contiene, que es la que hay que usar cuando el código posterior solo entiende rectángulos. result.boxes también se rellena, con la forma alineada a los ejes.

Modelos

Dos familias cubren esta tarea, y a cuál recurrir depende de si necesitas entrenar.

RF-DETR es la que entrena. Predice, entrena, valida y exporta cajas orientadas, y publica checkpoints orientados en cuatro tamaños, n, s, m y l. Necesita su propio extra, pip install "libreyolo[rfdetr]", y su página de modelo lleva la licencia de los pesos y su procedencia.

Lee la sección de más abajo sobre qué predicen realmente esos checkpoints antes de planificar nada con ellos.

RT-DETRv2 es la que tiene pesos aéreos. Publica desde LibreRTDETRv2n-obb.pt hasta LibreRTDETRv2x-obb.pt, los checkpoints oficiales de DOTA v1.0 a una sola escala convertidos al formato de LibreYOLO, que cubren las 15 clases de DOTA a 1024 px. No necesita ningún extra más allá del paquete base, el grafo orientado se reconoce a partir de los propios tensores del checkpoint, y la predicción, la validación y la exportación a ONNX y TorchScript están todas soportadas. El entrenamiento no: la tarea orientada es solo de inferencia en esa familia, train() lanza un error, y no hay transferencia desde sus pesos de detección, que usan un backbone distinto. El seguimiento y el aumento de datos en tiempo de test tampoco están disponibles para cajas orientadas.

Así que: categorías de DOTA listas para usar, RT-DETRv2. Tus propias etiquetas orientadas, RF-DETR.

Predicción

Los pesos se descargan de Hugging Face en el primer uso y se guardan en caché en local.

Python
# Necesita el extra rfdetr: pip install "libreyolo[rfdetr]"from libreyolo import LibreYOLO, SAMPLE_IMAGE # El sufijo -obb del nombre de archivo selecciona la tarea, así que no# hace falta ningún argumento de tarea.model = LibreYOLO("LibreRFDETRs-obb.pt")result = model(SAMPLE_IMAGE, save=True) obb = result.obbprint(obb.xywhr)   # (N, 5): centro x, centro y, ancho, alto, radianesprint(obb.conf, obb.cls)
CLI
libreyolo predict model=LibreRFDETRs-obb.pt save=True \  source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpg
Esquinas en vez de ángulos
from libreyolo import LibreYOLO, SAMPLE_IMAGE result = LibreYOLO("LibreRFDETRs-obb.pt")(SAMPLE_IMAGE)obb = result.obb print(obb.xyxyxyxy.shape)    # (N, 4, 2) puntos de esquina en píxelesprint(obb.xyxyxyxyn.shape)   # los mismos, normalizadosprint(obb.xyxy.shape)        # (N, 4) caja alineada a los ejes que la contiene
Un checkpoint más pequeño
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreRFDETRn-obb.pt")result = model(SAMPLE_IMAGE) print(result.obb.xywhr.shape)
RT-DETRv2
from libreyolo import LibreYOLO # Pesos de DOTA v1.0, 15 clases aéreas a 1024 px. El grafo orientado# se reconoce a partir de los propios tensores del checkpoint, así que# no hace falta argumento de tarea.model = LibreYOLO("LibreRTDETRv2n-obb.pt")result = model("aerial.png", save=True) obb = result.obbprint(obb.xywhr)print(result.names)   # plane, ship, harbor, helicopter y 11 más

Ten claro qué son los checkpoints publicados de RF-DETR antes de ejecutarlos. A pesar de que DOTA es el benchmark de referencia de esta tarea, esos pesos no se entrenaron con él. Los cuatro se inicializaron desde los pesos de detección de RF-DETR y se les hizo fine-tuning sobre un único dataset de Roboflow Universe de grabaciones de dron, con seis clases de vehículos: bike, bus, car, other_vehicle, taxi y truck. Sus model cards los describen como pesos de desarrollo, producidos mientras se validaba el soporte de entrenamiento orientado, y dicen que no deben interpretarse como pesos de producción ni oficiales de benchmark.

En la práctica eso significa que son un punto de partida utilizable para cajas orientadas sobre vehículos vistos desde arriba, y para verificar que tu pipeline funciona de principio a fin. Cualquier otro dominio implica entrenar con tus propias etiquetas orientadas, y para las categorías aéreas por las que DOTA es conocido, los checkpoints de RT-DETRv2 son los que están realmente entrenados con esos datos. conf y max_det dan forma a la salida igual que en detección. Consulta predicción para fuentes, streaming y manejo de resultados.

Formato del dataset

La estructura es la de detección: un archivo de etiquetas .txt por imagen, que se localiza sustituyendo images por labels en la ruta de la imagen y cambiando la extensión.

dataset/
  data.yaml
  images/
    train/P0001.png
    val/P0101.png
  labels/
    train/P0001.txt
    val/P0101.txt

Una fila son exactamente nueve campos, un índice de clase seguido de cuatro puntos de esquina en orden:

<class_id> <x1> <y1> <x2> <y2> <x3> <y3> <x4> <y4>

Los cuatro puntos son floats normalizados en [0, 1] y tienen que formar un rectángulo orientado no degenerado. En el archivo de etiquetas no se guarda ningún ángulo: el loader deriva el xywhr canónico a partir de las esquinas. El parser es estricto por defecto y rechaza coordenadas fuera de rango, mientras que la ingesta del dataset y de la validación puede recortar primero a [0, 1] las etiquetas por lo demás válidas que caen en el borde de un crop, y aun así rechazar después las cajas degeneradas.

El parseo de filas depende de la tarea. Nueve campos significan una caja orientada solo en modo obb; en modo segment esa misma fila se lee como un polígono de cuatro puntos.

El YAML es el YAML de detección:

yaml
path: dataset
train: images/train
val: images/val
names:
  0: plane
  1: ship

El COCO JSON nativo también se carga, con un mapeo annotations de nombre de split a archivo JSON. Las anotaciones se leen por orden de prioridad: un campo obb con ocho esquinas en espacio de píxeles, un campo obb con [cx, cy, w, h, angle] y el ángulo en radianes, un polígono o RLE de segmentation reajustado a su rectángulo de área mínima, o un bbox de COCO normal, que se trata como un rectángulo alineado a los ejes y se convierte a la forma canónica xywhr.

El parser canónico de filas es libreyolo.data.parse_yolo_obb_label_line.

Entrenamiento

Python
from libreyolo import LibreYOLO # Continúa desde pesos orientados publicados. data debe apuntar a un# dataset cuyas filas de etiquetas lleven cuatro esquinas.model = LibreYOLO("LibreRFDETRs-obb.pt")model.train(data="my-obb-dataset.yaml", epochs=50, imgsz=512, batch=8, lr0=1e-4)
CLI
libreyolo train model=LibreRFDETRs-obb.pt data=my-obb-dataset.yaml \  epochs=50 imgsz=512 batch=8 lr0=1e-4
Desde pesos de detección
# Los pesos de detección no predicen ningún ángulo, así que esto es una# transferencia explícita. Pedir task=obb es lo que la autoriza.libreyolo train model=LibreRFDETRs.pt data=my-obb-dataset.yaml \  task=obb epochs=50 imgsz=512

Entrenar en esta tarea significa RF-DETR. El entrenamiento continúa por defecto desde un checkpoint -obb publicado. Partir de pesos de detección es una transferencia deliberada: esos pesos no predicen ningún ángulo, y pasar task=obb es lo que autoriza el cambio. Mantén lr0 en 1e-4 o por debajo, igual que en las demás tareas de la familia. Los checkpoints orientados de RT-DETRv2 no admiten fine-tuning; úsalos tal cual, o entrena un modelo RF-DETR con tus propias etiquetas. Consulta entrenamiento para datasets, aumento de datos, multi-GPU y loggers.

Validación

val() devuelve un diccionario normal de claves metrics/. El emparejamiento usa IoU rotado, calculado entre rectángulos orientados y no entre las cajas alineadas a los ejes que los contienen, así que una predicción con la posición correcta y el ángulo equivocado cuenta como fallo.

Python
from libreyolo import LibreYOLO model = LibreYOLO("LibreRFDETRs-obb.pt") # val() devuelve un dict normal, no un objeto.metrics = model.val(data="my-obb-dataset.yaml") print(metrics["metrics/mAP50-95"])print(metrics["metrics/mAP50"], metrics["metrics/mAP75"])print(metrics["metrics/precision"], metrics["metrics/recall"])
CLI
libreyolo val model=LibreRFDETRs-obb.pt data=my-obb-dataset.yaml
RT-DETRv2
libreyolo val model=LibreRTDETRv2n-obb.pt data=my-obb-dataset.yaml

metrics/mAP50-95 es la precisión media promediada sobre umbrales de IoU de 0.50 a 0.95 en pasos de 0.05, y es la cifra principal. A diferencia de la ruta COCO que usa la detección, esta tarea respeta iou_thresholds en la configuración de validación, así que el barrido se puede cambiar. metrics/mAP50 y metrics/mAP75 son las versiones de un solo umbral. metrics/precision y metrics/recall son precisión y recall reales con IoU 0.50, leídos en el punto de operación más laxo: se cuenta cada predicción que sobrevive al umbral de confianza, y ese umbral vale 0.001 por defecto durante la validación. Por tanto, subir conf las mueve, mientras que las cifras de mAP, que usan la curva de precisión-recall entera, se quedan donde están. Cuatro de estas se repiten con un sufijo (OBB), metrics/mAP50-95(OBB), metrics/mAP50(OBB), metrics/precision(OBB) y metrics/recall(OBB), que es como el código que llama distingue un resultado orientado de uno alineado a los ejes cuando ambos están en la misma tabla. metrics/mAP75 no tiene equivalente con sufijo.

Dos opciones no hacen nada en esta tarea. save_json y save_plots se aceptan y registran un aviso: los volcados de predicciones orientadas y las gráficas de validación no están implementados.

Exportación

Python
from libreyolo import LibreYOLO model = LibreYOLO("LibreRFDETRs-obb.pt")model.export(format="onnx", imgsz=512)
CLI
libreyolo export model=LibreRFDETRs-obb.pt format=onnx imgsz=512
RT-DETRv2
# ONNX y TorchScript son los destinos validados aquí, a FP32,# batch 1 y con un lienzo fijo de 1024 por 1024.libreyolo export model=LibreRTDETRv2n-obb.pt format=onnx imgsz=1024libreyolo export model=LibreRTDETRv2n-obb.pt format=torchscript imgsz=1024
Usar el archivo exportado
from libreyolo import LibreYOLO, SAMPLE_IMAGE # La factoría enruta según el sufijo del archivo, así que un artefacto# exportado se carga como un checkpoint y devuelve el mismo objeto Results.model = LibreYOLO("LibreRFDETRs-obb.onnx")result = model(SAMPLE_IMAGE) print(result.obb.xywhr)

Un artefacto exportado se vuelve a cargar con LibreYOLO() según el sufijo de su archivo, así que un archivo .onnx o .engine se comporta como un checkpoint y devuelve el mismo Results. La cobertura de formatos varía por tarea dentro de una misma familia, y la matriz de la página del modelo se genera a partir del conjunto validado y da el motivo por el que un destino no está disponible. Consulta exportación y despliegue para los formatos, sus extras y sus restricciones.

Verificado con LibreYOLO v1.5.0.