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

Segmentação semântica: rotular cada pixel

Prevê um mapa de classes por pixel com yolo26n-sem.pt, converte-o em valores de cobertura, cria conjuntos de dados de mapas de classes PNG e interpreta mIoU sem te deixares enganar pela precisão por pixel.

Algumas perguntas não envolvem objetos para contar: que parte da calçada está bloqueada, que parte de um campo tem ervas daninhas, onde termina a via transitável. A segmentação semântica responde com um ID de classe por pixel. No entanto, a sua métrica principal é fácil de interpretar mal: o modelo que treinas nesta lição atinge 91% de precisão por pixel sem detetar uma única pessoa.

Resultado

Converte um mapa de classes yolo26n-sem.pt num valor de cobertura para uma região do quadro, treina com Cityscapes8 e indica mIoU, precisão por pixel e as classes que o modelo não deteta.

Via rápida
Se já sabes orientar-te, aqui está a versão resumida.
  1. Faz uma previsão com yolo26n-sem.pt e lê result.semantic_mask.data: um tensor H × W de IDs de classe no tamanho original, em que 255 = ignorar.

  2. Responde a perguntas sobre cobertura comparando o mapa de classes com os IDs de classe de result.names.

  3. Armazena os rótulos como mapas de classes PNG sem perdas em masks/, espelhando images/, com masks_dir: masks no YAML.

  4. Define mIoU e IoU por classe como critérios para aprovar versões, nunca apenas a precisão por pixel.

Prático

Instância ou semântica?#

Segmentação semântica de uma rua de cidade, com cada pixel colorido de acordo com a classe: estrada, calçada, carro, pessoa, bicicleta, vegetação, edifício e céu

Segmentação de instâncias (segment)Segmentação semântica (semantic)
Saídauma máscara por objeto, mais uma caixaum ID de classe por pixel
Conta objetossimnão — dois carros encostados formam uma única região de «carro»
Lida com «coisas» (estrada, céu, relva)mal — não há instâncias naturaissim
Rótulospolígonosmapas de classes PNG ou polígonos
Métrica de aprovaçãomAP50-95 de máscarasmIoU
Pesos pré-treinadosyolo26n-seg.pt (COCO, 80 classes)yolo26n-sem.pt (Cityscapes, 19 classes)
Quantos ou quanto?

Se a pergunta a jusante incluir "quantos", usa segmentação de instâncias. Se for "quanto" ou "onde", a segmentação semântica é mais simples de etiquetar e interpretar. A página da tarefa de segmentação semântica inclui a tabela de modelos.

Faz uma previsão de mapa de classes#

result.semantic_mask.data é um tensor H × W no tamanho original da imagem; cada valor é um ID de classe e 255 significa ignorar. result.summary() converte-o em proporções de pixels por classe:

from ultralytics import YOLO

model = YOLO("yolo26n-sem.pt")  # Cityscapes, 19 classes
result = model("https://ultralytics.com/images/bus.jpg")[0]

class_map = result.semantic_mask.data  # H x W tensor of class ids, 255 = ignore
print("class map:", tuple(class_map.shape), class_map.dtype)
for row in result.summary():
    if row["pixel_ratio"] >= 0.02:
        print(f"{row['name']:12s} {row['pixel_ratio']:.1%}")
class map: (1080, 810) torch.uint8
road         27.5%
sidewalk     2.0%
building     19.3%
vegetation   4.5%
person       14.6%
train        30.9%

Observa a última linha. Cityscapes tem uma classe bus, mas o modelo classifica o ônibus como train. O modelo aprendeu com imagens gravadas a partir de carros, sobretudo em ruas alemãs, e um ônibus fotografado de perto a partir da calçada está fora dessa distribuição. Os modelos semânticos herdam o ponto de vista e as definições de classe do conjunto de treino — o seu viés de conjunto de dados — de forma mais rígida do que os detetores; por isso, ajusta o modelo com imagens da tua câmera antes de confiar no mapa.

Converte o mapa de classes numa resposta#

Um mapa de classes transforma-se num valor de produto com simples comparações de tensores. Eis «que parte da área em que um pedestre caminharia corresponde à estrada ou à calçada, e que parte corresponde a pessoas»:

from ultralytics import YOLO

result = YOLO("yolo26n-sem.pt")("https://ultralytics.com/images/bus.jpg", verbose=False)[0]
class_map = result.semantic_mask.data  # H x W class ids at the original size, 255 = ignore
ids = {name: i for i, name in result.names.items()}

