Câmpus Itajaí - Departamento de Engenharia Elétrica - Bacharelado em Engenharia Elétrica

Rastreamento de veículos em vídeo utilizando algoritmos de Deep Learning

Alunos: Luis Davi Kenig Paganella e Pedro Augusto dos Reis.

Orientador: Ênio dos Santos Silva.

Desenvolvimento

Existem inúmeras formas fazer a detecção de veículos em uma imagem de forma automática, mas como o trabalho de Monteiro e Albuquerque (2022) já mostrou resultados satisfatórios para a detecção de veículos com a YOLO versão 3 treinada pelo banco de dados COCO. Além disso, outros trabalhos, como o de Brostrom (2022), também utilizam versões da YOLO integrada à um rastreador. Por isso para as aplicações de detecção, foi escolhido utilizar a YOLO.

Diversos trabalhos citam diferentes tipos de rastreadores, mas não definem quais cenários são mais apropriados para cada tipo de rastreador. Visando comparar qual o desempenho de cada um para o rastreamento de veículos em vias urbanas, não foi descartado nenhum modelo de rastreador apresentado na revisão literária.

O primeiro modelo de rastreador escolhido para aplicar foram os já desenvolvidos na biblioteca OpenCV. O Código descrito por Mallick (2018) foi adaptado para receber as coordenadas dos objetos detectados automaticamente da YOLO. Para isso, baseou-se no código de Monteiro e Albuquerque (2022), onde foi alterado os dados de saída da função findObject() para retornar a lista de coordenadas dos veículos detectados no primeiro frame.

Após isso, foi necessário criar uma lista, chamada de cada_tracker, com o mesmo número de elementos que a quantidade de coordenadas. Cada elemento dessa lista é uma string composta por a e um número, iniciando em zero e sendo incrementado conforme o laço for avança. Isso foi necessário pois diferentemente da forma que Mallick (2018) descreve seu código, a função Multitracker.add(), que recebe as informações de cada objeto que deve ser rastreado, só associou corretamente cada rastreador quando existe uma variável única definida como o rastreador escolhido.

Por isso, foi atribuído a cada elemento da lista cada_tracker, um dos rastreadores disponíveis da biblioteca OpenCV. Foi escolhido o rastreador KCF porque ele é baseado na correlação de filtros de Kernel. Esse método também é aplicado em outros modelos de rastreadores mais desenvolvidos, como deepSort. Isso significa que pode apresentar um desempenho melhor para essa tarefa.

A função para atualizar os objetos rastreadores seguiu uma lógica semelhante ao código de Mallick (2018), como pode ser visualizado nas imagens a seguir:

O resultado dessa primeira aplicação pode ser vista no vídeo a seguir. O Rastreador consegue acompanhar alguns veículos até que saiam da área da câmera. Outros veículos o rastreador deixa de detectar durante oclusões e não os rastreia novamente. Além disso, como foi atribuído apenas as coordenadas das detecções do primeiro frame, é necessário desenvolver um método que insira no rastreador os próximos veículos que surgirem em vídeo.

O principal obstáculo para essa tarefa é que a YOLO detecta veículos em todo o frame, incluindo os que já estão sendo rastreados pelo Multitracker. Isso significa que é necessário separar as coordenadas já acompanhadas pelo Tracker quando associar as novas coordenadas encontradas pela YOLO. Então criou-se uns laços de verificação, em que caso uma coordenada de um objeto detectado pela YOLO já esteja sendo rastreada pelo Tracker, ela é ignorada. Mas isso aumentou muito o tempo de processamento do código, inviabilizando qualquer chance de ser continuado.

O próximo modelo de rastreador aplicado foi o modelo Euclidiano descrito por Jaiswal (2022). A lógica desse rastreador consiste em associar a uma mesma identificação o veículo que esteja dentro de uma distância de pixeis definida a cada frame. Para identificar o veículo a cada frame foi utilizado novamente a YOLO versão 3, mas dessa vez adicionando na saída da função findObject() a classe dos veículos identificados. Esses dados são carregados para uma função update(), onde para cada dado é calculado seu centro cx e cy.

A partir desses pontos é comparado a distância para cada ponto armazenado previamente no dicionário self.center_points.items() que inicia vazio. Dessa forma, na primeira vez que rodar o código, same_object_detected mantém seu valor como falso e entra na condição de atribuir uma nova identificação, com suas coordenadas e identificação de classe. O laço seguinte armazena as informações em uma lista para enfim fornecer os dados de saída da função update(). A próxima vez que rodar, será feito uma comparação dos novos pontos centrais identificados com o anterior, e se estiver na mesma distância definida, será considerado o mesmo veículo.

Como as imagens analisadas são de duas dimensões físicas, veículos que se distanciam na profundidade apenas diminuem seu tamanho, portanto a deslocação desses veículos entre cada frame diminui. Visando contornar esse problema, foi estabelecido dois tipos de distância de pixeis, uma distância para veículos mais ao fundo da imagem e distância para veículos mais próximos. A definição dessas duas regiões é feita manualmente com a seleção do mouse no início do programa.

