Ultralytics YOLO27: Iscriviti alla lista d'attesa.
Get Started
Lezioneintermedio

Riquadri di delimitazione orientati per oggetti ruotati

Etichetta OBB con quattro angoli, addestra yolo26n-obb.pt su DOTA8 e converti le previsioni xywhr in un asse di orientamento stabile senza incappare nelle convenzioni sugli angoli.

Navi in porto, pacchi su un nastro trasportatore e auto in un parcheggio viste dall'alto condividono lo stesso problema: i riquadri allineati agli assi attorno a oggetti ruotati e ravvicinati si sovrappongono e includono troppo sfondo, quindi NMS elimina oggetti reali e il conteggio risulta inferiore al reale. I riquadri di delimitazione orientati racchiudono ogni oggetto in un rettangolo ruotato, risolvendo i problemi di conteggio nelle scene dense e fornendo gratuitamente un angolo. Le convenzioni associate all'angolo traggono in inganno quasi tutte le prime integrazioni: nell'immagine del porto di questa lezione, la prima nave indica una rotazione di −15°, ma lo scafo è orientato a 75°.

Esito

Addestra yolo26n-obb.pt su dota8.yaml, conta le rilevazioni per classe da result.obb e converti ogni xywhr previsto in un asse del lato lungo compreso tra [0, 180) gradi.

Percorso rapido
Se sai già come muoverti, ecco la versione breve.
  1. Riga dell'etichetta: classe, seguita dai quattro angoli x1 y1 … x4 y4, normalizzati e disposti in ordine lungo il perimetro.

  2. yolo obb train data=dota8.yaml model=yolo26n-obb.pt epochs=1 imgsz=320.

  3. Leggi result.obb.xywhr (rotazione in radianti) o .xyxyxyxy (angoli); i risultati OBB non valorizzano result.boxes.

  4. Normalizza l'orientamento rispetto al lato lungo: aggiungi 90° quando w < h, quindi calcola l'angolo modulo 180°.

Pratico

Quando la rotazione è importante#

Riquadri di delimitazione orientati di Ultralytics YOLO aderenti a tre yacht ormeggiati in una foto aerea, ciascuno etichettato Ship con il relativo livello di confidenza

Stima della posa e OBB spiega quando scegliere OBB invece del rilevamento. La verifica pratica: se i riquadri di delimitazione allineati agli assi attorno a oggetti vicini si sovrappongono più degli oggetti stessi, NMS sopprime le rilevazioni reali e il conteggio risulta inferiore al reale. Le immagini aeree sono il caso classico, ed è per questo che il modello preaddestrato yolo26n-obb.pt viene addestrato su DOTA a imgsz=1024.

Riquadro allineato agli assiRiquadro orientato
Adattato a una nave a 45°circa metà sfondoaderente
File dense di oggetti ruotatii riquadri si sovrappongono; NMS elimina oggetti realii riquadri sono ben separati
Fornisce un angolonosì, come asse (modulo 180°)
Campo dei risultatiresult.boxesresult.obb

Il formato xyxyxyxy delle etichette#

Una riga di etichetta OBB contiene l'indice della classe seguito dai quattro angoli, normalizzati e disposti in ordine lungo il perimetro. Una riga reale di DOTA8 (la classe 1 è ship):

1 0.813477 0.523437 0.814453 0.504882 0.87207 0.504882 0.87207 0.524414

Leggila come quattro punti: (0.813, 0.523) → (0.814, 0.505) → (0.872, 0.505) → (0.872, 0.524). Questa nave è quasi orizzontale: è larga circa 0.059 e alta 0.019 rispetto all'immagine. Internamente, Ultralytics converte ogni insieme di angoli in centro, larghezza, altezza e rotazione (xywhr) adattando il rettangolo di area minima, così è possibile addestrare anche su quadrilateri leggermente irregolari provenienti da uno strumento di annotazione. La guida ai dataset OBB descrive il formato, mentre convert_dota_to_yolo_obb in ultralytics.data.converter converte le annotazioni originali di DOTA.

Editor di annotazione di Ultralytics Platform su un'immagine aerea di DOTA8, con riquadri ruotati attorno a campi da tennis, piccoli veicoli e una piscina