h = class_map.shape[0]
lower = class_map[h // 2 :]  # the half of the frame a pedestrian would walk into
walkable = (lower == ids["road"]) | (lower == ids["sidewalk"])
people = lower == ids["person"]
print(f"lower half: {walkable.float().mean():.0%} road or sidewalk, {people.float().mean():.0%} person")
result.save("semantic_overlay.jpg")  # colored class map over the photo, for a visual check
lower half: 59% road or sidewalk, 23% person

Três hábitos tornam estes valores fiáveis:

  • Consulta os IDs pelo nome em result.names, nunca por um número fixo; um modelo treinado com as tuas próprias classes atribui-lhes números diferentes.
  • Limita a análise à região relevante. As proporções do quadro inteiro incluem céu e edifícios; primeiro, recorta o mapa de classes (ou multiplica-o por uma máscara de região).
  • Abre a sobreposição. semantic_overlay.jpg mostra num relance se a «estrada» contabilizada pelo valor é realmente estrada.

O formato do conjunto de dados#

Os conjuntos de dados semânticos associam cada imagem a uma imagem de máscara de canal único, cujos valores de pixel são IDs de classe. O guia de conjuntos de dados semânticos é a referência; a estrutura espelha images/ ficheiro a ficheiro:

cityscapes8/
├── images/
│   ├── train/aachen_000000_000019.png
│   └── val/aachen_000004_000019.png
└── masks/
    ├── train/aachen_000000_000019.png
    └── val/aachen_000004_000019.png

O YAML acrescenta masks_dir e, quando os IDs armazenados nas tuas máscaras diferem dos IDs de treino, um bloco label_mapping:

path: cityscapes8
train: images/train
val: images/val
masks_dir: masks # semantic mask directory

names:
  0: road
  1: sidewalk
  # ... 19 classes in total

label_mapping: # raw mask value -> training id
  0: ignore_label
  7: 0 # road
  8: 1 # sidewalk
  # ...

As máscaras Cityscapes armazenam IDs de rótulo brutos de -1 a 33; o mapeamento mantém 19 deles e atribui os restantes a ignore_label, armazenado como 255. Os pixels definidos como 255 são excluídos tanto da função de perda como das métricas — usa o mesmo valor para as regiões sobre as quais os teus anotadores não conseguiram chegar a uma decisão.

Se já tens rótulos de polígonos e não tens masks_dir, o carregador rasteriza os polígonos por ti e atribui a classe background aos pixels não cobertos.

Nunca guardes máscaras como JPEG

A compressão com perdas mistura valores de píxeis vizinhos, transformando a fronteira entre a classe 7 e a classe 8 numa mistura de valores 7, 8 e IDs inexistentes. Guarda todas as máscaras como PNG ou TIFF.

Treina e interpreta mIoU#

O Cityscapes8 tem 8 imagens de cenas urbanas: 4 para treino e 4 para validação, o suficiente para um teste rápido no CPU:

from ultralytics import YOLO

model = YOLO("yolo26n-sem.pt")
model.train(data="cityscapes8.yaml", epochs=1, imgsz=320, device="cpu")
metrics = model.val()
print(f"mIoU: {metrics.miou:.3f}  pixel accuracy: {metrics.pixel_accuracy:.3f}")
                 Class     Images     Pixels       mIoU     PixAcc
                   all          4     192315      0.415      0.906
                  road          4      78529      0.974      0.996
            vegetation          4      37602      0.844      0.936
                person          3        722          0          0
                 rider          1         21          0          0
mIoU: 0.415  pixel accuracy: 0.906

(Linhas por classe omitidas.) A precisão por pixel indica 91%; mIoU indica 42%; as linhas por classe mostram que o modelo não detetou uma única pessoa. As três afirmações são verdadeiras. A estrada, os edifícios e a vegetação ocupam a maioria dos pixels — só a estrada ocupa 78,529 de 192,315 —, por isso um modelo que os classifique bem alcança uma precisão por pixel elevada, mesmo sem detetar nenhuma das classes pequenas. mIoU calcula a média da IoU por classe com pesos iguais, por isso cada classe não detetada reduz o valor.

Interseção sobre União numa ave: a sobreposição das regiões previstas e de referência dividida pela sua união, tanto para caixas como para máscaras

Regra de decisão

Define limiares tanto para mIoU como para o IoU das classes em que o teu produto atua. Uma funcionalidade de segurança para peões com IoU de pessoa igual a 0 falha, independentemente do que indicar a precisão por píxel.

Experimente

Executa semantic.py numa foto de rua tua e lista as três classes com maior área. Adapta walkable.py para responder a uma pergunta sobre a cobertura da tua foto, como a proporção da metade inferior que corresponde à estrada. Depois, treina durante uma época com cityscapes8.yaml e anota mIoU, precisão por pixel e todas as classes com IoU igual a 0.

Concluído quando
Você concluiu a aula quando todos estes critérios forem atendidos.
  • Tens as proporções de pixels por classe e um valor de cobertura de região para a tua própria imagem, confirmados com a sobreposição guardada.

  • Registaste mIoU e precisão por pixel da execução de treino e listaste as classes com IoU igual a zero.

  • Consegues explicar numa frase por que a precisão por pixel é superior a mIoU nesta execução.

Mostrar solução

Na nossa execução: mIoU 0.415, precisão por pixel 0.906 e IoU igual a 0 para parede, pessoa, ciclista, motocicleta e bicicleta, além de todas as classes sem pixels nas quatro imagens de validação.

A precisão por pixel é superior porque conta pixels, e a estrada, os edifícios, a vegetação e o céu compõem a maior parte de cada imagem Cityscapes. mIoU atribui o mesmo peso a cada classe, por isso cinco classes pequenas não detetadas reduzem a média, apesar de ocuparem apenas algumas centenas de pixels. Para um produto que age sobre pessoas, este modelo falha o critério de aprovação apenas pela IoU da classe pessoa.

Próximos passos

A seguir, passamos de regiões para pontos e rotulamos e treinamos um modelo de pose.

Entre para salvar seu progresso