Ultralytics YOLO27: Inscreve-te na lista de espera.
Get Started
Aulaintermediário

Caixas delimitadoras orientadas para objetos girados

Rotule caixas delimitadoras orientadas (OBB) de quatro cantos, treine yolo26n-obb.pt com DOTA8 e converta previsões xywhr em um eixo de orientação estável sem cair nas armadilhas das convenções angulares.

Navios em um porto, pacotes em uma esteira e carros em um estacionamento vistos de cima compartilham um problema: caixas delimitadoras alinhadas aos eixos em torno de objetos girados e muito próximos umas das outras se sobrepõem e incluem áreas de fundo, então o NMS exclui objetos reais e as contagens ficam abaixo do valor correto. As caixas delimitadoras orientadas ajustam um retângulo girado a cada objeto, resolvendo a contagem em cenas densas e fornecendo também um ângulo. Esse ângulo vem com convenções que confundem quase todas as primeiras integrações — na imagem do porto desta lição, o primeiro navio indica uma rotação de −15°, mas o casco está orientado a 75°.

Resultado

Treine yolo26n-obb.pt com dota8.yaml, conte as detecções por classe a partir de result.obb e converta cada xywhr da previsão em um eixo do lado mais comprido, com ângulos no intervalo [0, 180).

Via rápida
Se já sabes orientar-te, aqui está a versão resumida.
  1. Linha do rótulo: classe, seguida dos quatro cantos x1 y1 … x4 y4, normalizados e ordenados ao longo do perímetro.

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

  3. Leia result.obb.xywhr (rotação em radianos) ou .xyxyxyxy (cantos); os resultados OBB não preenchem result.boxes.

  4. Normalize a orientação em relação ao lado mais comprido: some 90° quando w < h e, em seguida, calcule o ângulo módulo 180°.

Prático

Quando a rotação importa#

Caixas delimitadoras orientadas da Ultralytics YOLO ajustadas rente a três iates atracados em uma foto aérea, cada um identificado como Ship com sua confiança

Estimativa de pose e OBB explica quando escolher OBB em vez de detecção. O teste prático: se as caixas delimitadoras alinhadas aos eixos em torno de objetos vizinhos se sobrepõem mais do que os próprios objetos, o NMS suprime detecções verdadeiras e as contagens ficam abaixo do valor correto. Imagens aéreas são o caso clássico, por isso o modelo pré-treinado yolo26n-obb.pt é treinado com DOTA em imgsz=1024.

Caixa alinhada aos eixosCaixa orientada
Ajustada a um navio a 45°cerca de metade é fundojusta
Fileiras densas e giradasas caixas se sobrepõem; o NMS descarta objetos verdadeirosas caixas ficam claramente separadas
Fornece um ângulonãosim, como um eixo (módulo 180°)
Campo de resultadoresult.boxesresult.obb

O formato de rótulo xyxyxyxy#

Uma linha de rótulo OBB contém o índice da classe seguido dos quatro cantos, normalizados e ordenados ao longo do perímetro. Uma linha real do DOTA8 (a classe 1 é ship):

1 0.813477 0.523437 0.814453 0.504882 0.87207 0.504882 0.87207 0.524414

Leia como quatro pontos: (0.813, 0.523) → (0.814, 0.505) → (0.872, 0.505) → (0.872, 0.524). Este navio está quase na horizontal, com cerca de 0.059 da largura e 0.019 da altura da imagem. Internamente, a Ultralytics converte cada conjunto de cantos em centro, largura, altura e rotação (xywhr) ajustando o retângulo de área mínima; assim, uma quadrilateral ligeiramente irregular de uma ferramenta de anotação ainda pode ser usada no treinamento. O guia de conjuntos de dados OBB documenta o formato, e convert_dota_to_yolo_obb em ultralytics.data.converter converte as anotações originais do DOTA.

Editor de anotações da Ultralytics Platform em uma imagem aérea do DOTA8, com caixas giradas em torno de quadras de tênis, veículos pequenos e uma piscina

Treinar com DOTA8#

DOTA8 é um pequeno conjunto de dados de caixas orientadas criado para testar pipelines:

Mosaico de treinamento do DOTA8 com imagens aéreas em blocos: fileiras de barcos atracados, quadras de tênis, um estacionamento e aviões no pátio, cada objeto com uma caixa orientada e um índice de 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