Para realizar a contagem de veículos que passam em cada via, foi feito uma função que a partir da seleção manual com o mouse é traçado a quantidade de linhas desejada. Tanto a função de definir a região retangular de distância de fundo, quanto a função de traçar as linhas possuem uma lógica semelhante. A principal diferença é que para as linhas, é associado junto uma letra do alfabeto e uma ordem para identificar cada uma delas posteriormente.

A partir dos dados obtidos do rastreador, é feito um laço para ler cada elemento da lista por frame, onde para cada elemento, é feito um segundo laço para analisar se ele está dentro da região da linha. Para definir a região de cada linha, é feito uma equação da reta em relação ao ponto. Além disso, linhas que estiverem dentro da região de fundo, terão uma região de contato menor que linhas que estiverem fora. Se o ponto central do veículo estiver dentro dessa região, uma série de condições em relação a classe é feita: Se a classe do veículo for carro (2), moto (3), caminhão (7) ou ônibus (8), é adicionado à uma lista específica para cada classe a identificação do veículo que esteve naquela região.

É lido a quantidade de identificações diferentes que existem para cada classe em cada linha, dessa forma é possível contar o total de veículos e seus tipos que passaram nas opções da via.

O último rastreador que foi aplicado é o descrito por Brostrom (2022), que consiste nos modelos de rastreamento StrongSort, OCSort e ByteTrack. O repositório desse algoritmo possui um código que integra os rastreadores com um detector. O detector que já está descrito por padrão pelo autor é o Yolo Versão 5, por isso manteve-se essa mesma versão para as aplicações desse código. Para aplicar o código, foi necessário desenvolver um ambiente computacional prório, onde foi instalado com o comando "pip install" todos as bibliotecas necessárias que estão descritas no documento "requirements.txt".

Para rodar o código, é possível passar uma série de parâmetros, desde o vídeo e classes de objetos que deseja rastrear, até qual modelo de rastreador e modelo da Yolo treinada. Source é o parâmetro do vídeo, save-vid salva o resultado das detecções em vídeo, save-txt salva um arquivo de texto com os dados, classes são as classes do banco de dados que deseja detectar, tracking-method é o modelo de rastreador, yolo-weights é o peso da Yolo que deseja utilizar e img é o tamanho da imagem.

Para essa aplicação foi escolhido dois pesos diferentes da YOLO, yolov5n.pt e yolov5m.pt, que segundo o repositório da Ultralytics, desenvolvedora que disponibilizou os modelos pré treinados da YOLO versão 5 com o banco de dados COCO, yolov5n.pt apresenta um tempo de processamento para detecção mais leve, enquanto yolov5m.pt apresenta um tempo maior em troca de uma precisão maior.

O arquivo salvo com dados possui em cada coluna: o número do frame, a identificação do veículo, a coordenada x e y do topo esquerdo da caixa de detecção, a largura da caixa de detecção, a altura da caixa de detecção, a classe do veículo.

Para realizar a contagem dos rastreadores desse modelo, foi desenvolvido um código que lê o arquivo de texto gerado e conta os veículos de entrada e saída de cada região. Além disso, esse código gera linhas para cada posição dos veículos e plota na imagem para que seja possível visualizar o fluxo de veículos. Por fim, é possível também selecionar qual veículo deseja visualizar sua rota.

Isso é feito primeiro filtrando as informações, separando em uma lista principal Veiculos contendo uma lista com a as informações de cada linha(que também tem o formato de lista), do frame inicial ao último que aparece o veículo de cada identificação: Veiculos[Veiculo0[coordenadas[frame0[0,1,1,...], coordenadas[frame1[0,1,1,...]]], Veiculo1[coordenadas[frame0[0,1,1,...]], coordenadas[frame1[0,1,1,...]]], Veiculo2[coordenadas[frame0[0,1,1,...]], coordenadas[frame1[0,1,1,...]]], ...etc].

A partir disso, é escaneado cada veiculo identificado na lista e traçado suas linhas de acordo com cada posição. Depois disso é feito a contagem de veículos de entrada e saída em cada região.

O traçamento das linhas de um veículo é feito de forma semelhante a anterior:

Download de arquivos do projeto

Script código Rastreador Euclidiano: <https://drive.google.com/file/d/1HSpnLeFR5zTeu8IvCiNED3dV1iq1YAhP/view?usp=sharing>

Script código Contador: <https://drive.google.com/file/d/1QaY2hrSpPJjj6-6p1XjvXw6vJBi61Z5I/view?usp=sharing>

Algoritmo Detector e Rastreador Strong Sort: <https://drive.google.com/drive/folders/1shmnUy-uqN3GLgZUjGk1TnHzGNcBpDlV?usp=sharing>