A biblioteca Supervision é uma solução Python de alto desempenho e baixo código, projetada para simplificar tarefas de visão computacional. Sua principal finalidade é oferecer uma interface intuitiva e eficaz para o gerenciamento de conjuntos de dados e a visualização de resultados de modelos de detecção, segmentação e rastreamento.
Para utilizar o Supervision, é necessário ter o Python 3.8 ou superior. A instalação pode ser feita de duas formas:
- Para desenvolvimento com componentes de GUI (interface gráfica do usuário) que dependem do OpenCV para exibir imagens e vídeos:
pip install supervision[desktop]
- Para implantação de aplicações sem a necessidade de uma interface gráfica:
pip install supervision
É importante notar que a API da biblioteca pode sofrer alterações frequentes devido às suas atualizações contínuas.
import supervision as sv
# Exibe a versão atual da biblioteca Supervision
print(sv.__version__)
'0.19.0'
- Processamento de Diferentes Tarefas de Visão Computacional
1.1 Detecção e Segmentação de Objetos
1.1.1 Análise de Resultados
A classe sv.Detections é fundamental para a análise das saídas de modelos de detecção e segmentação. Ela oferece métodos convenientes para carregar resultados de diversas estruturas, incluindo:
from_ultralytics(para YOLOv8)from_detectron2(para Detectron2)from_mmdetection(para MMDetection)from_yolov5(para YOLOv5)from_sam(para Segment Anything Model)from_transformers(para HuggingFace Transformers)from_paddledet(para PaddleDetection)
A seguir, um exemplo prático utilizando um modelo YOLOv8 para analisar e visualizar detecções em uma imagem:
import cv2
import supervision as sv
from ultralytics import YOLO
import numpy as np
# Carregar o modelo YOLOv8 pré-treinado
modelo_yolo = YOLO("yolov8n.pt") # Ou "yolov8n-seg.pt" para segmentação
# Carregar imagem
caminho_imagem = "img/dog.png" # Assumindo que a imagem 'dog.png' existe
imagem_original = cv2.imread(caminho_imagem)
# Realizar inferência
resultados_raw = modelo_yolo(imagem_original, verbose=False)[0]
# Converter os resultados para o formato Detections do Supervision
deteccoes_sv = sv.Detections.from_ultralytics(resultados_raw)
# Exibir os resultados iniciais
print("Detalhes das detecções:\n", deteccoes_sv)
print("Número de objetos detectados:", len(deteccoes_sv))
# Acessar a primeira detecção
primeira_deteccao = deteccoes_sv[0]
print("\nPrimeira detecção:\n", primeira_deteccao)
# Calcular a área de cada caixa delimitadora
areas_caixas = deteccoes_sv.box_area
print("\nÁrea das caixas delimitadoras:", areas_caixas)
# Preparar anotadores para visualização
anotador_caixas = sv.BoundingBoxAnnotator()
anotador_rotulos = sv.LabelAnnotator()
# Gerar rótulos a partir dos IDs de classe
rotulos = [
modelo_yolo.model.names[id_classe]
for id_classe in deteccoes_sv.class_id
]
# Anotar a imagem
imagem_anotada = anotador_caixas.annotate(
scene=imagem_original.copy(), detections=deteccoes_sv)
imagem_anotada = anotador_rotulos.annotate(
scene=imagem_anotada, detections=deteccoes_sv, labels=rotulos)
# Exibir a imagem anotada (requer ambiente de desktop ou backend de visualização)
sv.plot_image(imagem_anotada)
Observa-se que, em alguns casos, pode haver múltiplas detecções para o mesmo objeto, como um carro sendo classificado como 'car' e 'truck'. Para consolidar essas detecções, pode-se aplicar a Supressão Não Máxima (NMS) agnóstica à classe:
# Aplicar NMS para remover detecções duplicadas do mesmo objeto
deteccoes_filtradas = deteccoes_sv.with_nms(threshold=0.5, class_agnostic=True)
print("Classes detectadas após NMS:")
for id_classe in deteccoes_filtradas.class_id:
print(modelo_yolo.model.names[id_classe])
1.1.2 Funções Auxiliares
O Supervision também oferece funções utilitárias para manipulação de caixas delimitadoras e máscaras:
Cálculo de Intersection over Union (IOU)
import supervision as sv
import numpy as np
# Definindo duas caixas delimitadoras no formato (x_min, y_min, x_max, y_max)
caixa_A = np.array([[50, 50, 150, 150]])
caixa_B = np.array([[100, 100, 200, 200]])
# Calcular o IOU entre as caixas
iou_resultado = sv.box_iou_batch(caixa_A, caixa_B)
print("IOU entre as caixas:", iou_resultado)
Cálculo de Non-Maximum Suppression (NMS)
import supervision as sv
import numpy as np
# Definindo caixas com scores: (x_min, y_min, x_max, y_max, score)
caixas_com_score = np.array([[50, 50, 150, 150, 0.2], [100, 100, 200, 200, 0.5]])
# Aplicar NMS e obter um array booleano indicando quais caixas preservar
indices_para_manter = sv.box_non_max_suppression(caixas_com_score, 0.1)
print("Índices a serem mantidos após NMS:", indices_para_manter)
Geração de Máscara a partir de Polígono
import supervision as sv
import numpy as np
# Definindo vértices de um polígono
vertices_poligono = np.array([(10, 20), (50, 10), (90, 20), (70, 50), (30, 50)])
# Gerar uma máscara a partir do polígono com resolução (largura, altura)
mascara_gerada = sv.polygon_to_mask(vertices_poligono, (100, 60))
# Visualizar a máscara (pixels brancos para o polígono, pretos para o fundo)
sv.plot_image(mascara_gerada)
# Para converter uma máscara de volta para polígonos, use sv.mask_to_polygons(mask)
Filtro de Polígonos por Área
import supervision as sv
import numpy as np
# Criar uma lista de polígonos de exemplo
poligono_1 = np.array([[0, 0], [0, 1], [1, 1], [1, 0]]) # Área 1
poligono_2 = np.array([[0, 0], [0, 2], [2, 2], [2, 0]]) # Área 4
poligono_3 = np.array([[0, 0], [0, 3], [3, 3], [3, 0]]) # Área 9
lista_poligonos = [poligono_1, poligono_2, poligono_3]
# Filtrar polígonos com área mínima de 3.0 e sem limite máximo
poligonos_filtrados = sv.filter_polygons_by_area(lista_poligonos, min_area=3.0, max_area=None)
print("Número de polígonos originais:", len(lista_poligonos))
print("Número de polígonos filtrados:", len(poligonos_filtrados))
Redimensionamento de Caixas Delimitadoras
import numpy as np
import supervision as sv
# Caixas delimitadoras no formato (x_min, y_min, x_max, y_max)
caixas_originais = np.array([[10, 10, 20, 20], [30, 30, 40, 40]])
# Fator de escala (maior que 1 para ampliar, menor que 1 para reduzir)
fator_escala = 1.2
# Redimensionar as caixas
caixas_redimensionadas = sv.scale_boxes(caixas_originais, fator_escala)
print("Caixas redimensionadas:\n", caixas_redimensionadas)
1.2 Rastreamento de Objetos
O Supervision integra o rastreador de objetos ByteTrack, que difere dos métodos baseados em Re-ID, pois se concentra na utilização das informações das caixas delimitadoras fornecidas pelo detector de objetos. A precisão e estabilidade do detector influenciam diretamente o desempenho do ByteTrack.
A classe sv.ByteTrack é inicializada com os seguintes parâmetros:
track_thresh(float, opcional, padrão 0.25): Limite de confiança da detecção para considerar uma pista.track_buffer(int, opcional, padrão 30): Número de quadros para manter uma pista perdida antes de removê-la.match_thresh(float, opcional, padrão 0.8): Limite para associar pistas a detecções.frame_rate(int, opcional, padrão 30): Taxa de quadros do vídeo.
Principais métodos de sv.ByteTrack:
reset(): Reinicia o estado interno do rastreador.update_with_detections(detections): Atualiza o rastreador com novas detecções e retorna as detecções atualizadas, incluindo IDs de rastreamento.
Exemplo de uso para rastreamento de objetos em um vídeo:
import supervision as sv
from ultralytics import YOLO
import numpy as np
# Carregar modelo de detecção
modelo_detector = YOLO("yolov8n.pt")
# Inicializar o rastreador ByteTrack
rastreador = sv.ByteTrack()
# Inicializar anotadores para caixas delimitadoras e rótulos
anotador_caixas_rastreio = sv.BoundingBoxAnnotator()
anotador_rotulos_rastreio = sv.LabelAnnotator()
# Função de callback para processar cada quadro do vídeo
def processar_quadro_rastreio(quadro: np.ndarray, indice: int) -> np.ndarray:
resultados = modelo_detector(quadro, verbose=False)[0]
# Converter resultados para Detections do Supervision
deteccoes = sv.Detections.from_ultralytics(resultados)
# Atualizar o rastreador com as detecções
deteccoes = rastreador.update_with_deteccoes(deteccoes)
# Gerar rótulos com os IDs de rastreamento
rotulos_rastreio = [f"ID #{track_id}" for track_id in deteccoes.tracker_id]
# Anotar o quadro
quadro_anotado = anotador_caixas_rastreio.annotate(scene=quadro.copy(), detections=deteccoes)
quadro_anotado = anotador_rotulos_rastreio.annotate(scene=quadro_anotado, detections=deteccoes, labels=rotulos_rastreio)
return quadro_anotado
# Processar um vídeo de exemplo
sv.process_video(
source_path="https://media.roboflow.com/supervision/video-examples/people-walking.mp4",
target_path="output_rastreamento.mp4",
callback=processar_quadro_rastreio
)
O exemplo utiliza yolov8n.pt; para maior estabilidade no rastreamento, modelos de detecção mais robustos são recomendados.
1.3 Classificação de Imagens
Para classificação de imagens, o Supervision oferece suporte limitado para resultados de modelos como CLIP, TIMM e YOLOv8, permitindo a extração dos resultados top-k e suas probabilidades.
import cv2
from ultralytics import YOLO
import supervision as sv
# Carregar imagem
imagem_gato = cv2.imread("img/cat.png") # Assumindo a imagem 'cat.png' existe
# Carregar modelo de classificação YOLOv8
modelo_classificacao = YOLO('yolov8n-cls.pt')
# Realizar inferência
saida_modelo = modelo_classificacao(imagem_gato, verbose=False)[0]
# Converter a saída para o formato Classifications do Supervision
classificacoes_sv = sv.Classifications.from_ultralytics(saida_modelo)
# Alternativamente, para outros modelos: sv.Classifications.from_clip() ou sv.Classifications.from_timm()
# Imprimir as 2 principais classificações (IDs de classe e probabilidades)
top_k_classes, top_k_probs = classificacoes_sv.get_top_k(k=2)
print("Top 2 classificações (IDs e Probabilidades):", top_k_classes, top_k_probs)
- Visualização de Dados e Funções de Apoio
2.1 Configurações de Cores
O Supervision oferece as classes sv.Color e sv.ColorPalette para definir e converter cores, facilitando a personalização visual.
import supervision as sv
# Cores pré-definidas
print("Cor ROBOFLOW:", sv.Color.ROBOFLOW)
# Obter valores BGR de uma cor
cor_amarela = sv.Color(r=255, g=255, b=0)
print("Amarelo em BGR:", cor_amarela.as_bgr())
# Obter valores RGB (já é o formato padrão, mas existe o método)
print("Amarelo em RGB:", cor_amarela.as_rgb())
# Obter representação hexadecimal
print("Amarelo em Hex:", cor_amarela.as_hex())
# Criar uma cor a partir de um valor hexadecimal
cor_magenta_from_hex = sv.Color.from_hex('#ff00ff')
print("Magenta de Hex:", cor_magenta_from_hex)
# Paletas de cores padrão
print("\nPaleta padrão:", sv.ColorPalette.DEFAULT)
# Outras paletas: sv.ColorPalette.ROBOFLOW, sv.ColorPalette.LEGACY
# Acessar cores por índice em uma paleta personalizada
paleta_custom = sv.ColorPalette.from_hex(['#ff0000', '#00ff00', '#0000ff']) # Vermelho, Verde, Azul
print("Segunda cor da paleta customizada (índice 1):", paleta_custom.by_idx(1))
# Criar paleta a partir de matplotlib
paleta_mpl = sv.ColorPalette.from_matplotlib('viridis', 4)
print("Paleta viridis (4 cores) de Matplotlib:", paleta_mpl)
2.2 Exemplos de Visualização de Resultados
O Supervision oferece uma rica coleção de anotadores para visualizar resultados, especialmente para detecção e rastreamento de objetos. Abaixo, exemplos dos principais estilos de anotação para caixas delimitadoras.
import cv2
import supervision as sv
from ultralytics import YOLO
import numpy as np
# Carregar modelo e imagem para demonstração
modelo_vis = YOLO("yolov8n.pt")
imagem_pessoas = cv2.imread("img/person.png") # Assumindo 'person.png' existe
# Realizar detecção e converter para Detections do Supervision
resultados_vis = modelo_vis(imagem_pessoas, verbose=False)[0]
deteccoes_vis = sv.Detections.from_ultralytics(resultados_vis)
print(f"Número de detecções na imagem de pessoas: {len(deteccoes_vis)}")
# Gerar rótulos para os anotadores
labels_vis = [
modelo_vis.model.names[class_id]
for class_id in deteccoes_vis.class_id
]
Anotação de Caixas Delimitadoras (BoundingBoxAnnotator)
# Configurar anotador de caixas padrão
# color_lookup: Estratégia de mapeamento de cores (INDEX, CLASS, TRACK)
anotador_retangular = sv.BoundingBoxAnnotator(
color=sv.ColorPalette.DEFAULT, thickness=2, color_lookup=sv.ColorLookup.CLASS
)
quadro_anotado_ret = anotador_retangular.annotate(
scene=imagem_pessoas.copy(), detections=deteccoes_vis
)
sv.plot_image(quadro_anotado_ret, (10, 10))
Anotação de Caixas com Cantos Arredondados (RoundBoxAnnotator)
# roundness: Porcentagem de arredondamento dos cantos
anotador_arredondado = sv.RoundBoxAnnotator(color_lookup=sv.ColorLookup.INDEX, roundness=0.6)
quadro_anotado_round = anotador_arredondado.annotate(
scene=imagem_pessoas.copy(), detections=deteccoes_vis
)
sv.plot_image(quadro_anotado_round, (10, 10))
Anotação de Cantos de Caixa (BoxCornerAnnotator)
# corner_length: Comprimento de cada segmento de canto
anotador_cantos = sv.BoxCornerAnnotator(corner_length=12, color=sv.Color(r=255, g=255, b=0))
quadro_anotado_cantos = anotador_cantos.annotate(
scene=imagem_pessoas.copy(), detections=deteccoes_vis
)
sv.plot_image(quadro_anotado_cantos, (10, 10))
Anotação com Máscara Colorida (ColorAnnotator)
# opacity: Opacidade da máscara colorida
anotador_mascara_cor = sv.ColorAnnotator(opacity=0.4)
quadro_anotado_mascara = anotador_mascara_cor.annotate(
scene=imagem_pessoas.copy(), detections=deteccoes_vis
)
sv.plot_image(quadro_anotado_mascara, (10, 10))
Anotação com Círculos (CircleAnnotator)
anotador_circulo = sv.CircleAnnotator(color=sv.Color(r=255, g=255, b=128))
quadro_anotado_circulo = anotador_circulo.annotate(
scene=imagem_pessoas.copy(), detections=deteccoes_vis
)
sv.plot_image(quadro_anotado_circulo, (10, 10))
Anotação com Pontos (DotAnnotator)
O DotAnnotator permite desenhar pontos em posições específicas das caixas delimitadoras. As posições disponíveis são:
for pos in sv.Position:
print(pos)
CENTER, CENTER_LEFT, CENTER_RIGHT, TOP_CENTER, TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_CENTER, BOTTOM_RIGHT, CENTER_OF_MASS.
anotador_ponto = sv.DotAnnotator(radius=4, position=sv.Position.BOTTOM_CENTER, color=sv.Color.ROBOFLOW)
quadro_anotado_ponto = anotador_ponto.annotate(
scene=imagem_pessoas.copy(), detections=deteccoes_vis
)
sv.plot_image(quadro_anotado_ponto, (10, 10))
Anotação com Triângulos (TriangleAnnotator)
# base/height: Largura e altura do triângulo
anotador_triangulo = sv.TriangleAnnotator(base=30, height=30, position=sv.Position.TOP_CENTER, color=sv.Color.BLUE)
quadro_anotado_tri = anotador_triangulo.annotate(
scene=imagem_pessoas.pessoas.copy(), detections=deteccoes_vis
)
sv.plot_image(quadro_anotado_tri, (10, 10))
Anotação com Elipses (EllipseAnnotator)
# start_angle/end_angle: Ângulo inicial e final da elipse
anotador_elipse = sv.EllipseAnnotator(start_angle=-45, end_angle=215, color=sv.Color.GREEN)
quadro_anotado_elipse = anotador_elipse.annotate(
scene=imagem_pessoas.copy(), detections=deteccoes_vis
)
sv.plot_image(quadro_anotado_elipse, (10, 10))
Anotação com Barra de Porcentagem (PercentageBarAnnotator)
# Exibe a confiança da detecção como uma barra
anotador_barra_perc = sv.PercentageBarAnnotator(
border_color=sv.Color(r=128, g=0, b=0),
position=sv.Position.BOTTOM_CENTER,
width=100,
height=20
)
quadro_anotado_barra = anotador_barra_perc.annotate(
scene=imagem_pessoas.copy(), detections=deteccoes_vis
)
sv.plot_image(quadro_anotado_barra, (10, 10))
Anotação de Rótulos de Texto (LabelAnnotator)
# Personalização completa do texto do rótulo
anotador_rotulo_custom = sv.LabelAnnotator(
color=sv.Color(r=255, g=255, b=255), # Cor de fundo do rótulo
text_color=sv.Color(r=128, g=0, b=128), # Cor do texto
text_scale=1.5, # Tamanho do texto
text_position=sv.Position.TOP_CENTER, # Posição do texto
text_thickness=2, # Espessura do texto
text_padding=8 # Preenchimento do texto
)
quadro_anotado_rotulo = anotador_rotulo_custom.annotate(
scene=imagem_pessoas.copy(), detections=deteccoes_vis, labels=labels_vis
)
sv.plot_image(quadro_anotado_rotulo, (10, 10))
Pixelização de Objetos (PixelateAnnotator)
# pixel_size: Tamanho do bloco de pixelização
anotador_pixelizar = sv.PixelateAnnotator(pixel_size=12)
quadro_pixelizado = anotador_pixelizar.annotate(
scene=imagem_pessoas.copy(), detections=deteccoes_vis
)
# Pode-se sobrepor outros anotadores, como os rótulos
quadro_pixelizado = anotador_rotulo_custom.annotate(
scene=quadro_pixelizado, detections=deteccoes_vis, labels=labels_vis
)
sv.plot_image(quadro_pixelizado, (10, 10))
2.3 Funções Auxiliares
2.3.1 Relacionadas a Vídeos
Leitura de Informações do Vídeo
import supervision as sv
# Obter informações básicas de um arquivo de vídeo
info_video = sv.VideoInfo.from_video_path(video_path="https://media.roboflow.com/supervision/video-examples/people-walking.mp4")
print("Informações do vídeo:", info_video)
Geração e Gravação de Quadros de Vídeo
import supervision as sv
from tqdm import tqdm
caminho_video_origem = "https://media.roboflow.com/supervision/video-examples/people-walking.mp4"
informacoes_video = sv.VideoInfo.from_video_path(caminho_video_origem)
# Gerador de quadros: stride define o intervalo de quadros, start/end definem o range
gerador_quadros = sv.get_video_frames_generator(source_path=caminho_video_origem, stride=10, start=0, end=100)
caminho_video_saida = "video_processado_out.mp4"
# Escrever os quadros processados em um novo arquivo de vídeo
with sv.VideoSink(target_path=caminho_video_saida, video_info=informacoes_video) as coletor_video:
for quadro_gerado in tqdm(gerador_quadros, total=informacoes_video.total_frames // 10): # Estimativa para tqdm
coletor_video.write_frame(frame=quadro_gerado)
print(f"Vídeo salvo em: {caminho_video_saida}")
Cálculo de FPS (Quadros por Segundo)
import supervision as sv
gerador_frames_fps = sv.get_video_frames_generator(source_path="https://media.roboflow.com/supervision/video-examples/people-walking.mp4")
monitor_fps = sv.FPSMonitor()
for frame_medicao in gerador_frames_fps:
monitor_fps.tick() # Registra o tempo atual
# Calcular e exibir o FPS médio
fps_medio = monitor_fps.fps
print("FPS médio calculado:", fps_medio)
2.3.2 Relacionadas a Imagens
Salvamento de Imagens
import supervision as sv
import cv2
import os
diretorio_saida_imagens = 'imagens_salvas_exemplo'
# Criar um ImageSink para salvar imagens sequencialmente
with sv.ImageSink(
target_dir_path=diretorio_saida_imagens,
overwrite=True, # Sobreescrever se o diretório já existe
image_name_pattern="imagem_seq_{:05d}.png" # Padrão de nome de arquivo
) as coletor_imagens:
# Gerar alguns quadros para salvar (usando um vídeo como fonte)
video_exemplo_path = "https://media.roboflow.com/supervision/video-examples/people-walking.mp4"
for idx, img_frame in enumerate(sv.get_video_frames_generator(source_path=video_exemplo_path, stride=50, start=0, end=200)):
coletor_imagens.save_image(image=img_frame)
if idx >= 3: # Salvar apenas algumas para o exemplo
break
print(f"Imagens salvas em: {diretorio_saida_imagens}")
Recorte de Imagens com Base em Caixas Delimitadoras
import supervision as sv
import cv2
from ultralytics import YOLO
import os
modelo_yolo_crop = YOLO("yolov8n.pt")
imagem_para_cortar = cv2.imread("img/person.png") # Assumindo 'person.png' existe
resultados_crop = modelo_yolo_crop(imagem_para_cortar, verbose=False)[0]
detecções_crop = sv.Detections.from_ultralytics(resultados_crop)
diretorio_recortes = 'recortes_objetos'
# Criar um ImageSink para salvar as imagens recortadas
with sv.ImageSink(target_dir_path=diretorio_recortes, overwrite=True) as coletor_recortes:
for bbox_coords in detecções_crop.xyxy:
# Recortar a imagem usando as coordenadas da caixa delimitadora
imagem_recortada = sv.crop_image(image=imagem_para_cortar, xyxy=bbox_coords)
coletor_recortes.save_image(image=imagem_recortada)
print(f"Imagens recortadas salvas em: {diretorio_recortes}")
2.4 Outras Funções
O Supervision oferece funcionalidades adicionais que não foram detalhadas aqui, como:
- Classes para conversão de diversos formatos de conjuntos de dados (detecção de objetos e classificação de imagens) - veja a documentação de datasets.
- Classes para cálculo de métricas de avaliação de detecção de objetos - veja a documentação de métricas.
- Classes para desenho de formas geométricas diversas - veja a documentação de draw utils.
- Ferramentas para Tarefas Práticas
3.1 Contagem de Objetos em Linhas
A classe sv.LineZone permite contar o número de objetos que cruzam uma linha predefinida. O mecanismo envolve detecção e rastreamento de objetos, verificando se o ponto central da caixa delimitadora cruza a linha.
import supervision as sv
from ultralytics import YOLO
import numpy as np
modelo_linha_zona = YOLO("yolov8n.pt")
rastreador_linha_zona = sv.ByteTrack()
caminho_video_linha_zona = "https://media.roboflow.com/supervision/video-examples/vehicles.mp4"
gerador_quadros_linha_zona = sv.get_video_frames_generator(caminho_video_linha_zona, start=0, end=500)
info_video_linha_zona = sv.VideoInfo.from_video_path(caminho_video_linha_zona)
largura = info_video_linha_zona.width
altura = info_video_linha_zona.height
# Definir a linha de contagem (do lado esquerdo para o direito, no meio da altura)
ponto_inicio_linha = sv.Point(x=0, y=int(altura / 2))
ponto_fim_linha = sv.Point(x=largura, y=int(altura / 2))
# Inicializar o contador de linha
contador_de_linha = sv.LineZone(start=ponto_inicio_linha, end=ponto_fim_linha)
# Inicializar anotadores
anotador_tracos = sv.TraceAnnotator()
anotador_rotulos_linha = sv.LabelAnnotator(text_scale=2, text_color=sv.Color.BLACK)
anotador_zona_linha = sv.LineZoneAnnotator(thickness=4, text_thickness=4, text_scale=1)
with sv.ImageSink(target_dir_path='output_contagem_linha', overwrite=True, image_name_pattern="frame_linha_{:05d}.png") as sink_linha_zona:
for quadro_atual in gerador_quadros_linha_zona:
resultado_frame = modelo_linha_zona(quadro_atual, verbose=False)[0]
deteccoes_frame = sv.Detections.from_ultralytics(resultado_frame)
# Atualizar o rastreador
deteccoes_frame = rastreador_linha_zona.update_with_deteccoes(deteccoes_frame)
# Ativar o contador de linha
cruzou_entrada, cruzou_saida = contador_de_linha.trigger(deteccoes_frame)
# Gerar rótulos para as detecções (ID de rastreamento e nome da classe)
rotulos_detalhes = [
f"#{track_id} {modelo_linha_zona.model.names[class_id]}"
for class_id, track_id in zip(deteccoes_frame.class_id, deteccoes_frame.tracker_id)
]
# Anotar o quadro
quadro_anotado_linha = anotador_tracos.annotate(scene=quadro_atual.copy(), detections=deteccoes_frame)
quadro_anotado_linha = anotador_rotulos_linha.annotate(scene=quadro_anotado_linha, detections=deteccoes_frame, labels=rotulos_detalhes)
quadro_anotado_linha = anotador_zona_linha.annotate(quadro_anotado_linha, line_counter=contador_de_linha)
# sv.plot_image(quadro_anotado_linha) # Descomentar para visualizar em tempo real
sink_linha_zona.save_image(image=quadro_anotado_linha)
# Imprimir as contagens finais
print(f"Total de objetos que entraram: {contador_de_linha.in_count}")
print(f"Total de objetos que saíram: {contador_de_linha.out_count}")
3.2 Detecção e Rastreamento em Regiões Específicas
A classe sv.PolygonZone permite monitorar a presença e a contagem de objetos dentro de áreas poligonais predefinidas. É útil para analisar tráfego em interseções, pessoas em zonas restritas, etc.
import numpy as np
import supervision as sv
from ultralytics import YOLO
modelo_zona_poligono = YOLO('yolov8n.pt')
caminho_video_zona = "https://media.roboflow.com/supervision/video-examples/vehicles-2.mp4"
info_video_zona = sv.VideoInfo.from_video_path(caminho_video_zona)
print("Informações do vídeo para zona poligonal:", info_video_zona)
gerador_quadros_zona = sv.get_video_frames_generator(caminho_video_zona)
# Definir polígonos para as zonas de interesse
poligonos_zonas = [
np.array([
[718, 595],[927, 592],[851, 1062],[42, 1059]
]),
np.array([
[987, 595],[1199, 595],[1893, 1056],[1015, 1062]
])
]
# Definir paleta de cores para as zonas e caixas
paleta_cores_zonas = sv.ColorPalette.DEFAULT
# Inicializar objetos PolygonZone para cada polígono
objetos_zona = [
sv.PolygonZone(
polygon=poligono,
frame_resolution_wh=info_video_zona.resolution_wh
)
for poligono in poligonos_zonas
]
# Inicializar anotadores de zona para cada objeto_zona
anotadores_zona_pol = [
sv.PolygonZoneAnnotator(
zone=zona,
color=paleta_cores_zonas.by_idx(idx_zona),
thickness=4,
text_thickness=8,
text_scale=4,
display_in_zone_count=True # Exibir a contagem de objetos na zona
)
for idx_zona, zona in enumerate(objetos_zona)
]
# Inicializar anotadores de caixa para cada zona
anotadores_caixa_zona = [
sv.BoxAnnotator(
color=paleta_cores_zonas.by_idx(idx_caixa),
thickness=4,
text_thickness=4,
text_scale=2
)
for idx_caixa in range(len(poligonos_zonas))
]
with sv.ImageSink(target_dir_path='output_zona_poligonal', overwrite=True, image_name_pattern="frame_zona_{:05d}.png") as sink_zona:
for quadro_processar in gerador_quadros_zona:
# Realizar inferência com YOLOv8, ajustando o tamanho da imagem para melhor precisão
resultados_zona = modelo_zona_poligono(quadro_processar, imgsz=1280, verbose=False)[0]
detecções_zona = sv.Detections.from_ultralytics(resultados_zona)
quadro_com_zonas = quadro_processar.copy()
for zona_obj, anot_zona, anot_caixa in zip(objetos_zona, anotadores_zona_pol, anotadores_caixa_zona):
# Identificar quais detecções estão dentro da zona
mascara_na_zona = zona_obj.trigger(detections=detecções_zona)
detecções_na_zona = detecções_zona[mascara_na_zona]
# Anotar apenas os objetos dentro da zona
quadro_com_zonas = anot_caixa.annotate(scene=quadro_com_zonas, detections=detecções_na_zona)
quadro_com_zonas = anot_zona.annotate(scene=quadro_com_zonas)
# sv.plot_image(quadro_com_zonas, (16, 16)) # Descomentar para visualizar
sink_zona.save_image(image=quadro_com_zonas)
3.3 Inferência com Fatiamento (Slicing Aided Hyper Inference - SAHI)
Para otimizar a detecção de objetos pequenos, o Supervision oferece suporte à inferência com fatiamento (SAHI). Esta técnica divide a imagem em várias fatias, executa a inferência em cada fatia e na imagem completa, e então combina e filtra os resultados com NMS.
import cv2
import supervision as sv
from ultralytics import YOLO
import numpy as np
modelo_sahi = YOLO("yolov8n.pt")
imagem_para_sahi = cv2.imread("img/person.png") # Assumindo 'person.png' existe
# Detecção original sem fatiamento
resultados_sem_sahi = modelo_sahi(imagem_para_sahi, verbose=False)[0]
detecções_sem_sahi = sv.Detections.from_ultralytics(resultados_sem_sahi)
print(f"Detecções antes do SAHI: {len(detecções_sem_sahi)}")
# Função de callback que será aplicada a cada fatia da imagem
def inferencia_por_fatia(imagem_fatiada: np.ndarray) -> sv.Detections:
resultado_fatia = modelo_sahi(imagem_fatiada, verbose=False)[0]
return sv.Detections.from_ultralytics(resultado_fatia)
# Configurar o objeto InferenceSlicer
fatiador_inferencia = sv.InferenceSlicer(
callback=inferencia_por_fatia, # Função para processar cada fatia
slice_wh=(320, 320), # Largura e altura das fatias
overlap_ratio_wh=(0.3, 0.3), # Taxa de sobreposição entre fatias
iou_threshold=0.4, # Limite de IOU para NMS na combinação
thread_workers=4 # Número de threads para processamento paralelo
)
# Executar a inferência com fatiamento
detecções_com_sahi = fatiador_inferencia(imagem_para_sahi)
print(f"Detecções após o SAHI: {len(detecções_com_sahi)}")
3.4 Suavização de Trajetórias
A classe sv.DetectionsSmoother é uma ferramenta útil para suavizar as trajetórias de rastreamento de objetos em vídeos. Ela mantém um histórico das detecções para cada objeto rastreado e utiliza esse histórico para fornecer previsões mais suaves e consistentes.
import supervision as sv
from ultralytics import YOLO
import numpy as np
caminho_video_suavizacao = "https://media.roboflow.com/supervision/video-examples/grocery-store.mp4"
info_video_suavizacao = sv.VideoInfo.from_video_path(video_path=caminho_video_suavizacao)
gerador_quadros_suavizacao = sv.get_video_frames_generator(source_path=caminho_video_suavizacao)
modelo_suavizacao = YOLO("yolov8n.pt")
rastreador_suavizacao = sv.ByteTrack(frame_rate=info_video_suavizacao.fps)
# Inicializar o suavizador de detecções
# length: Número máximo de quadros a serem considerados para suavização de cada trajetória
suavizador = sv.DetectionsSmoother(length=4)
anotador_suavizacao = sv.BoundingBoxAnnotator()
with sv.VideoSink("output_suavizacao.mp4", video_info=info_video_suavizacao) as coletor_suavizacao:
for quadro_process_suave in gerador_quadros_suavizacao:
resultado_suave = modelo_suavizacao(quadro_process_suave, verbose=False)[0]
detecções_suave = sv.Detections.from_ultralytics(resultado_suave)
# Atualizar o rastreador
detecções_suave = rastreador_suavizacao.update_with_deteccoes(detecções_suave)
# Aplicar suavização às trajetórias de rastreamento
detecções_suave = suavizador.update_with_deteccoes(detecções_suave)
quadro_anotado_suave = anotador_suavizacao.annotate(quadro_process_suave.copy(), detecções_suave)
# sv.plot_image(quadro_anotado_suave, (16, 16)) # Descomentar para visualizar
coletor_suavizacao.write_frame(quadro_anotado_suave)