ÍNDICE DE CONTEÚDO
- Modelo para identificação dos caracteres da placa
- Implementação na Jetson Nano
- Reconhecimento automático de placas de identificação de veículos em uma Jetson Nano usando YOLOv4-tiny e LPRNet
Nesta série de artigos vamos apresentar um workflow completo para reconhecimento automático de placas de identificação de veículos usando uma Jetson Nano com uma câmera Raspberry Pi v2 8MP. Durante o desenvolvimento desse workflow, tomamos decisões visando um cenário para reconhecimento de placas de identificação de veículos em portaria para controle de entrada e saída de veículos. Entretanto, este pode ser estendidos para outras aplicações, como monitoramento de trânsito, alterando poucas ou nenhuma das decisões apresentadas. Este primeiro artigo da série aborda sobre a etapa de detecção da placa, responsável por extrair a placa de cada frame para que sejam reconhecidos os caracteres.
Modelo para detecção de placas
A YOLOv4 é considerada estado da arte para detecção de objetos em imagens, contando com várias versões mais compactas para dispositivos com restrições computacionais. A YOLOv4-tiny é um exemplo de arquitetura mais compacta baseada na YOLOv4, apresentando FPS até oito vezes maior que a YOLOv4 [Techzizou-Medium, 2021]. Ambos modelos possuem pesos pré-treinados com o dataset MS COCO, o qual conta com 80 classes com mais de 330 mil imagens.
Yolo-v4 vs Yolo-v4 tiny
Testamos ambos modelos na Jetson Nano usando o framework Darknet com pesos pré-treinados no dataset COCO para avaliar e comparar o desempenho destas. Usamos este vídeo de aproximadamente 1min30s capturado por uma câmera para monitoramento de trânsito localizada na rodovia PST da Tailândia em um momento de trânsito intenso. Usamos o vídeo na resolução 1080p e 720p. No caso da YOLOv4, tanto em 720p e 1080p obteve-se FPS médio de 0,8. Também observamos um perfil similar entre as duas resoluções de uso de memória RAM e SWAP, picos de 3,1GB a 3,3GB de RAM e menos de 100MB de SWAP. Já no caso da YOLOv4tiny, usando o vídeo 720p o FPS médio foi de 12,8 enquanto que com o vídeo de 1080p obteve-se um FPS médio de 5,9. Também foi observado um perfil similar de uso de RAM e SWAP para ambas resoluções, mas menor do que no caso da YOLOv4, com picos de uso de RAM em torno de 2GB e sem uso de SWAP.
Na Figura 1 mostramos as detecções de um frame selecionado do vídeo resultante do teste com a YOLOv4 em 1080p. Já na Figura 2 mostramos as detecções do mesmo frame obtidas com a YOLOv4-tiny em 1080p. Comparando ambos, notamos que a YOLOv4-tiny falha em detectar objetos menores e mais distantes da câmera, enquanto a YOLOv4 é mais robusta nesses casos. Esse comportamento é esperado já que no conjunto de teste do dataset MS COCO a acurácia da YOLOv4-tiny é cerca de 2/3 da acurácia da YOLOv4.
No cenário de uso do nosso problema, reconhecimento de placas de identificação de veículos em uma portaria, a câmera geralmente é localizada próxima a cancela da portaria onde os veículos serão parados e então as placas estarão próximas da câmera. Além disso, os caracteres da placa precisam estar legíveis e com boa visibilidade para serem reconhecidos na próxima etapa. Dessa forma, a falta de robustez do modelo YOLOv4-tiny com objetos distantes da câmera não deve afetar o cenário de uso. Neste caso, obter um maior FPS é mais relevante para a aplicação do problema, uma vez que os automóveis não deverão ficar muito tempo parados na cancela. Devido a todas essas implicações, escolhemos o modelo YOLOv4-tiny para ser treinado para detectar as placas de identificação de veículos.
Dataset Open Images V6
Para treinamento do modelo de detecção das placas usamos o dataset da Google chamado Open Images, o qual está disponível publicamente. Ele possui cerca de 9 milhões de imagens, e 16 milhões de bounding boxes para 600 classes de objetos, dentre as quais há a “Vehicle registration plate”. Na Figura 3 apresentamos a imagem deste dataset.
artigo anterior da série
Também selecionamos algumas imagens de outras classes, como por exemplo “Bicycle”, “Airplane” e “Building”, para incluir no conjunto de treinamento e validação. Essas imagens foram usadas como exemplos negativos, i.e., não possui anotações para a classe alvo “Vehicle registration plate”. Esses exemplos são usados para balancear os exemplos da classe alvo e aumentar a robustez das detecções. Após adicionar esses exemplos, totalizaram 8091 imagens para treinamento e 675 imagens para validação. O conjunto de teste foi composto somente por imagens da classe alvo para avaliar o desempenho do modelo para detecção de placas de identificação de veículos, totalizando em 1113 imagens.
O OIDv4 ToolKit pode ser usado para fazer o download dos conjuntos de treinamento, validação e teste. Essa ferramenta faz o download de classes específicas e salva as anotações de cada imagem em um arquivo “.txt” correspondente na pasta Label dentro de cada conjunto. Para construir o conjunto de treinamento da classe “Vehicle registration plate” executa-se o código main.py com a opção downloader, o parâmetro –classes indicando a classe desejada e –type_csv indicando o conjunto: treinamento, validação ou teste.
1 2 |
$ python main.py downloader --classes 'Vehicle registration plate' \ --type_csv train |
Para os conjuntos de validação e teste repete-se o mesmo procedimento alterando o parâmetro –type_csv para validation e test , respectivamente. Também executamos esses passos para as demais classes que foram usadas como exemplos negativos. Para cada conjunto é criada uma pasta no diretório OIDv4_ToolKit/OID/Dataset e dentro de cada uma dessas pastas, há uma pasta para cada classe de objetos baixada.
Após realizar o download das imagens, usando o código convert_annotations.py, essas anotações são transformadas para o padrão de anotação de bouding box requerido pelas arquiteturas YOLOv4. Esse código irá realizar esse processo para todos os conjuntos em OIDv4_ToolKit/OID/Dataset, e para cada classe salva na pasta de cada conjunto. Sendo necessário então, executar esse código somente uma vez diretamente no root do diretório OIDv4_ToolKit:
1 |
$ python convert_annotations.py |
Por fim, pode-se remover a pasta Label criada dentro da pasta de cada classe de objetos. Por exemplo, para a classe “Vehicle registration plate”, utilizar os comandos:
1 2 3 |
$ rm -r OID/Dataset/train/'Vehicle registration plate'/Label/ $ rm -r OID/Dataset/validation/'Vehicle registration plate'/Label/ $ rm -r OID/Dataset/test/'Vehicle registration plate'/Label/ |
Após esses passos, cada pasta correspondente a uma classe de objetos para cada conjunto de treinamento apresentará para cada imagem um arquivo “.txt” com o mesmo nome, como mostra a Figura 4.
Treinamento do modelo YOLOv4-tiny
Para o treinamento do modelo de detecção YOLOv4-tiny, utilizamos o framework Darknet. Este possui várias redes neurais implementadas em C e CUDA, incluindo a YOLOv4 e a YOLOv4-tiny. Primeiro passo é clonar este repositório git:
$ git clone https://github.com/AlexeyAB/darknet
É importante se atentar aos requisitos descritos no README:
- CMake >= 3.18
- CUDA >= 10.2
- OpenCV >= 2.4
- cuDNN >= 8.0.2
- GPU with CC >= 3.0
Utilizamos CMake 3.22.1, CUDA 11.4, cuDNN 8.2.2 e OpenCV 4.1.1.
Com os todos requisitos corretos, basta compilar o Darknet a partir do Makefile:
1 2 3 4 5 6 |
$ cd darknet $ sed -i 's/OPENCV=0/OPENCV=1/' Makefile $ sed -i 's/GPU=0/GPU=1/' Makefile $ sed -i 's/CUDNN=0/CUDNN=1/' Makefile $ sed -i 's/CUDNN_HALF=0/CUDNN_HALF=1/' Makefile $ make |
Neste caso, alteramos o Makefile para usar GPU e OpenCV, mas é possível utilizar o framework sem GPU apesar de uma drástica redução no desempenho do treinamento sendo executado somente em CPU.
Após compilar Darknet com sucesso, são necessárias algumas configurações para realizar o treinamento com o dataset de placas de identificação de veículos. Neste caso em que há um número reduzido de exemplos no conjunto de treinamento e validação, é recomendado fazer o treinamento a partir dos pesos pré-treinados no dataset COCO para que o modelo tenha uma convergência no treinamento mais rápida. Para isso, os pesos pré-treinados estão disponíveis por este link, o qual deve ser salvo no diretório darknet/.
O próximo passo é copiar o conjunto de treinamento e validação para o diretório darknet/data. Todos arquivos do diretório OIDv4_ToolKit/OID/Dataset/train que estão em subpastas, representando as diferentes classes de objetos, devem ser copiados para o diretório darknet/data/obj. A Figura 5 apresenta como este diretório deve ficar ao final. Da mesma forma, todos arquivos do diretório OIDv4_ToolKit/OID/Dataset/validation que estão em subpastas, representando as diferentes classes de objetos, devem ser copiados para o diretório darknet/data/test. A Figura 6 mostra como esse diretório deve ficar. Lembrando que essa pasta apesar do nome, contém os exemplos de validação.
Após copiar os arquivos de treinamento para o diretório darknet/data/obj e os de validação para darknet/data/test, é necessário gerar os arquivos darknet/data/train.txt e darknet/data/test.txt com os caminhos relativos das imagens de treinamento e validação, respectivamente. Para isso, usamos os scripts generate_train.py e generate_test.py disponibilizados neste repositório github. Estes scripts devem ser copiados para o diretório darknet/. Para gerar os arquivos train.txt e test.txt em darknet/data/, basta executar:
1 2 |
$ python generate_train.py $ python generate_test.py |
Após esses passos, deve-se criar os arquivos obj.names e obj.data em darknet/data/. No arquivo obj.names inclui o nome das classes de interesse, neste caso somente a classe alvo:
Já no arquivo obj.data descreve-se o número de classes, indica-se a localização dos arquivos train.txt e test.txt, do arquivo com o nome das classes e de um diretório backup no qual serão salvos os pesos durante o treinamento:
O último passo consiste em criar um arquivo com as configurações de treinamento para a YOLOv4-tiny. Faça uma cópia do arquivo darknet/cfg/yolov4-tiny.cfg nomeando-o como darknet/cfg/custom-yolov4-tiny-detector.cfg. Após, é necessário realizar algumas modificações neste arquivo para adaptar ao problema de detecção de placas, o qual só possui uma classe. A primeira alteração é a variável classes, o qual aparece duas vezes no custom-yolov4-tiny-detector.cfg e está atribuído com o número de classes do dataset COCO, classes=80. Ele deve ser alterado em ambas ocorrências para classes=1. Com a alteração do número de classes, é necessário alterar a quantidade de filtros da última camada convolucional. O bloco de variáveis dessa camada está localizado logo antes dos dois blocos [yolo], sendo necessário alterar a variável filters em ambos blocos. Esse valor deve ser configurado como:
filters=(classes + 5) * 3
Neste caso: filters=18
Além disso, precisamos reduzir os valores das variáveis max_batches e steps, pois há uma quantidade reduzida de exemplos para treinamento e faremos apenas um fine-tuning. Modificamos para:
max_batches=8000
steps=6400.0,7200.0
A variável steps possui dois valores, o primeiro corresponde a 80% do valor de max_batches e o segundo a 90%. Também alteramos a resolução da imagem de entrada para 640×640, objetivando garantir uma boa resolução para as placas a serem detectadas. Além disso, o número de batches e mini batches foram configurados para 64 e 16, respectivamente. Assim as primeiras linhas do arquivo custom-yolov4-tiny-detector.cfg, que definem a arquitetura e as configurações de treinamento, ficaram como:
Ao finalizar todos esses passos, o treinamento pode ser realizado pela seguinte linha de comando, certificando-se estar no diretório darknet/:
1 2 3 4 |
$ ./darknet detector train data/obj.data \ cfg/custom-yolov4-tiny-detector.cfg \ yolov4-tiny.conv.29 -dont_show -map \ | tee output_yolov4-tiny.log |
Realizamos o treinamento em um servidor com 16 cores do processador Intel Xeon Gold 6130 CPU 2.10GHz e duas GPUS Tesla T4 16Gb, levando cerca de 5h. A Figura 7 mostra o gráfico gerado pelo Darknet do progresso do treinamento com a loss do conjunto de treino e o mAP calculado no conjunto de validação.
O modelo com melhor mAP no conjunto de validação é salvo no diretório configurado no campo backup do arquivo obj.data, no nosso caso é /storage/backup/yolov4-tiny. O nome do arquivo é custom-yolov4-tiny-detector_best.weights. Este arquivo será usado para realizar inferência na Jetson Nano.
O processo de treinamento gera um log, o qual salvamos no arquivo output_yolov4-tiny.log. Ao final do treinamento, é calculado o mAP em todo o conjunto de validação usando o melhor modelo. O resultado obtido no conjunto de validação foi:
IoU = 78.82 %
[email protected] = 83.86 %
Para avaliar o conjunto de teste do dataset Open Images, que fizemos o download previamente, copiamos a pasta OIDv4_ToolKit/OID/Dataset/test para o diretório darknet/data/, sobrescrevendo a pasta darknet/data/test que continham os arquivos de validação. Então, geramos o arquivo darknet/data/test.txt novamente executando:
1 |
$ python generate_test.py |
Após esses passos, esse conjunto de teste pode ser avaliado usando o melhor modelo salvo em /storage/backup/yolov4-tiny/:
1 |
$ ./darknet detector map data/obj.data cfg/custom-yolov4-tiny-detector.cfg /storage/backup/yolov4-tiny/custom-yolov4-tiny-detector_best.weights |
O resultado obtido com o conjunto de teste foi:
IoU = 80.39 %
[email protected] = 81.67 %
Observamos que os resultados nos conjuntos de validação e de teste foram similares, indicando que o modelo conseguiu generalizar os padrões de detecção aprendidos. Disponibilizamos o modelo treinado e exportado a partir deste link, na pasta darknet_yolov4-tiny.
Próximos passos
Neste artigo apresentamos os passos adotados para treinar um modelo de detecção de placas de identificação de veículos usando a YOLOv4-tiny. Utilizamos como referências principais estes dois repositórios git: YOLOv4-Cloud-Tutorial e yolov4-tiny-custom_training.
Este artigo é o primeiro passo para o reconhecimento automático de placas veiculares. No próximo artigo exploraremos a identificação dos caracteres da placa detectada.
Referência
[Techzizou-Medium, 2021] Medium, por Techzizou. YOLOv4 vs YOLOv4-tiny: Training YOLO for Object Detection. 2021. URL: https://medium.com/analytics-vidhya/yolov4-vs-yolov4-tiny-97932b6ec8ecAutores
- Geise Santos
- Matheus Kowalczuk Ferst
- Elton de Alencar
- Lucas Coutinho
- Murilo Pechoto
Conteúdo incrível, parabéns!
Ótimo conteúdo, parabéns!
Muito obrigada, Vinícius!
Geise! Muito obrigado por compartilhar! Excelente artigo. Aproveitando, gostaria de trocar umas palavrinhas com você. Agradeço desde já. Abraços!
Muito obrigada, Normano! Abraços.