Addestra su DOTA8#

DOTA8 è un piccolo dataset con bounding box orientate, creato per testare le pipeline:

Mosaico di addestramento DOTA8 con riquadri aerei: file di barche ormeggiate, campi da tennis, un parcheggio e aerei su una pista, ciascun oggetto con un riquadro orientato e un indice di classe

from ultralytics import YOLO

model = YOLO("yolo26n-obb.pt")
model.train(data="dota8.yaml", epochs=1, imgsz=320, device="cpu")
metrics = model.val()
print(f"OBB mAP50: {metrics.box.map50:.3f}  mAP50-95: {metrics.box.map:.3f}")
OBB mAP50: 0.995  mAP50-95: 0.832

Le metriche OBB si trovano in metrics.box, ma la IoU alla loro base è calcolata tra rettangoli ruotati.

DOTA8 è un test rapido

Il punteggio è alto perché DOTA8 è una porzione dei dati di preaddestramento. Dimostra che la pipeline viene eseguita, ma non dice nulla sulle tue immagini.

Leggi le predizioni e normalizza l'angolo#

import math
from collections import Counter

from ultralytics import YOLO

def long_side_axis(w, h, r):
    """Angle of the box's long side in degrees, in [0, 180), whichever side the model called width."""
    deg = math.degrees(r) + (90 if w < h else 0)
    return deg % 180

model = YOLO("yolo26n-obb.pt")  # trained on DOTAv1, 15 aerial classes
result = model("https://ultralytics.com/images/boats.jpg", verbose=False)[0]
obb = result.obb.cpu()

print("xywhr:", tuple(obb.xywhr.shape), " xyxyxyxy:", tuple(obb.xyxyxyxy.shape))
print(Counter(result.names[int(c)] for c in obb.cls.tolist()))
for (cx, cy, w, h, r), conf in list(zip(obb.xywhr.tolist(), obb.conf.tolist()))[:5]:
    print(f"{conf:.2f} center=({cx:.0f},{cy:.0f}) size={w:.0f}x{h:.0f} raw={math.degrees(r):4.0f} deg  axis={long_side_axis(w, h, r):4.0f} deg")
xywhr: (173, 5)  xyxyxyxy: (173, 4, 2)
Counter({'ship': 168, 'harbor': 5})
0.87 center=(619,850) size=33x108 raw= -15 deg  axis=  75 deg
0.86 center=(1650,937) size=116x33 raw=  21 deg  axis=  21 deg
0.86 center=(1105,571) size=124x35 raw=  21 deg  axis=  21 deg
0.86 center=(1596,1037) size=129x35 raw=  25 deg  axis=  25 deg
0.86 center=(825,940) size=34x99 raw= -15 deg  axis=  75 deg

Analisi riga per riga:

  • xywhr: (173, 5) — 173 rilevamenti, ciascuno con centro x, centro y, larghezza e altezza in pixel e una rotazione in radianti. xyxyxyxy contiene gli stessi riquadri come quattro punti d'angolo, pronti per cv2.polylines.
  • Il contatore conta a partire da result.obb.cls: 168 navi e 5 porti. Nei modelli OBB, result.boxes è None, quindi il codice di conteggio scritto per il rilevamento deve usare campi diversi.
  • raw rispetto a axis. La prima nave misura 33 × 108 a −15°: il suo w è il lato corto, quindi lo scafo è orientato a −15° + 90° = 75°. La seconda misura 116 × 33 a 21° e ha già il lato lungo per primo. Se leggi gli angoli grezzi, l'asse della prima nave è sbagliato di 90°; dopo la normalizzazione, l'angolo di ogni nave ha lo stesso significato e può essere confrontato tra navi e fotogrammi.

La regola deriva dal codice sorgente installato: la rotazione predetta è compresa nell'intervallo [−π/4, 3π/4) — all'incirca da −45° a 135° — e lo stesso rettangolo può essere (w, h, θ) oppure (h, w, θ + 90°). Non dare mai per scontato che w sia il lato lungo.

Per gli oggetti piccoli servono riquadri, non immagini ridotte#

