YOLO (You Only Look Once) é um algoritmo de deep learning proposto em 2015 e publicado em 2016. Ele se destaca pela sua rapidez e eficiência para detectar objetos em tempo real.
A detecção de objetos é uma subdisciplina de visão computacional que envolve o reconhecimento de classes específicas de objetos e a identificação de suas localizações. Em postagens anteriores, já conhecemos um pouco da área em tarefas de reconhecimento e detecção facial e detecção de veículos.
YOLO é bastante popular para tarefas de detecção de objetos em função de sua velocidade, boa acurácia, alta capacidade de generalização e por ser open-source. Além disso, ele foi projetado para detecção de objetos em tempo real. Por isso, ele é adequado para aplicações como análise de vídeos e vigilância.
Desde sua primeira publicação, várias versões do YOLO foram desenvolvidas. Neste post, utilizaremos a versão mais atual, YOLOv8, lançada em 2023. A nova versão do YOLO realiza detecção de objetos, segmentação de imagens, estimativa de poses e tracking. Neste post, nos concentraremos na detecção de objetos.
YOLO
As várias versões do YOLO possuem redes neurais com arquiteturas diferentes. No entanto, todas as arquiteturas do YOLO são compostas por uma rede neural profunda (deep learning) com camadas convolucionais para extração de características seguidas por camadas totalmente conectadas (fully connected layers).
Linha do tempo das diversas versões do YOLO (fonte)
A ideia-chave por trás do YOLO é dividir a imagem de entrada em uma grade e, para cada célula da grade, prever caixas delimitadoras e probabilidades para classificação.
Ao contrário dos métodos tradicionais de detecção de objetos que usam vários estágios para localização e classificação de objetos, o YOLO processa toda a imagem de uma só vez. Ou seja, ele prevê caixas delimitadoras e probabilidades de classe para cada objeto em uma única passagem para frente (feedforward). A arquitetura de passagem única da YOLO permite que ele faça previsões rapidamente, o que o torna um algoritmo adequado para aplicações em tempo real.
Como o YOLO funciona?
O YOLO é treinado em conjuntos de dados rotulados, ou seja, ele tem aprendizagem supervisionada. O treinamento do YOLO otimiza os parâmetros do modelo para minimizar as diferenças entre caixas delimitadoras previstas e caixas delimitadoras corretas. Com o auxílio das coordenadas das caixas delimitadoras e das probabilidades de classe, o YOLO retorna não apenas a classe do objeto detectado, mas também a sua localização numa imagem.
Durante a detecção de objetos, o YOLO determina várias caixas delimitadoras para cada potencial alvo presente numa imagem.
Para selecionar apenas uma caixa por objeto, o YOLO usa o conceito de IoU (intersections over unions). IoU mede a sobreposição entre uma caixa delimitadora prevista e uma caixa delimitadora rotulada como correta. O valor de IoU é calculado como a razão entre a área de sobreposição entre essas duas caixas delimitadoras e a área total abrangida por sua união. Os resultados podem variar de 0 a 1. Quanto mais próximo de 1, melhor é o resultado.
Esquema para cálculo de IoU (a) e (b) para a seleção das caixas delimitadoras com melhores sobreposições (fonte)
Após calcular IoU, o YOLO usa Non-Maximal Suppression (NMS) para eliminar caixas delimitadoras sobrepostas. Algoritmos de detecção de objetos normalmente geram várias caixas delimitadoras ao redor do mesmo objeto com pontuações de confiança diferentes. NMS filtra as caixas delimitadoras redundantes mantendo apenas as mais precisas. Para mais detalhes, clique aqui, aqui e aqui.
Detecção de objetos com Python: bibliotecas
Para esse post, testaremos como usar o YOLOv8 para detecção de objetos. Usaremos as seguintes bibliotecas: PyTorch, torchvision, OpenCV e Ultralytics. As instalações podem ser feitas com pip. É recomendável instalar tudo em um ambiente virtual.
pip install opencv-python torch torchvision ultralytics
Após as instalações, crie um arquivo Python e importe os pacotes que serão usados. O PyTorch e torchvision rodam no background.
import os
import cv2
from ultralytics import YOLO
from ultralytics.utils.plotting import Annotator
Objetivos
YOLOv8 é um modelo pré-treinado. Ele pode ser usado para detecção e classificação de objetos sem nenhuma alteração. Porém, ele também pode ser retreinado para customizações. Portanto, primeiro, utilizaremos o YOLO sem nenhum treino adicional. Em seguida, faremos uma demonstração de como ele pode ser retreinado com dados customizados.
Detecção de objetos com YOLOv8
O YOLOv8 tem 5 modelos pré-treinados de diferentes tamanhos: n (nano), s (small), m (medium), l (large), x (extra-large). Para nossa primeira tarefa, usaremos o m. Selecione o modelo como mostrado abaixo. Assim que o código for executado pela primeira vez, o arquivo será baixado automaticamente.
model = YOLO("yolov8m.pt")
Também utilizaremos os rótulos do dataset já treinado, o COCO dataset (Common Object in Context). Esse conjunto de dados inclui 80 categorias de objetos que podem ser reconhecidas pelo YOLOv8. O link para o arquivo com os rótulos está aqui. Substitua no seu código a localização correta do seu arquivo após baixá-lo.
labels = open("data/coco.names").read().strip().split("\n")
Depois, precisamos selecionar uma imagem e abri-la com OpenCV para que o YOLO detecte seus objetos. No trecho de código mostrado abaixo, mude path_to_img para a localização de sua imagem.
path_name = "path_para_img"
image = cv2.imread(path_name)
A imagem que escolhemos esta abaixo com o link para seu download.
Em seguida, aplicamos o modelo na imagem para fazer predições. Copie o comando abaixo no seu arquivo. Salve tudo e execute o arquivo. Os resultados da detecção de objetos com YOLO serão impressos no terminal.
results = model(img1)
Veja abaixo nossos resultados. Eles incluem as classes identificadas e algumas informações adicionais sobre o tempo de computação.
0: 416x640 12 persons, 4 cars, 2 buss, 2 trucks, 7 traffic lights, 31.9ms
Speed: 1.8ms preprocess, 31.9ms inference, 80.4ms postprocess per image at shape (1, 3, 416, 640)
Detecção de objetos: desenhando as caixas delimitadoras
A maneira mais interessante de apresentar os resultados do YOLO para detecção de objetos é marcando os objetos diretamente na imagem. Para isso, usaremos os resultados retornados para desenhar as caixas delimitadoras com seus respectivos rótulos.
Dando continuação ao código escrito anteriormente, definiremos um for loop para desenhar as caixas. Dentro do loop, usaremos o Annotator importado no início do código.
Os resultados do YOLO possuem várias informações que podem ser usadas em diferentes aplicações (boxes, masks, keypoints e probs). Utilizaremos apenas boxes. Essa é a variável que contém as classificações e as coordenadas das caixas delimitadoras. Ela é usada com a função annotator para desenhar a caixa e identificar a classe de cada objeto. Salvaremos a imagem com as caixas delimitadoras logo após o loop (linha 10).
for r in results:
annotator = Annotator(img1)
boxes = r.boxes
for box in boxes:
b = box.xyxy[0] # coordenadas das caixas (top, left, bottom, right)
c = box.cls # classes
annotator.box_label(b, model.names[int(c)])
nome = os.path.basename(path_name)[:-4]
cv2.imwrite(nome + f"_yolo8m" + '.jpg', img1)
Após adicionar o trecho de código no seu arquivo, salve e execute o código completo. Abaixo mostramos o resultado que obtivemos.
Um exercício interessante é executar o mesmo código com outros modelos YOLOv8 para ver as diferenças entre eles.
Customizando um modelo pré-treinado
O YOLO também pode ser modificado para customizações. Demonstraremos como isso pode ser feito retreinando um de seus modelos com os conjunto de dados COCO128. Note que, no lugar do COCO128, você pode inserir seus próprios dados.
O treinamento é bastante simples. Crie um novo arquivo Python. No início do arquivo, precisamos importar os pacotes necessários.
import os
import cv2
from ultralytics import YOLO
from ultralytics.utils.plotting import Annotator
Depois, definimos um novo modelo, carregamos o modelo pré-treinado e transferimos seus pesos para o novo modelo. A documentação oficial com essas instruções pode ser lida aqui. Note que não é preciso baixar nenhum arquivo.
model = YOLO('yolov8n.yaml') # build a new model from YAML
model = YOLO('yolov8n.pt') # load a pretrained model (recommended for training)
model = YOLO('yolov8n.yaml').load('yolov8n.pt') # build from YAML and transfer weights
Em seguida, o modelo é treinado. Para o treinamento ser rápido, determinamos apenas 50 épocas. Mas sinta-se livre para variar os parâmetros que usamos. Para quem não tem familiaridade com essa terminologia, recomendamos ler a documentação do YOLO e do PyTorch.
results = model.train(data='coco128.yaml', epochs=50, imgsz=640)
Depois do treinamento, selecionamos uma nova imagem para classificação com o modelo que treinamos. Atualize o trecho de código mostrado abaixo para inserir a localização correta da sua imagem.
img_name = 'path_to_img'
img1 = cv2.imread(img_name)
results = model(img1)
A imagem que usamos pode ser baixada nesse link. Selecionamos o menor tamanho disponível.
Também copiamos no nosso código o for loop definido anteriormente para desenhar as caixas delimitadoras nos objetos classificados.
for r in results:
annotator = Annotator(img1)
boxes = r.boxes
for box in boxes:
b = box.xyxy[0] # coordenadas das caixas (top, left, bottom, right) format
c = box.cls # classes
annotator.box_label(b, model.names[int(c)])
nome = os.path.basename(img_name)[:-4]
cv2.imwrite(nome + f"_yolo8m" + '.jpg', img1)
O resultado da execução do código para a imagem que testamos é mostrado abaixo.
Conclusões
Neste post, fizemos uma introdução ao famoso algoritmo YOLO para detecção de objetos. Utilizamos sua mais nova versão, YOLOv8, que também tem capacidade para outras aplicações como reconhecimento de poses e tracking. Demonstramos como o YOLO é facilmente utilizado para detecção de objetos com Python e como é simples retreinar um de seus modelos para tarefas customizadas. Portanto, para os interessados, consulte a documentação oficial, busque tutoriais e se divirta.