As métricas de OBB estão em metrics.box, mas o IoU usado nelas é calculado entre retângulos rotacionados.

DOTA8 é um teste rápido

A pontuação é elevada porque DOTA8 é uma amostra dos dados de pré-treino. Prova que o pipeline funciona; não diz nada sobre as tuas imagens.

Ler as previsões e normalizar o ângulo#

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

Linha por linha:

  • xywhr: (173, 5) — 173 detecções, cada uma com centro x, centro y, largura e altura em pixels e uma rotação em radianos. xyxyxyxy contém as mesmas caixas como quatro pontos de canto, prontas para cv2.polylines.
  • O contador conta a partir de result.obb.cls: 168 navios e 5 portos. result.boxes é None nos modelos OBB, então o código de contagem escrito para detecção precisa usar campos diferentes.
  • raw versus axis. O primeiro navio mede 33 × 108 a −15°: o w dele é o lado curto, então o casco está alinhado a −15° + 90° = 75°. O segundo mede 116 × 33 a 21°, já com o lado longo primeiro. Se você ler os ângulos brutos, o eixo do primeiro navio estará errado em 90°; após a normalização, o ângulo de cada navio significa a mesma coisa e pode ser comparado entre navios e quadros.

A regra vem do código-fonte instalado: a rotação prevista está no intervalo [−π/4, 3π/4) — aproximadamente de −45° a 135° — e o mesmo retângulo pode ser (w, h, θ) ou (h, w, θ + 90°). Nunca presuma que w é o lado longo.

Objetos pequenos precisam de blocos, não de redução de tamanho#

Os objetos aéreos são pequenos, e validar ou implantar com uma resolução inferior à de treinamento imgsz reduz rapidamente o recall. Divida blocos de imagens de satélite maiores que a entrada do modelo em blocos sobrepostos, em vez de reduzi-los, ou um carro de 30 pixels vira uma mancha de 5 pixels. O guia de inferência em blocos do SAHI mostra a técnica; em um quadro de rodovia com um detector, a inferência da imagem inteira deixa passar muitos veículos distantes:

Quadro de rodovia com detecções da inferência da imagem inteira, que deixa passar muitos veículos pequenos e distantes

e a inferência em blocos os encontra:

O mesmo quadro de rodovia com inferência em blocos do SAHI, que detecta muito mais veículos pequenos e distantes

Armadilhas#

Sem distinção entre frente e traseira

Sintoma: uma "orientação" que muda 180° entre fotogramas ou navios que parecem navegar para trás. Causa: um retângulo rodado 180° continua a ser o mesmo retângulo, por isso OBB indica um eixo, não uma orientação. Solução: se precisares de saber para que lado aponta um navio ou empilhador, adiciona um ponto-chave de pose na proa ou nas pontas dos garfos, ou um classificador aplicado ao recorte.

Comparação de curvas de validação brutas com execuções de detecção

Sintoma: curvas de precisão-revocação OBB que parecem diferentes das suas execuções de detecção em dados semelhantes. Causa: a validação OBB usa conf=0.01 por padrão, enquanto outras tarefas usam 0.001. Solução: compare os valores de mAP ou defina conf explicitamente quando precisar de curvas de precisão-revocação correspondentes.

Experimente

Execute obb.py e escreva um script que calcule o eixo do lado longo de cada navio na imagem do porto, encontre o eixo mais comum arredondado para os 10° mais próximos e conte os navios que se desviam mais de 30° dele. Lembre-se: 175° e 5° estão separados por apenas 10°.

Concluído quando
Você concluiu a aula quando todos estes critérios forem atendidos.
  • Seu script imprime o número de navios, o eixo mais comum e a quantidade de navios que se desviam dele em mais de 30°.

  • A comparação dos seus eixos considera a volta em 180°, então 175° e 5° contam como separados por 10°.

  • Você consegue explicar por que OBB não distingue a proa da popa e o que adicionaria se precisasse de um rumo.

Mostrar solução
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

A maioria dos barcos está paralela, em torno de 20°; os 32 navios fora do eixo são os casos atípicos que uma regra de monitoramento do porto sinalizaria para revisão. OBB não consegue distinguir a proa da popa porque um retângulo é simétrico sob uma rotação de 180°; um ponto-chave de pose na proa forneceria um rumo verdadeiro.

Próximos passos

A seguir, abandonamos totalmente a localização e treinamos um classificador para imagens inteiras e recortes.

Entre para salvar seu progresso