Gli oggetti ripresi dall'alto sono piccoli e validare o distribuire con una imgsz inferiore a quella di addestramento riduce rapidamente il richiamo. Suddividi i riquadri satellitari più grandi dell'input del modello in riquadri sovrapposti, invece di ridimensionarli: un'auto di 30 pixel diventerebbe una macchia di 5 pixel. La guida all'inferenza a riquadri SAHI illustra la tecnica; in un fotogramma autostradale, un rilevatore che analizza l'intera immagine non rileva molti veicoli lontani:

Fotogramma autostradale con rilevamenti ottenuti analizzando l'intera immagine, in cui mancano molti veicoli piccoli e lontani

mentre l'inferenza a riquadri li trova:

Lo stesso fotogramma autostradale con l'inferenza a riquadri SAHI, che rileva molti più veicoli piccoli e lontani

Insidie#

Né fronte né retro

Sintomo: un «orientamento» che cambia di 180° tra un frame e l'altro, oppure navi che sembrano navigare all'indietro. Causa: un rettangolo ruotato di 180° è lo stesso rettangolo, quindi OBB fornisce un asse, non un orientamento. Soluzione: se ti serve sapere in che direzione è rivolto una nave o un carrello elevatore, aggiungi un keypoint di posa sulla prua o sulle punte delle forche, oppure un classificatore applicato al ritaglio.

Confronto tra curve di validazione grezze e risultati di rilevamento

Sintomo: curve precision-recall OBB diverse da quelle dei tuoi rilevamenti su dati simili. Causa: la validazione OBB usa conf=0.01 come valore predefinito, mentre gli altri task usano 0.001. Soluzione: confronta i valori mAP oppure imposta esplicitamente conf quando ti servono curve precisione-richiamo corrispondenti.

Provalo

Esegui obb.py, poi scrivi uno script che calcoli l'asse del lato lungo di ogni nave nell'immagine del porto, trovi l'asse più comune arrotondato ai 10° più vicini e conti le navi che se ne discostano di oltre 30°. Ricorda che 175° e 5° distano solo 10°.

Criteri di completamento
La lezione è completata quando tutte le seguenti condizioni sono soddisfatte.
  • Lo script stampa il numero di navi, l'asse più comune e il numero di navi che se ne discostano di oltre 30°.

  • Il confronto degli assi si ripete a 180°, quindi 175° e 5° risultano distanti 10°.

  • Sai spiegare perché OBB non può distinguere la prua dalla poppa e cosa aggiungeresti se ti servisse conoscere la direzione di prua.

Mostra la soluzione
import math
from collections import Counter

from ultralytics import YOLO

result = YOLO("yolo26n-obb.pt")("https://ultralytics.com/images/boats.jpg", verbose=False)[0]
axes = []
for (cx, cy, w, h, r), cls in zip(result.obb.xywhr.cpu().tolist(), result.obb.cls.tolist()):
    if result.names[int(cls)] == "ship":
        axes.append((math.degrees(r) + (90 if w < h else 0)) % 180)  # long-side axis in [0, 180)

def axis_gap(a, b):
    """Smallest difference between two axes; 175 and 5 degrees are only 10 apart."""
    d = abs(a - b) % 180
    return min(d, 180 - d)

common = Counter(round(a / 10) * 10 % 180 for a in axes).most_common(1)[0][0]
off = [a for a in axes if axis_gap(a, common) > 30]
print(f"{len(axes)} ships, most common axis ~{common} deg, {len(off)} more than 30 deg off it")
168 ships, most common axis ~20 deg, 32 more than 30 deg off it

La maggior parte delle barche è allineata parallelamente, a circa 20°; le 32 navi fuori asse sono i casi anomali che una regola di monitoraggio del porto segnalerebbe per una verifica. OBB non può distinguere la prua dalla poppa perché un rettangolo è simmetrico rispetto a una rotazione di 180°; un keypoint di posa sulla prua fornirebbe la direzione effettiva.

Prossimi passi

Ora eliminiamo del tutto la localizzazione e addestriamo un classificatore per immagini intere e ritagli.

Accedi per salvare i progressi