Segmentação panóptica
A segmentação panóptica atribui cada pixel a exatamente um segmento, sem sobreposição, unificando instâncias de objetos contáveis com regiões amorfas de fundo. A chave da tarefa é panoptic.
Definição
A segmentação panóptica é a união das outras duas tarefas de segmentação. Cada pixel recebe exatamente um segmento, os segmentos nunca se sobrepõem, e um segmento é ou um thing, uma instância de objeto contável, ou stuff, uma região amorfa como o céu ou a estrada. Isso a torna mais rigorosa que a segmentação de instâncias, que deixa os pixels de fundo sem atribuição e permite que as máscaras se sobreponham, e mais rigorosa que a segmentação semântica, que rotula cada pixel, mas funde instâncias de uma mesma classe que se tocam.
panoptic é a chave canônica da tarefa, e o sufixo -panoptic no nome do
arquivo de um checkpoint a seleciona, então task= não é necessário ao carregar
pesos publicados.
predict() preenche result.panoptic. .data é um mapa inteiro (H, W) de
ids de segmento sobre o canvas da imagem original. .segments_info é uma lista
de dicts, um por segmento, cada um carregando ao menos {"id", "category_id"},
onde id corresponde a um valor no mapa e category_id indexa result.names.
.segment_ids lista os ids presentes em ordem crescente e .segment_mask(id)
retorna a seleção booleana (H, W) de um segmento. O id de segmento 0 é o
valor void: pixels sem rótulo, excluídos da métrica e deixados de fora de
.segment_ids.
Ser thing ou stuff é uma propriedade da categoria, não do segmento
individual. Essa informação fica nos metadados de categoria do conjunto de
rótulos, e um payload de predição pode copiá-la para cada segmento como
"isthing" por conveniência, mas os metadados de categoria continuam sendo a
fonte autoritativa.
Modelos
EoMT é a família que atende a esta tarefa através de
LibreYOLO(). Roda no pacote base e traz checkpoints panópticos em três
tamanhos, s, b e l, treinados no COCO.
SenseNova-Vision também emite mapas panópticos.
É um modelo generativo guiado por prompt, com sua própria fábrica, LibreVLM, e
seu próprio extra; sem nenhum vocabulário definido, ele recorre às categorias
panópticas do COCO nas quais foi ajustado. Seus pesos são não comerciais. A
latência por imagem é bem mais alta que a de um segmentador dedicado, porque
cada predição é uma decodificação por difusão.
Predição
Os pesos são baixados do Hugging Face no primeiro uso e ficam em cache localmente.
from libreyolo import LibreYOLO, SAMPLE_IMAGE # O sufixo -panoptic no nome do arquivo seleciona a tarefa, então# nenhum argumento de tarefa é necessário.model = LibreYOLO("LibreEoMTl-panoptic.pt")result = model(SAMPLE_IMAGE, save=True) pan = result.panopticprint(pan.data.shape) # (H, W) ids de segmentoprint(pan.segments_info) # [{"id": ..., "category_id": ...}, ...]libreyolo predict model=LibreEoMTl-panoptic.pt save=True \ source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpgfrom libreyolo import LibreYOLO, SAMPLE_IMAGE result = LibreYOLO("LibreEoMTl-panoptic.pt")(SAMPLE_IMAGE)pan = result.panoptic for segment in pan.segments_info: pixels = pan.segment_mask(segment["id"]) # booleano (H, W) print(result.names[segment["category_id"]], int(pixels.sum()))from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreEoMTs-panoptic.pt")result = model(SAMPLE_IMAGE) print(len(result.panoptic.segment_ids))conf filtra a seleção de queries. Veja predição para fontes,
streaming e tratamento de resultados.
Formato do dataset
O LibreYOLO adota o formato COCO-panoptic tal como está, de Kirillov et al., CVPR 2019. Não existe um layout panóptico específico do LibreYOLO.
dataset/
data.yaml
images/
val/000000000139.jpg
annotations/
panoptic_val.json
panoptic_val/000000000139.pngCada imagem é pareada com um PNG RGB na mesma resolução, onde a cor de cada pixel codifica o id do segmento ao qual ele pertence:
segment_id = R + 256 * G + 256 * 256 * BO id de segmento 0, o preto RGB, é void: pixels sem rótulo que não premiam nem
penalizam uma predição. Todos os demais pixels pertencem a exatamente um
segmento.
O JSON lista, por imagem, o PNG de ids de segmento e os segmentos dentro dele:
{
"images": [{"id": 139, "file_name": "000000000139.jpg"}],
"annotations": [{"image_id": 139, "file_name": "000000000139.png",
"segments_info": [
{"id": 3226956, "category_id": 1, "area": 2840,
"bbox": [413, 158, 53, 138], "iscrowd": 0}]}],
"categories": [{"id": 1, "name": "person", "isthing": 1}]
}annotations[].file_name nomeia o PNG dentro do diretório panóptico, e
segments_info[].id corresponde a um valor nesse PNG. iscrowd marca regiões
de grupo: elas nunca são contadas como falsos negativos, e uma predição que
cobre a maior parte de uma delas não é um falso positivo. isthing fica em
categories e nunca em um segmento individual.
O YAML aponta para os dois:
path: dataset
val: images/val
annotations:
val: annotations/panoptic_val.json
panoptic_dir:
val: annotations/panoptic_val
names:
0: person
1: bicycleannotations e panoptic_dir aceitam, cada um, um único caminho ou um
mapeamento por split. Os ids de categoria brutos do COCO costumam ser não
contíguos, enquanto os modelos preveem um intervalo contíguo 0..nc-1, então os
ids são remapeados através de names pelo nome da categoria. Uma categoria do
JSON ausente em names é um erro, em vez de um descarte silencioso, porque
descartá-la contaria como um falso negativo permanente.
O loader canônico é libreyolo.data.PanopticDataset.
Treinamento
Nenhuma família treina segmentação panóptica no LibreYOLO hoje: o train() do
EoMT levanta NotImplementedError, então os checkpoints panópticos são usados
tal como publicados.
Validação
val() retorna um dicionário simples de chaves metrics/, calculadas na
resolução do ground truth sobre o split indicado por val no YAML do dataset.
Um segmento predito e um verdadeiro da mesma categoria correspondem quando o IoU
entre eles supera 0.5, e essa correspondência é única.
from libreyolo import LibreYOLO model = LibreYOLO("LibreEoMTl-panoptic.pt") # val() retorna um dict simples, não um objeto.metrics = model.val(data="my-dataset.yaml") print(metrics["metrics/PQ"])print(metrics["metrics/SQ"], metrics["metrics/RQ"])print(metrics["metrics/PQ_things"], metrics["metrics/PQ_stuff"])libreyolo val model=LibreEoMTl-panoptic.pt data=my-dataset.yamlmetrics/PQ é a Panoptic Quality, o número principal. Dentro de uma categoria,
é o produto de dois fatores. A qualidade de segmentação é o IoU médio sobre os
segmentos correspondentes e diz o quão bem as formas correspondentes se alinham.
A qualidade de reconhecimento é TP / (TP + 0.5 FP + 0.5 FN), o F1 da própria
correspondência, e diz quantos segmentos foram de fato encontrados. Em seguida,
é feita a média dos três números sobre as categorias que apareceram, e eles são
reportados como metrics/PQ, metrics/SQ e metrics/RQ, de modo que o PQ
reportado é a média dos produtos por categoria, e não o produto das duas médias
reportadas.
metrics/PQ_things e metrics/PQ_stuff fazem a média desse mesmo PQ por
categoria sobre as categorias thing e as categorias stuff separadamente, e
metrics/categories conta as categorias que apareceram e sobre as quais,
portanto, a média foi feita. O dicionário também carrega fitness, uma cópia do
valor de PQ.
Exportação
Checkpoints panópticos não exportam. export() levanta NotImplementedError
para esta tarefa, porque a saída de máscaras por query ainda não tem um contrato
de exportação de runtime. A tarefa semântica do EoMT exporta; veja
segmentação semântica e
exportação e deploy.