{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Capítulo 5: Detecção e Segmentação\n", "\n", "Este capítulo apresenta dois grandes problemas da visão computacional moderna: **detecção de objetos** (localizar e classificar instâncias dentro de uma imagem) e **segmentação** (delimitar regiões pixel a pixel). Começamos pelo YOLO26, o detector em tempo real mais recente da família YOLO; passamos por um detector baseado em Transformers (RF-DETR), que aborda a detecção por um caminho arquitetural diferente; e seguimos para o Segment Anything (SAM), o modelo de segmentação fundacional da Meta." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Tutorial do YOLO26\n", "\n", "Uma introdução rápida ao [YOLO26 da Ultralytics](https://docs.ultralytics.com/pt/models/yolo26/), incluindo instalação e inferência com modelos pré-treinados.\n", "\n", "O **YOLO26** é a versão mais recente da família YOLO da Ultralytics e foi projetada com uma filosofia *edge-first*: simplificar a arquitetura para que o mesmo modelo rode bem em CPU, GPU e aceleradores embarcados. Três decisões marcam essa geração:\n", "\n", "- **Detecção sem NMS** (*non-max suppression*): o YOLO26 introduz uma cabeça de detecção **one-to-one** que prevê diretamente um conjunto fixo de hipóteses, eliminando o pós-processamento de NMS e simplificando a exportação para ONNX, TensorRT, CoreML, TFLite e OpenVINO.\n", "- **Treino reformulado**: novas técnicas como *Progressive Loss Balancing* (ProgLoss), *Small-Target-Aware Label Assignment* (STAL) e o otimizador MuSGD melhoram a estabilidade do treino e a precisão em objetos pequenos.\n", "- **Desempenho em CPU**: até **43% mais rápido em CPU** que o YOLO11 com a mesma precisão, o que torna o modelo prático para deployment em hardware modesto (Raspberry Pi, Jetson Nano).\n", "\n", "Suporta as cinco tarefas clássicas da família: **detecção**, **segmentação de instâncias**, **estimativa de pose**, *oriented bounding boxes* (OBB) e **classificação**." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Instalação e Modelo Pré-treinado\n", "\n", "Nesta seção instalamos a biblioteca e carregamos um modelo YOLO26 já treinado, pronto para uso. Vamos começar pela parte mais direta (rodar **inferência** com pesos pré-treinados) e, mais adiante no capítulo, veremos como **treinar** o modelo com dados próprios.\n", "\n", "**Instalação**\n", "\n", "Primeiro, instale a biblioteca `ultralytics`, que contém a implementação do YOLO26:\n", "\n", "```bash\n", "!pip install ultralytics\n", "```\n", "\n", "**Modelos pré-treinados**\n", "\n", "A Ultralytics disponibiliza pesos já treinados no COCO (80 classes), prontos para uso imediato. Basta escolher o tamanho do modelo conforme o equilíbrio desejado entre velocidade e precisão:\n", "\n", "* **`yolo26n.pt`**: versão *nano*, leve e otimizada para dispositivos com recursos limitados, ideal para testes rápidos e prototipagem.\n", "* Variantes maiores (**`yolo26s.pt`**, **`yolo26m.pt`**, **`yolo26l.pt`**, **`yolo26x.pt`**) trocam velocidade por mais precisão.\n", "\n", "Carregar um modelo pré-treinado é simples:\n", "\n", "```python\n", "from ultralytics import YOLO\n", "\n", "# Baixa (na primeira vez) e carrega os pesos pré-treinados\n", "model = YOLO(\"yolo26n.pt\")\n", "```\n", "\n", "Com o modelo carregado, já podemos rodar inferência diretamente, como nos exemplos a seguir." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exemplo de Detecção de Objetos\n", "\n", "Com o modelo pré-treinado carregado, já podemos detectar objetos em uma imagem. Para não repetir o mesmo código de visualização em cada tarefa, definimos primeiro uma função auxiliar que roda o modelo e exibe a imagem original ao lado do resultado:\n", "\n", "```python\n", "from ultralytics import YOLO\n", "from ultralytics.utils import ASSETS # imagens de exemplo que vêm com o pacote\n", "import cv2\n", "import matplotlib.pyplot as plt\n", "\n", "def inferir_e_exibir(model, image_path, titulo=\"Resultado\"):\n", " \"\"\"Roda o modelo na imagem e mostra original vs. resultado lado a lado.\"\"\"\n", " results = model(image_path)\n", "\n", " # OpenCV lê em BGR; convertemos para RGB para exibir no matplotlib\n", " img = cv2.cvtColor(cv2.imread(str(image_path)), cv2.COLOR_BGR2RGB)\n", " # results[0].plot() devolve a imagem anotada (também em BGR)\n", " result_img = cv2.cvtColor(results[0].plot(), cv2.COLOR_BGR2RGB)\n", "\n", " fig, axs = plt.subplots(1, 2, figsize=(15, 7))\n", " axs[0].imshow(img); axs[0].set_title(\"Imagem Original\"); axs[0].axis(\"off\")\n", " axs[1].imshow(result_img); axs[1].set_title(titulo); axs[1].axis(\"off\")\n", " plt.tight_layout()\n", " plt.show()\n", "```\n", "\n", "Como imagem de teste usamos a `bus.jpg`, que já acompanha a biblioteca `ultralytics` (em `ASSETS`), então não é preciso baixar nada. Começando pela detecção:\n", "\n", "```python\n", "# Modelo de detecção\n", "model = YOLO(\"yolo26s.pt\")\n", "\n", "# Imagem de exemplo embutida no pacote ultralytics\n", "from ultralytics.utils import ASSETS\n", "rua = ASSETS / \"bus.jpg\"\n", "\n", "inferir_e_exibir(model, rua, \"Resultado da Detecção\")\n", "```\n", "\n", "
\n", " \"Resultado\n", "
\n", "
\n", "\n", "### Exemplo de Segmentação\n", "\n", "A mesma função serve para segmentação: basta trocar para um modelo `-seg` e reutilizar a mesma imagem.\n", "\n", "```python\n", "# Modelo de segmentação de instâncias\n", "model = YOLO(\"yolo26n-seg.pt\")\n", "\n", "# Mesma imagem do exemplo anterior\n", "from ultralytics.utils import ASSETS\n", "rua = ASSETS / \"bus.jpg\"\n", "\n", "inferir_e_exibir(model, rua, \"Resultado da Segmentação\")\n", "```\n", "\n", "
\n", " \"Resultado\n", "
\n", "
\n", "\n", "### Exemplo de Estimativa de Pose\n", "\n", "Para estimativa de pose, usamos a imagem `zidane.jpg` (também embutida no pacote), que mostra pessoas em pé.\n", "\n", "```python\n", "# Modelo de estimativa de pose\n", "model = YOLO(\"yolo26n-pose.pt\")\n", "\n", "# Outra imagem de exemplo do pacote, com pessoas\n", "from ultralytics.utils import ASSETS\n", "pessoas = ASSETS / \"zidane.jpg\"\n", "\n", "inferir_e_exibir(model, pessoas, \"Resultado da Pose\")\n", "```\n", "\n", "
\n", " \"Resultado\n", "
\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Aplicação em um Vídeo\n", "\n", "A detecção também pode ser aplicada quadro a quadro em um vídeo. Para testar, use o vídeo de exemplo abaixo (uma cena curta de rua, gerada para este material):\n", "\n", "[Download do vídeo de teste](video_teste.mp4)\n", "\n", "O código abaixo lê o vídeo de entrada, roda o YOLO26 em cada quadro e grava um novo vídeo com as detecções desenhadas:\n", "\n", "```python\n", "from ultralytics import YOLO\n", "import cv2\n", "\n", "# Modelo de detecção\n", "model = YOLO(\"yolo26n.pt\")\n", "\n", "# Caminhos dos vídeos de entrada e saída\n", "input_video_path = \"video_teste.mp4\"\n", "output_video_path = \"video_resultado.mp4\"\n", "\n", "# Abrir o vídeo de entrada\n", "cap = cv2.VideoCapture(input_video_path)\n", "\n", "# Configurar o vídeo de saída com a mesma taxa de quadros e resolução da entrada\n", "fourcc = cv2.VideoWriter_fourcc(*\"mp4v\")\n", "out = cv2.VideoWriter(output_video_path, fourcc, cap.get(cv2.CAP_PROP_FPS),\n", " (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))))\n", "\n", "while cap.isOpened():\n", " ret, frame = cap.read()\n", " if not ret:\n", " break\n", "\n", " # Detectar objetos no quadro (verbose=False evita imprimir um log por quadro)\n", " results = model(frame, verbose=False)\n", "\n", " # Quadro com as detecções desenhadas\n", " annotated_frame = results[0].plot()\n", "\n", " # Gravar o quadro anotado no vídeo de saída\n", " out.write(annotated_frame)\n", "\n", "# Liberar os recursos\n", "cap.release()\n", "out.release()\n", "cv2.destroyAllWindows()\n", "\n", "print(f\"Vídeo com detecção salvo em: {output_video_path}\")\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Treinamento com Dados Próprios\n", "\n", "Até aqui usamos um modelo pré-treinado no COCO para fazer inferência. Quando as classes do seu problema não estão entre as 80 do COCO, o caminho é **treinar** (ou fazer *fine-tuning*) o modelo com seus próprios dados. Agora que você já viu o modelo em ação, vamos a esse passo.\n", "\n", "**Dados de exemplo: o `coco8`**\n", "\n", "Para uma demonstração rápida usamos o **`coco8`**, um subconjunto minúsculo do famoso dataset **COCO** (*Common Objects in Context*). Ele tem apenas **8 imagens** (4 para treino e 4 para validação), com as anotações no formato YOLO (um arquivo `.txt` por imagem, contendo a classe e a caixa delimitadora normalizada de cada objeto). É baixado automaticamente na primeira vez que você treina com `data='coco8.yaml'`, então serve para validar o fluxo de treino em segundos, sem precisar de um dataset grande.\n", "\n", "Embora tenha poucas imagens, o `coco8` herda o **espaço de 80 classes** do COCO, as mesmas que os modelos pré-treinados que usamos na inferência reconhecem. Elas cobrem categorias do dia a dia, que podemos agrupar assim:\n", "\n", "- **Pessoas e animais**: person, bird, cat, dog, horse, sheep, cow, elephant, bear, zebra, giraffe\n", "- **Veículos**: bicycle, car, motorcycle, airplane, bus, train, truck, boat\n", "- **Trânsito e rua**: traffic light, fire hydrant, stop sign, parking meter, bench\n", "- **Acessórios e esporte**: backpack, umbrella, handbag, tie, suitcase, frisbee, skis, snowboard, sports ball, kite, baseball bat, baseball glove, skateboard, surfboard, tennis racket\n", "- **Cozinha e comida**: bottle, wine glass, cup, fork, knife, spoon, bowl, banana, apple, sandwich, orange, broccoli, carrot, hot dog, pizza, donut, cake\n", "- **Móveis e casa**: chair, couch, potted plant, bed, dining table, toilet\n", "- **Eletrônicos**: tv, laptop, mouse, remote, keyboard, cell phone\n", "- **Eletrodomésticos e objetos**: microwave, oven, toaster, sink, refrigerator, book, clock, vase, scissors, teddy bear, hair drier, toothbrush\n", "\n", "Abaixo, quatro imagens do `coco8` com as anotações (caixas e classes) que acompanham o dataset:\n", "\n", "
\n", " \"Quatro\n", "
\n", "
\n", "\n", "**O arquivo de configuração (`data.yaml`)**\n", "\n", "O que diz ao YOLO *onde* estão as imagens e *quais* são as classes é um arquivo `.yaml`. O `coco8.yaml` é um bom modelo para entender o formato:\n", "\n", "```yaml\n", "path: coco8 # diretório raiz do dataset\n", "train: images/train # imagens de treino (relativo a 'path')\n", "val: images/val # imagens de validação (relativo a 'path')\n", "test: # imagens de teste (opcional)\n", "\n", "names: # mapeamento índice -> nome da classe\n", " 0: person\n", " 1: bicycle\n", " 2: car\n", " # ... (as 80 classes do COCO)\n", "```\n", "\n", "São quatro elementos:\n", "\n", "- **`path`**: a pasta raiz do dataset (pode ser um caminho absoluto, como `/home/usuario/meu_dataset`, ou relativo ao diretório de datasets do Ultralytics).\n", "- **`train`** e **`val`**: as pastas de imagens de treino e validação, *relativas a* `path`. Cada imagem deve ter um arquivo de rótulo `.txt` correspondente, na pasta `labels/` no mesmo nível de `images/`.\n", "- **`names`**: a lista de classes, na ordem dos índices usados nos rótulos.\n", "\n", "A estrutura de pastas esperada é:\n", "\n", "```text\n", "meu_dataset/\n", "├── images/\n", "│ ├── train/ # imagens de treino (.jpg, .png)\n", "│ └── val/ # imagens de validação\n", "└── labels/\n", " ├── train/ # um .txt por imagem (mesmo nome)\n", " └── val/\n", "```\n", "\n", "```{tip}\n", "Ao baixar um dataset de fora (por exemplo, do [Kaggle](https://www.kaggle.com/datasets) ou do [Roboflow](https://roboflow.com/)), procure justamente por esse `data.yaml`. Muitos já vêm com ele pronto. Basta ajustar o `path` para o local onde você descompactou os arquivos e passar esse caminho em `data=`. Se o dataset não estiver no formato YOLO, será preciso converter as anotações antes.\n", "```\n", "\n", "Para treinar com um dataset próprio, é só apontar para o seu YAML:\n", "\n", "```python\n", "# Em vez de coco8.yaml, use o caminho do seu arquivo de configuração\n", "model.train(data=\"/caminho/para/meu_dataset/data.yaml\", epochs=3)\n", "```\n", "\n", "**Exemplo de Treinamento**\n", "\n", "```python\n", "from ultralytics import YOLO\n", "\n", "# Carregar o modelo YOLO26 nano como ponto de partida\n", "model = YOLO(\"yolo26n.pt\")\n", "\n", "# Treinar o modelo com o conjunto COCO8 por 3 épocas\n", "model.train(data='coco8.yaml', epochs=3)\n", "```\n", "\n", "**Resumo do Log Gerado**\n", "\n", "Durante o treinamento, o YOLO26 exibe um log com informações úteis. A tabela abaixo resume os principais elementos:\n", "\n", "| Campo | Descrição |\n", "| --- | --- |\n", "| `epoch` | Época atual / total |\n", "| `gpu_mem` | Memória de GPU usada (GB) |\n", "| `box_loss` | Erro na predição das caixas delimitadoras |\n", "| `cls_loss` | Erro na classificação dos objetos |\n", "| `dfl_loss` | Erro na regressão refinada das caixas (Distribution Focal Loss) |\n", "| `instances` | Número de objetos anotados no batch |\n", "| `size` | Tamanho das imagens de entrada |\n", "| `metrics/precision` | Precisão: taxa de detecções corretas |\n", "| `metrics/recall` | Revocação: taxa de objetos corretamente detectados |\n", "| `metrics/mAP50` | Média da precisão com IoU de 0.5 |\n", "| `metrics/mAP50-95` | Média da precisão com IoU de 0.5 a 0.95 (mais rigoroso e completo) |\n", "\n", "Essas métricas ajudam a monitorar o desempenho do modelo. Idealmente, as perdas (`losses`) devem diminuir e as métricas (`mAP`, `precision`, `recall`) devem aumentar ao longo das épocas." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Configurações avançadas do YOLO26\n", "\n", "Abaixo, apresento uma tabela atualizada com as principais configurações avançadas que podem ser utilizadas tanto no **treinamento** quanto na **predição** com o YOLO26, abrangendo todas as tarefas suportadas.\n", "\n", "**Tabela de Configurações Avançadas do YOLO26**\n", "\n", "| Parâmetro | Descrição | Valor Padrão | Treinamento | Predição | Tarefas Suportadas |\n", "| --- | --- | --- | --- | --- | --- |\n", "| `imgsz` | Tamanho da imagem (pode ser um valor único ou uma tupla). | 640 | ✅ | ✅ | Todas |\n", "| `batch` | Tamanho do lote por GPU durante o treinamento. | 16 | ✅ | – | Todas |\n", "| `epochs` | Número de épocas para o treinamento. | 100 | ✅ | – | Todas |\n", "| `optimizer` | Otimizador utilizado (e.g., SGD, Adam, AdamW, RMSProp). | 'auto' | ✅ | – | Todas |\n", "| `lr0` | Taxa de aprendizado inicial. | 0.01 | ✅ | – | Todas |\n", "| `momentum` | Momentum para otimizadores baseados em momentum. | 0.937 | ✅ | – | Todas |\n", "| `weight_decay` | Decaimento de peso (regularização L2). | 0.0005 | ✅ | – | Todas |\n", "| `warmup_epochs` | Número de épocas de aquecimento para o ajuste gradual da taxa de aprendizado. | 3 | ✅ | – | Todas |\n", "| `patience` | Número de épocas sem melhoria antes de interromper o treinamento antecipadamente. | 100 | ✅ | – | Todas |\n", "| `pretrained` | Utilizar pesos pré-treinados (True, False ou caminho para o modelo). | True | ✅ | – | Todas |\n", "| `conf` | Threshold de confiança para filtrar detecções. | 0.25 | – | ✅ | Detecção, Segmentação, Pose, OBB |\n", "| `iou` | Limiar de IoU do NMS (só no modo `end2end=False`; o YOLO26 é NMS-free por padrão). | 0.7 | – | ✅ | Detecção, Segmentação, Pose, OBB |\n", "| `max_det` | Número máximo de detecções por imagem. | 300 | – | ✅ | Detecção, Segmentação, Pose, OBB |\n", "| `save_period` | Intervalo de épocas para salvar checkpoints durante o treinamento. | -1 | ✅ | – | Todas |\n", "| `augment` | Aplicar aumentos de dados durante a inferência. | False | – | ✅ | Todas |\n", "| `visualize` | Visualizar mapas de características intermediários para depuração. | False | ✅ | ✅ | Todas |\n", "| `flipud` | Probabilidade de aplicar flip vertical durante o aumento de dados. | 0.0 | ✅ | – | Todas |\n", "| `fliplr` | Probabilidade de aplicar flip horizontal durante o aumento de dados. | 0.5 | ✅ | – | Todas |\n", "| `degrees` | Ângulo máximo para rotação aleatória durante o aumento de dados. | 0.0 | ✅ | – | Todas |\n", "| `translate` | Fração máxima para translação aleatória durante o aumento de dados. | 0.1 | ✅ | – | Todas |\n", "| `scale` | Fator de escala para aumento de dados. | 0.5 | ✅ | – | Todas |\n", "| `shear` | Ângulo máximo para cisalhamento durante o aumento de dados. | 0.0 | ✅ | – | Todas |\n", "| `hsv_h`, `hsv_s`, `hsv_v` | Ajustes de matiz, saturação e valor para aumento de dados no espaço HSV. | 0.015, 0.7, 0.4 | ✅ | – | Todas |\n", "| `copy_paste` | Aplicar técnica de copy-paste durante o aumento de dados. | 0.0 | ✅ | – | Detecção, Segmentação |\n", "| `mask_ratio` | Threshold para binarização de máscaras em segmentação. | 0.5 | ✅ | ✅ | Segmentação |\n", "| `kpt_thr` | Threshold de confiança para keypoints em estimativa de pose. | 0.2 | ✅ | ✅ | Pose Estimation |\n", "| `resume` | Retomar treinamento a partir de um checkpoint salvo. | False | ✅ | – | Todas |\n", "| `project` | Nome do diretório de projeto para salvar resultados. | 'runs' | ✅ | ✅ | Todas |\n", "| `name` | Nome da execução específica dentro do projeto. | 'exp' | ✅ | ✅ | Todas |\n", "| `exist_ok` | Permitir sobrescrever diretórios existentes. | False | ✅ | ✅ | Todas |\n", "\n", "> **Nota:** na inferência padrão o YOLO26 é *end-to-end* (sem NMS), então o parâmetro `iou` acima só tem efeito ao ativar a cabeça legada *one-to-many* com `end2end=False`.\n", "\n", "**Tarefas Suportadas pelo YOLO26**\n", "\n", "O YOLO26 é uma estrutura versátil que suporta as seguintes tarefas de visão computacional:\n", "\n", "* **Detecção de Objetos**: Identificação e localização de objetos em imagens ou vídeos.\n", "* **Segmentação de Instâncias**: Delineamento preciso dos contornos de objetos detectados.\n", "* **Classificação de Imagens**: Categorização de imagens em classes predefinidas.\n", "* **Estimativa de Pose**: Detecção e rastreamento de pontos-chave em corpos humanos.\n", "* **Detecção Orientada (OBB)**: Detecção de objetos com orientação rotacional para maior precisão.\n", "\n", "Cada uma dessas tarefas pode ser executada utilizando modelos específicos do YOLO26, como `yolo26n.pt` para detecção, `yolo26n-seg.pt` para segmentação, `yolo26n-cls.pt` para classificação, `yolo26n-pose.pt` para estimativa de pose e `yolo26n-obb.pt` para detecção orientada.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Entendendo o IoU (Intersection over Union)\n", "\n", "Quando um detector como o YOLO26 prevê uma caixa para um objeto, precisamos de uma forma de medir **quão boa** é essa previsão em relação à caixa correta (anotada manualmente). O **IoU (Intersection over Union)** é a métrica padrão para isso: ele compara duas caixas e devolve um número entre 0 e 1 indicando o quanto elas se sobrepõem.\n", "\n", "A ideia é simples: dividir a **área em comum** das duas caixas pela **área total** que elas ocupam juntas.\n", "\n", "$$\n", "IoU = \\frac{\\text{Área da Interseção}}{\\text{Área da União}}\n", "$$\n", "\n", "- **Interseção (∩)**: a região onde as duas caixas se sobrepõem.\n", "- **União (∪)**: a área coberta por pelo menos uma das caixas (a soma das duas, sem contar a sobreposição em dobro).\n", "\n", "
\n", " \"Tres\n", "
\n", "
\n", "\n", "O valor resultante é fácil de interpretar:\n", "\n", "- **IoU = 0**: as caixas não se tocam (previsão totalmente errada de posição).\n", "- **IoU = 1**: as caixas são idênticas (previsão perfeita).\n", "- **valores intermediários**: quanto mais perto de 1, melhor o alinhamento.\n", "\n", "Na prática, o IoU aparece em dois momentos centrais da detecção de objetos:\n", "\n", "- **Avaliação (métrica)**: para decidir se uma detecção \"acertou\", compara-se a caixa prevista com a verdadeira; normalmente considera-se acerto quando o IoU passa de um limiar (por exemplo, **0,5**). É a base de métricas como o **mAP**.\n", "- **Pós-processamento (NMS)**: quando o modelo gera várias caixas sobrepostas para o mesmo objeto, o IoU é usado para identificar e remover as redundantes, mantendo apenas a melhor.\n", "\n", "> Curiosidade: o IoU é uma aplicação do **Índice de Jaccard**, criado para medir a similaridade entre conjuntos. Aqui, os \"conjuntos\" são as áreas das duas caixas.\n", "\n", "Agora que entendemos o conceito, vamos implementá-lo na prática." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Da Precisão ao mAP\n", "\n", "O IoU nos diz quando uma única detecção está bem posicionada. Mas para avaliar um modelo inteiro, em muitas imagens e várias classes, precisamos resumir tudo em um número. Esse número é o **mAP** (*mean Average Precision*), a métrica mais usada em detecção de objetos e a que aparece no log de treino do YOLO26.\n", "\n", "Para chegar nele, partimos de dois conceitos básicos:\n", "\n", "- **Precisão (*precision*)**: das caixas que o modelo previu, quantas estavam certas? Mede o quanto evitamos **falsos positivos**.\n", "- **Revocação (*recall*)**: dos objetos que realmente existiam, quantos o modelo encontrou? Mede o quanto evitamos **falsos negativos**.\n", "\n", "Uma detecção é contada como **acerto (verdadeiro positivo)** quando sua classe está correta **e** o IoU com a caixa verdadeira passa de um limiar (tipicamente 0,5). É aqui que o IoU entra na conta.\n", "\n", "**AP: a área sob a curva precision-recall**\n", "\n", "Ao variar o limiar de confiança do modelo (de muito exigente a muito permissivo), precisão e revocação mudam: exigir muita confiança aumenta a precisão mas reduz a revocação, e vice-versa. Plotando um valor contra o outro, obtemos a **curva precision-recall**. A **Average Precision (AP)** de uma classe é a **área sob essa curva**, um único número entre 0 e 1 que resume o equilíbrio entre acertar e não deixar passar.\n", "\n", "
\n", " \"Curva\n", "
\n", "
\n", "\n", "**De AP para mAP**\n", "\n", "- O **AP** é calculado **por classe**. O **mAP** é a **média dos APs de todas as classes** (o \"m\" de *mean*). Assim, um modelo que vai bem em \"pessoa\" mas mal em \"bicicleta\" tem o mAP penalizado.\n", "- O limiar de IoU usado muda o resultado, por isso existem duas formas comuns:\n", " - **mAP@50** (ou `mAP50`): usa um único limiar de IoU de 0,5. Mais permissivo.\n", " - **mAP@50-95** (ou `mAP50-95`): calcula o mAP em **dez** limiares de IoU (de 0,5 a 0,95, de 0,05 em 0,05) e tira a média. É mais rigoroso, pois exige caixas bem ajustadas, e é a métrica principal usada para comparar modelos no COCO.\n", "\n", "Em resumo: **IoU** mede uma caixa, **AP** resume uma classe, e **mAP** resume o modelo inteiro. São exatamente as colunas `metrics/mAP50` e `metrics/mAP50-95` que vimos no log de treinamento. Quanto mais perto de 1, melhor o detector." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exercícios\n", "\n", "Antes de seguir para outras arquiteturas, é hora de praticar. Os dois exercícios a seguir cobrem os dois lados do que vimos até aqui: medir detecções com o IoU e criar as anotações que servem de verdade para essa medida.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Cálculo do IoU (Intersection over Union)\n", "\n", "Agora é a sua vez: implemente uma função que calcula o IoU entre duas caixas.\n", "\n", "**Representação das Caixas**\n", "\n", "As caixas são representadas por seus cantos superior esquerdo e inferior direito:\n", "\n", "```python\n", "[x1_a, y1_a, x2_a, y2_a, x1_b, y1_b, x2_b, y2_b]\n", "```\n", "\n", "Ou seja:\n", "\n", "* `x1_a`, `y1_a`: coordenadas do **canto superior esquerdo** da **Caixa A**\n", "* `x2_a`, `y2_a`: coordenadas do **canto inferior direito** da **Caixa A**\n", "* `x1_b`, `y1_b`: coordenadas do **canto superior esquerdo** da **Caixa B**\n", "* `x2_b`, `y2_b`: coordenadas do **canto inferior direito** da **Caixa B**\n", "\n", "**Exemplo**\n", "\n", "```python\n", "dados = [2, 2, 5, 5, 4, 3, 7, 6]\n", "```\n", "\n", "* Caixa A: (2, 2) até (5, 5)\n", "* Caixa B: (4, 3) até (7, 6)\n", "\n", "\n", "
\n", " \"AI\"\n", "
\n", "\n", "\n", "**Tarefa**\n", "\n", "- Calcule a área de interseção entre as duas caixas.\n", "- Calcule a área da união.\n", "- Use a fórmula do IoU para obter o valor final.\n", "- **(Opcional, mas altamente recomendado)**: **plote as duas caixas em um gráfico 2D** para visualizar claramente a interseção.\n", "\n", "> Dica: use bibliotecas como `matplotlib` para desenhar as caixas e facilitar a interpretação.\n", "\n", "**Função a implementar**\n", "\n", "```python\n", "def calcular_iou(dados):\n", " \"\"\"\n", " Calcula o Intersection over Union entre duas caixas a partir de uma lista com 8 valores.\n", "\n", " Parâmetros:\n", " dados: lista com 8 elementos no formato\n", " [x1_a, y1_a, x2_a, y2_a, x1_b, y1_b, x2_b, y2_b]\n", "\n", " Retorno:\n", " IoU: float\n", " \"\"\"\n", " # Sua implementação aqui\n", " pass\n", "```\n", "\n", "**Testes**\n", "\n", "```python\n", "# Teste 1\n", "Entrada: 2, 2, 5, 5, 4, 3, 7, 6\n", "Saída: 0.12\n", "\n", "# Teste 2\n", "Entrada: 1, 1, 4, 4, 1, 1, 4, 4\n", "Saída: 1.00\n", "\n", "# Teste 3\n", "Entrada: 0, 0, 6, 6, 2, 2, 4, 4\n", "Saída: 0.11\n", "\n", "# Teste 4\n", "Entrada: 0, 0, 2, 2, 3, 3, 5, 5\n", "Saída: 0.0\n", "\n", "```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Criando seu Próprio Dataset com o Roboflow\n", "\n", "Até aqui você *usou* modelos e *avaliou* resultados. Agora vai para o outro lado do processo: **criar os dados** que alimentam um modelo. Anotar é uma das etapas mais importantes (e trabalhosas) de qualquer projeto de visão computacional. A qualidade das anotações limita a qualidade do modelo.\n", "\n", "Neste exercício você vai montar um pequeno dataset de **pessoas** a partir do [`video_teste.mp4`](video_teste.mp4) usado nos exemplos de detecção, e anotá-lo de três formas diferentes no [Roboflow](https://roboflow.com/), uma ferramenta gratuita e baseada no navegador.\n", "\n", "**Passo 1: Extrair quadros do vídeo**\n", "\n", "Um vídeo é uma sequência de imagens. Para anotar, primeiro extraímos alguns quadros como imagens individuais. O código abaixo salva 1 quadro por segundo do vídeo em uma pasta `frames/`:\n", "\n", "```python\n", "import cv2\n", "import os\n", "\n", "os.makedirs(\"frames\", exist_ok=True)\n", "\n", "cap = cv2.VideoCapture(\"video_teste.mp4\")\n", "fps = cap.get(cv2.CAP_PROP_FPS)\n", "passo = int(fps) # 1 quadro por segundo\n", "\n", "i = salvos = 0\n", "while cap.isOpened():\n", " ret, frame = cap.read()\n", " if not ret:\n", " break\n", " if i % passo == 0:\n", " cv2.imwrite(f\"frames/frame_{salvos:03d}.jpg\", frame)\n", " salvos += 1\n", " i += 1\n", "\n", "cap.release()\n", "print(f\"{salvos} quadros salvos em frames/\")\n", "```\n", "\n", "**Passo 2: Anotar no Roboflow**\n", "\n", "1. Crie uma conta gratuita em [roboflow.com](https://roboflow.com/) e inicie um novo projeto.\n", "2. Faça upload dos quadros da pasta `frames/`.\n", "3. Anote a classe **`person`** em cada imagem, repetindo o processo nos **três tipos de tarefa**:\n", " - **Detecção (*Object Detection*)**: desenhe uma *bounding box* ao redor de cada pessoa.\n", " - **Segmentação (*Instance Segmentation*)**: contorne o formato de cada pessoa com um polígono.\n", " - **Pose (*Keypoint Detection*)**: marque os pontos-chave do corpo (cabeça, ombros, cotovelos, joelhos, etc.).\n", "4. Gere uma versão do dataset e use o **Export** do Roboflow no formato **YOLO**.\n", "\n", "```{tip}\n", "Repare que o Roboflow exporta exatamente a estrutura que vimos na seção de treinamento: pastas `images/` e `labels/` mais um `data.yaml`. Ou seja, o que você anotar aqui pode ser treinado diretamente com `model.train(data=\"data.yaml\")`.\n", "```\n", "\n", "**Reflexão**\n", "\n", "- Quantos quadros foram necessários para ter variedade suficiente de poses e posições?\n", "- Qual das três anotações foi mais trabalhosa? Por quê?\n", "- Como a anotação se conecta com o IoU? (Dica: a caixa que você desenha é a \"verdade\" contra a qual o IoU do modelo será medido.)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Detecção com Transformers: RF-DETR\n", "\n", "O YOLO26 resolve a detecção com uma rede totalmente convolucional. Existe, porém, uma segunda grande linhagem de detectores, nascida de uma ideia diferente: tratar a detecção como um problema de **conjunto** e resolvê-lo com **Transformers**, a mesma arquitetura de atenção que revolucionou o processamento de linguagem natural.\n", "\n", "Essa linhagem começou com o **DETR (*Detection Transformer*)**, da Meta, em 2020. Em vez de gerar milhares de caixas candidatas e depois filtrá-las, o DETR prevê **diretamente** um conjunto fixo de objetos, casando cada previsão com um objeto real durante o treino. Disso vêm duas consequências importantes:\n", "\n", "- **Fim do NMS**: como cada objeto é previsto uma única vez, não há caixas redundantes para remover, e o modelo se torna *end-to-end* de verdade. (Curiosamente, o YOLO26 chega ao mesmo destino, a detecção sem NMS, partindo de uma arquitetura convolucional.)\n", "- **Visão global da imagem**: o mecanismo de atenção deixa cada região da imagem \"conversar\" com todas as outras, o que ajuda em cenas com muitos objetos ou com oclusão.\n", "\n", "O ponto fraco do DETR original era a lentidão e a dificuldade de treino. O **RF-DETR**, da [Roboflow](https://roboflow.com/), é uma família recente que resolve isso: parte de um backbone **DINOv2** pré-treinado, roda em **tempo real** e está entre os detectores mais precisos no COCO, com a vantagem de transferir muito bem para domínios específicos (justamente o cenário de quem anota o próprio dataset, como no exercício anterior).\n", "\n", "```{tip}\n", "YOLO e RF-DETR resolvem a mesma tarefa por caminhos opostos. O YOLO é uma CNN refinada por anos de engenharia incremental em busca de velocidade; o RF-DETR parte de um Transformer com visão global da cena. Na prática, vale testar os dois no seu problema: o YOLO costuma liderar em latência bruta, enquanto o RF-DETR tende a brilhar em precisão e na transferência para domínios fora do COCO.\n", "```\n", "\n", "### Instalação e Inferência\n", "\n", "Para comparar diretamente com o YOLO, vamos rodar o RF-DETR na **mesma imagem** (`bus.jpg`) usada no exemplo de detecção. Primeiro, a instalação:\n", "\n", "```bash\n", "!pip install rfdetr supervision\n", "```\n", "\n", "A inferência devolve um objeto `Detections` da biblioteca [supervision](https://supervision.roboflow.com/), que também cuida da anotação visual. A lista `COCO_CLASSES` traduz o índice numérico de cada classe para o nome legível:\n", "\n", "```python\n", "from rfdetr import RFDETRMedium\n", "from rfdetr.util.coco_classes import COCO_CLASSES\n", "import supervision as sv\n", "import matplotlib.pyplot as plt\n", "from PIL import Image\n", "from urllib.request import urlopen\n", "import numpy as np\n", "\n", "# Variantes: RFDETRNano, RFDETRSmall, RFDETRMedium, RFDETRBase, RFDETRLarge\n", "# (de mais rápido a mais preciso). Aqui usamos a Medium, pré-treinada no COCO.\n", "model = RFDETRMedium()\n", "\n", "# Mesma imagem do exemplo de detecção com YOLO (carregada direto do repositório oficial)\n", "url = \"https://raw.githubusercontent.com/ultralytics/ultralytics/main/ultralytics/assets/bus.jpg\"\n", "imagem = Image.open(urlopen(url)).convert(\"RGB\")\n", "\n", "# Inferência: só mantém detecções com confiança >= 0.5\n", "deteccoes = model.predict(imagem, threshold=0.5)\n", "\n", "# Monta os rótulos \"classe confiança\" e desenha caixas + textos sobre a imagem\n", "rotulos = [f\"{COCO_CLASSES[c]} {p:.2f}\"\n", " for c, p in zip(deteccoes.class_id, deteccoes.confidence)]\n", "anotada = sv.BoxAnnotator().annotate(np.array(imagem).copy(), deteccoes)\n", "anotada = sv.LabelAnnotator(smart_position=True).annotate(anotada, deteccoes, rotulos)\n", "\n", "# Exibe original vs. resultado, lado a lado\n", "fig, axs = plt.subplots(1, 2, figsize=(15, 7))\n", "axs[0].imshow(imagem); axs[0].set_title(\"Imagem Original\"); axs[0].axis(\"off\")\n", "axs[1].imshow(anotada); axs[1].set_title(\"Resultado da Detecção (RF-DETR)\"); axs[1].axis(\"off\")\n", "plt.tight_layout()\n", "plt.show()\n", "```\n", "\n", "
\n", " \"Resultado\n", "
\n", "
\n", "\n", "Na mesma cena, o RF-DETR encontra o ônibus e as pessoas com confiança alta, assim como o YOLO26. A diferença está por baixo: as caixas saem diretamente da previsão de conjunto do Transformer, sem nenhuma etapa de NMS. Vale rodar os dois modelos nas suas próprias imagens e comparar tanto a qualidade das detecções quanto o tempo de inferência." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Segment Anything (SAM)\n", "\n", "Em visão computacional, o **SAM (Segment Anything)**, desenvolvido pela Meta AI, tornou-se o **modelo de base** para segmentação. A ideia segue o mesmo princípio que consagrou os LLMs no processamento de linguagem natural: um único modelo, treinado em grande escala, capaz de segmentar praticamente qualquer objeto em qualquer imagem a partir de prompts simples: um clique, uma caixa, uma máscara aproximada ou, nas versões mais recentes, uma descrição em texto.\n", "\n", "A versão atual, **SAM 3**, estende essa capacidade para vídeos e introduz segmentação aberta por vocabulário (*open-vocabulary*), permitindo descrever em linguagem natural aquilo que se quer segmentar. A Meta disponibiliza um playground oficial onde é possível experimentar o modelo diretamente no navegador, sem instalação:\n", "\n", "**Playground oficial do SAM 3:** \n", "\n", "### Rodando o SAM 3 no Colab\n", "\n", "Além do playground, o SAM 3 está disponível na biblioteca `transformers` da Hugging Face, o que permite rodá-lo direto no Colab. Vamos usar o recurso central do modelo, a **segmentação por conceito** (*Promptable Concept Segmentation*): em vez de um clique ou uma caixa, passamos uma palavra (um substantivo) e o SAM 3 segmenta **todas as instâncias** daquele conceito na imagem. Para fechar o capítulo de forma coerente, vamos segmentar o conceito `person` na mesma `bus.jpg` dos exemplos de detecção, fazendo a transição natural de detecção (caixas) para segmentação (máscaras).\n", "\n", "```{note}\n", "O `facebook/sam3` é um modelo *gated* (de acesso controlado). Antes de rodar: (1) abra a [página do modelo no Hugging Face](https://huggingface.co/facebook/sam3), faça login e aceite os termos de uso (para os modelos SAM a liberação costuma ser imediata); (2) autentique o Colab **uma vez** executando o `notebook_login()` da célula abaixo e colando um token de acesso (gerado em *Settings → Access Tokens* no Hugging Face).\n", "```\n", "\n", "Primeiro, instalamos as bibliotecas (o SAM 3 exige uma versão recente do `transformers`):\n", "\n", "```bash\n", "!pip install -U transformers supervision\n", "```\n", "\n", "Em seguida, a autenticação no Hugging Face:\n", "\n", "```python\n", "from huggingface_hub import notebook_login\n", "\n", "notebook_login()\n", "```\n", "\n", "Agora a inferência. O `Sam3Processor` prepara a imagem e o texto; o modelo devolve as máscaras, que pós-processamos no tamanho original da imagem e anotamos com a biblioteca `supervision` (a mesma usada no RF-DETR):\n", "\n", "```python\n", "import torch\n", "import numpy as np\n", "import supervision as sv\n", "import matplotlib.pyplot as plt\n", "from PIL import Image\n", "from urllib.request import urlopen\n", "from transformers import Sam3Model, Sam3Processor\n", "\n", "# Carrega o SAM 3 (modelo gated: requer acesso aprovado + login acima)\n", "device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n", "model = Sam3Model.from_pretrained(\"facebook/sam3\").to(device)\n", "processor = Sam3Processor.from_pretrained(\"facebook/sam3\")\n", "\n", "# Mesma imagem dos exemplos de detecção\n", "url = \"https://raw.githubusercontent.com/ultralytics/ultralytics/main/ultralytics/assets/bus.jpg\"\n", "imagem = Image.open(urlopen(url)).convert(\"RGB\")\n", "\n", "# Prompt de conceito: segmenta TODAS as instâncias de \"person\"\n", "inputs = processor(images=imagem, text=\"person\", return_tensors=\"pt\").to(device)\n", "with torch.no_grad():\n", " outputs = model(**inputs)\n", "\n", "# Pós-processamento: máscaras, caixas e scores no tamanho original da imagem\n", "resultado = processor.post_process_instance_segmentation(\n", " outputs,\n", " threshold=0.5,\n", " mask_threshold=0.5,\n", " target_sizes=inputs.get(\"original_sizes\").tolist(),\n", ")[0]\n", "print(f\"{len(resultado['masks'])} instâncias de 'person' encontradas\")\n", "\n", "# Converte para o formato da supervision e desenha as máscaras + rótulos\n", "deteccoes = sv.Detections(\n", " xyxy=resultado[\"boxes\"].cpu().numpy(),\n", " mask=resultado[\"masks\"].cpu().numpy().astype(bool),\n", " confidence=resultado[\"scores\"].cpu().numpy(),\n", ")\n", "rotulos = [f\"person {p:.2f}\" for p in deteccoes.confidence]\n", "anotada = sv.MaskAnnotator(color_lookup=sv.ColorLookup.INDEX).annotate(np.array(imagem).copy(), deteccoes)\n", "anotada = sv.LabelAnnotator(smart_position=True, color_lookup=sv.ColorLookup.INDEX).annotate(anotada, deteccoes, rotulos)\n", "\n", "# Exibe original vs. resultado, lado a lado\n", "fig, axs = plt.subplots(1, 2, figsize=(15, 7))\n", "axs[0].imshow(imagem); axs[0].set_title(\"Imagem Original\"); axs[0].axis(\"off\")\n", "axs[1].imshow(anotada); axs[1].set_title(\"Segmentação por Conceito (SAM 3)\"); axs[1].axis(\"off\")\n", "plt.tight_layout()\n", "plt.show()\n", "```\n", "\n", "
\n", " \"Segmentação\n", "
\n", "
\n", "\n", "Ao executar no Colab (com GPU), o SAM 3 devolve uma máscara para cada pessoa da cena, e não apenas a caixa que a envolve. É a diferença entre **detecção** (onde está o objeto) e **segmentação** (quais pixels são o objeto), agora a partir de um simples prompt de texto, sem nenhuma classe pré-definida.\n", "\n", "```{tip}\n", "Como o SAM 3 trabalha com *vocabulário aberto* (*open-vocabulary*), basta trocar o texto em `text=\"...\"` para segmentar qualquer outro conceito, sem treinar nada. Experimente prompts como `\"bus\"`, `\"backpack\"` ou `\"shoe\"`, ou frases curtas com adjetivo, como `\"blue bus\"`.\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Localização por Linguagem: LocateAnything (NVIDIA)\n", "\n", "Os detectores que vimos até aqui (YOLO26, RF-DETR) localizam objetos de um conjunto de classes, e o SAM 3 segmenta instâncias de um conceito. O **LocateAnything**, da NVIDIA, dá um passo adicional: é um **modelo de visão-linguagem** (*Vision-Language Model*, VLM) que localiza objetos a partir de uma **descrição em linguagem natural** qualquer, incluindo *referring expressions* (frases que apontam para uma instância específica, como \"a pessoa de casaco branco\").\n", "\n", "Por baixo, ele combina um codificador visual (Moon-ViT) com um decodificador de linguagem (Qwen2.5) e introduz o **Parallel Box Decoding (PBD)**: em vez de gerar as coordenadas das caixas token a token, prevê as caixas em paralelo, ganhando velocidade e precisão em IoU alto. O modelo cobre desde detecção genérica até *grounding* de elementos de interface (GUI), OCR e leitura de documentos, e foi treinado em larga escala (12M de imagens, 138M de consultas).\n", "\n", "```{note}\n", "O `nvidia/LocateAnything-3B` **não é gated** (download livre), mas é distribuído sob a *NVIDIA License for non-commercial use*: permitido apenas para pesquisa acadêmica e fins não comerciais. Por ser um modelo de 3 bilhões de parâmetros, rode o exemplo no Colab **com GPU**.\n", "```\n", "\n", "A instalação segue as versões fixadas pelo cartão do modelo (repare no `transformers==4.57.1`; no Colab, pode ser necessário reiniciar a sessão em *Runtime → Restart session* depois de instalar):\n", "\n", "```bash\n", "!pip install opencv-python-headless==4.11.0.86 transformers==4.57.1 numpy==1.25.0 Pillow==11.1.0 peft torchvision decord==0.6.0 lmdb==1.7.5\n", "```\n", "\n", "O código abaixo organiza a inferência numa pequena classe: ela carrega o modelo uma vez e, a cada consulta, monta a mensagem (imagem + texto), aplica o *chat template*, gera a resposta e extrai as caixas. As coordenadas saem normalizadas no intervalo [0, 1000] e são convertidas para pixels:\n", "\n", "```python\n", "import re\n", "import torch\n", "from transformers import AutoModel, AutoTokenizer, AutoProcessor\n", "\n", "\n", "class LocateAnythingWorker:\n", " \"\"\"Carrega o modelo uma vez e responde a consultas de localização.\"\"\"\n", "\n", " def __init__(self, model_path, device=\"cuda\", dtype=torch.bfloat16):\n", " self.device, self.dtype = device, dtype\n", " self.tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)\n", " self.processor = AutoProcessor.from_pretrained(model_path, trust_remote_code=True)\n", " self.model = AutoModel.from_pretrained(\n", " model_path, torch_dtype=dtype, trust_remote_code=True,\n", " ).to(device).eval()\n", "\n", " @torch.no_grad()\n", " def predict(self, image, question, max_new_tokens=2048):\n", " # 1) Monta a mensagem com imagem + texto\n", " messages = [{\"role\": \"user\", \"content\": [\n", " {\"type\": \"image\", \"image\": image},\n", " {\"type\": \"text\", \"text\": question},\n", " ]}]\n", " # 2) Aplica o chat template e processa a imagem\n", " text = self.processor.py_apply_chat_template(\n", " messages, tokenize=False, add_generation_prompt=True)\n", " images, videos = self.processor.process_vision_info(messages)\n", " inputs = self.processor(\n", " text=[text], images=images, videos=videos, return_tensors=\"pt\"\n", " ).to(self.device)\n", " # 3) Gera a resposta (generation_mode=\"hybrid\" equilibra velocidade e precisão)\n", " resposta = self.model.generate(\n", " pixel_values=inputs[\"pixel_values\"].to(self.dtype),\n", " input_ids=inputs[\"input_ids\"],\n", " attention_mask=inputs[\"attention_mask\"],\n", " image_grid_hws=inputs.get(\"image_grid_hws\", None),\n", " tokenizer=self.tokenizer,\n", " max_new_tokens=max_new_tokens,\n", " use_cache=True,\n", " generation_mode=\"hybrid\",\n", " temperature=0.7, do_sample=True, top_p=0.9, repetition_penalty=1.1,\n", " )\n", " return resposta[0] if isinstance(resposta, tuple) else resposta\n", "\n", " @staticmethod\n", " def parse_boxes(answer, image_width, image_height):\n", " \"\"\"Extrai as caixas ... e converte de [0, 1000] para pixels.\"\"\"\n", " boxes = []\n", " for m in re.finditer(r\"<(\\d+)><(\\d+)><(\\d+)><(\\d+)>\", answer):\n", " x1, y1, x2, y2 = [int(g) for g in m.groups()]\n", " boxes.append([x1 / 1000 * image_width, y1 / 1000 * image_height,\n", " x2 / 1000 * image_width, y2 / 1000 * image_height])\n", " return boxes\n", "```\n", "\n", "Com a classe pronta, fazemos uma consulta por *referring expression* na mesma `bus.jpg`: em vez de \"todas as pessoas\", pedimos **uma** pessoa específica pela descrição. As caixas são desenhadas com a `supervision` (a mesma lib do RF-DETR):\n", "\n", "```python\n", "import numpy as np\n", "import supervision as sv\n", "import matplotlib.pyplot as plt\n", "from PIL import Image\n", "from urllib.request import urlopen\n", "\n", "worker = LocateAnythingWorker(\"nvidia/LocateAnything-3B\")\n", "\n", "# Mesma imagem dos exemplos anteriores\n", "url = \"https://raw.githubusercontent.com/ultralytics/ultralytics/main/ultralytics/assets/bus.jpg\"\n", "imagem = Image.open(urlopen(url)).convert(\"RGB\")\n", "\n", "# Referring expression: aponta para UMA instância pela descrição\n", "consulta = \"Locate the person wearing a white coat.\"\n", "resposta = worker.predict(imagem, consulta)\n", "\n", "# Converte a resposta em caixas (em pixels) e desenha\n", "largura, altura = imagem.size\n", "caixas = LocateAnythingWorker.parse_boxes(resposta, largura, altura)\n", "deteccoes = sv.Detections(xyxy=np.array(caixas, dtype=float).reshape(-1, 4))\n", "anotada = sv.BoxAnnotator(color_lookup=sv.ColorLookup.INDEX).annotate(\n", " np.array(imagem).copy(), deteccoes)\n", "\n", "# Exibe original vs. resultado, lado a lado\n", "fig, axs = plt.subplots(1, 2, figsize=(15, 7))\n", "axs[0].imshow(imagem); axs[0].set_title(\"Imagem Original\"); axs[0].axis(\"off\")\n", "axs[1].imshow(anotada); axs[1].set_title(\"Localização por Linguagem (LocateAnything)\"); axs[1].axis(\"off\")\n", "plt.tight_layout()\n", "plt.show()\n", "```\n", "\n", "Ao executar no Colab, o LocateAnything desenha a caixa apenas sobre a pessoa que corresponde à descrição, ignorando as demais. É a diferença em relação aos detectores anteriores: aqui a \"classe\" é uma frase livre, e o modelo entende a quem ela se refere.\n", "\n", "```{tip}\n", "A consulta é texto livre: troque a frase em `consulta` para localizar outras coisas, como `\"Locate all the people.\"` (detecção de todas as instâncias), `\"Locate the blue bus.\"`, ou mesmo elementos de interface e texto em capturas de tela. É o mesmo modelo, sem treinar nada.\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Referências e Conteúdo Extra\n", "\n", "- **Vídeo**\n", " - [How computers learn to recognize objects instantly](https://youtu.be/Cgxsv1riJhI?si=uaMiD3RmEiJO-oBT)\n", "- **Documentação**\n", " - [YOLO26 - Ultralytics Docs (PT)](https://docs.ultralytics.com/pt/models/yolo26/)\n", " - [RF-DETR - Roboflow (GitHub)](https://github.com/roboflow/rf-detr)\n", " - [DETR - End-to-End Object Detection with Transformers (Meta AI)](https://github.com/facebookresearch/detr)\n", " - [Segment Anything - Meta AI](https://segment-anything.com/)\n", " - [SAM 3 - facebook/sam3 (Hugging Face)](https://huggingface.co/facebook/sam3)\n", " - [SAM 3 - Transformers Docs](https://huggingface.co/docs/transformers/main/model_doc/sam3)\n", " - [LocateAnything - NVIDIA Research](https://research.nvidia.com/labs/lpr/locate-anything/)\n", " - [LocateAnything-3B (Hugging Face)](https://huggingface.co/nvidia/LocateAnything-3B)\n" ] } ], "metadata": { "colab": { "provenance": [], "toc_visible": true }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.20" } }, "nbformat": 4, "nbformat_minor": 4 }