Implementação na Jetson Nano

Nos artigos anteriores apresentamos as etapas de detecção da placa de automóveis e de identificação dos caracteres da placa. Detalhamos os métodos utilizados e o treinamento dos modelos para cada uma destas tarefas. Neste artigo, descreveremos a última etapa do workflow de reconhecimento automático de placas, o qual consiste na implementação das duas etapas na Jetson Nano.  

Hardware de teste 

A Jetson Nano foi escolhida por ser um SBC (Single Board Computer) com bastante recurso, contando com uma GPU 128‑core NVIDIA Maxwell que possibilita melhor desempenho para os modelos de deep learning. Usamos o modelo P3450 B01 da Jetson Nano. 

O primeiro passo é conectar uma câmera Raspberry Pi V2 8MP na Jetson, a qual utiliza o protocolo CSI (Camera Serial Interface) e por isso tem maior velocidade de transmissão com a placa host. Como este modelo da Jetson tem duas interfaces para a câmera, conectamos na CAM0, como mostra a Figura 1.  

reconhecimento de placas na Jetson Nano
Figura 1: Câmera Raspberry Pi V2 8MP conectada na Jetson Nano usando a interface CAM0

Após conectar a câmera, podemos iniciar a configuração do sistema. Primeiramente, é necessário gravar a imagem em um SD card. Usamos um microSD card de 32Gb e gravamos a Jetson Nano Developer Kit SD Card Image versão 4.5. Para realizar a gravação da imagem, basta executar os seguintes passos (mais detalhes neste link): 

  • Fazer o download e instalar o Etcher 
  • Clicar em “Flash from file” e selecionar o arquivo jetson-nano-jp45-sd-card-image.zip, baixado anteriormente. 
  • Selecionar o SD card já formatado, clicando em “Select target” e iniciar o flash da imagem. 

Após a gravação da imagem no SD card com sucesso, este pode ser inserido na Jetson. Ao ligar a Jetson Nano com o SD card recém-formatado, é necessário realizar as configurações pela primeira vez. Estas envolvem selecionar o idioma do sistema, criar usuário e senha, selecionar o tamanho da partição do APP (é recomendado usar o tamanho máximo sugerido) e selecionar o modo “MAXN”. Devido a potencial aquecimento da Jetson Nano após essa configuração, adicionamos um cooler Noctua 12V em seu exaustor. 

Com o sistema configurado, o próximo passo é realizar o setup do CUDA. No terminal, executamos os seguintes comandos:  

Após essa etapa, devemos instalar as dependências e o Python 3, assim como os pacotes necessários. Apesar de já possuir o OpenCV 4.1.1 na imagem disponibilizada pela NVIDIA, é necessário instalar as dependências desta versão do OpenCV também. Para instalar os pacotes necessários com as versões compatíveis, utilizamos os seguintes comandos:

Também é necessário instalar o Tensorflow, a versão compatível neste caso é a 2.4. Para instalar o Tensorflow e suas dependências são usados os seguintes comandos no terminal:

Os modelos treinados serão exportados em TensorRT para otimizar seu uso na Jetson. Então, é necessário instalar o TensorRT e suas dependências. Para isso, execute os seguintes comandos no terminal:

Exportar modelos para TensorRT

Após preparar o ambiente na Jetson, podemos converter os modelos treinados da YOLOv4-tiny e da LPRNet em TensorRT. Os modelos treinados estão disponíveis no arquivo modelos.zip. Começando com o modelo custom-yolov4-tiny-detector_best.weights, copie-o para a Jetson no diretório /{HOME}/alpr/tensorrt_demos/yolo, renomeando-o para custom-yolov4-tiny-detector.weights, juntamente com o arquivo custom-yolov4-tiny-detector.cfg. Ambos arquivos devem ter o mesmo nome. Então, execute os seguintes comandos:

Com estes comandos é gerado o modelo custom-yolov4-tiny-detector.trt no diretório /{HOME}/alpr/tensorrt_demos/yolo. Este modelo que será utilizado para realizar a detecção de placas na Jetson. Para converter o modelo da LPRNet, precisamos do tlt-converter disponibilizado pela Nvida. Fizemos o download da versão compatível com o JetPack 4.5, a partir do link e seguimos os passos deste tutorial. Primeiro, é necessário instalar o pacote OpenSSL:

Após, exportamos as variáveis de ambiente:

Por fim, descompactamos a pasta cuda10.2_trt7.1_jp4.5, que está dentro do arquivo obtido no link, em /{HOME}/alpr/. Então, dentro da pasta /{HOME}/alpr/cuda10.2_trt7.1_jp4.5 atribuímos permissão para execução para o tlt-converter:

Com isso, copiamos o modelo lprnet_model.etlt exportado no artigo anterior para a Jetson salvando no diretório /{HOME}/alpr/cuda10.2_trt7.1_jp4.5. Dentro desse diretório, convertemos o modelo em TensorRT, utilizando o seguinte comando:

Com esse comando, é gerado o modelo lprnet_model.engine no diretório /{HOME}/alpr/cuda10.2_trt7.1_jp4.5, que será utilizado para fazer a identificação dos caracteres da placa.

Workflow de reconhecimento automático de placas

A partir dos modelos convertidos em engine do TensorRT, definimos um workflow completo para reconhecimento de placas de automóveis a partir da câmera Raspberry Pi V2. Primeiro, criamos a pasta /{HOME}/alpr/ALPR_TRT para colocar os modelos e códigos necessários. Então copiamos os modelos engine gerados para este diretório. O modelo lprnet_model.engine pode ser copiado diretamente para o diretório /{HOME}/alpr/ALPR_TRT. Entretanto, o modelo custom-yolov4-tiny-detector.trt precisa estar dentro de uma pasta chamada yolo, e por isso criamos essa pasta em /{HOME}/alpr/ALPR_TRT/yolo e salvamos o modelo dentro dela. Então criamos um arquivo Python chamado alprnet_cam.py, no diretório /{HOME}/alpr/ALPR_TRT. Este será o código do workflow completo de reconhecimento automático de placas. 

Os imports dos pacotes necessários para execução deste workflow e definições de constantes são apresentados no código a seguir. A constante CHARS define os caracteres reconhecidos pelo modelo LPRNet, os quais devem ser os mesmos que definimos no treinamento deste modelo. Os pacotes utils e plugins são os mesmos do diretório ${HOME}/alpr/tensorrt_demos/yolo, sendo disponibilizados no repositório git tensorrt_demos

Utilizamos este exemplo sobre o uso do modelo YOLO com TensorRT e este tutorial sobre gerar um modelo engine e fazer inferência com ele, para implementar as seguintes funções do Código 1.

Além disso, foi necessário definir uma função para recortar a placa da imagem, dado o bounding box retornado pelo modelo da YOLOv4-tiny: 

Também definimos uma função para iniciar a câmera Raspberry Pi usando GStreamer, em que a resolução da câmera é definida como 720p:

Por fim, definimos a função loop_and_detect(cam, conf_th, vis) para executar o workflow usando as funções definidas, gravando um vídeo com o resultado da detecção da placa e do reconhecimento dos caracteres. Esta função recebe os parâmetros: o objeto de captura de vídeo gerado a partir da classe cv2.VideoCapture; o threshold de confiança para considerar a detecção válida; e um objeto da classe BBoxVisualization. Então ela: (1) usa as funções definidas para carregar os modelos; (2) lê cada frame obtido da câmera; (3) obtém o bouding box da placa para este frame a partir do engine da YOLOv4-tiny; (4) caso seja detectada uma placa, obtém o recorte da placa no frame a partir da função crop_bboxes; (5) obtém os caracteres da placa a partir do engine da LPRNet; (6) desenha no frame o bouding box obtido e os caracteres reconhecidos; (7) plota no frame o FPS atual; (8) e grava o frame no arquivo de vídeo lplate.mp4. Esse processo é repetido iterativamente até interromper a execução pressionando a tecla ESC. Ao final, o arquivo lplate.mp4 contém o vídeo gerado com todos os frames e detecções realizadas até o momento da interrupção da execução do código. O Código 2 apresenta esta função. 

Assim, o código principal define os objetos cap da classe cv2.VideoCapture, inicializado a partir da função gstreamer_pipeline(); e vis da classe BBoxVisualization. Então, é chamada a função loop_and_detect(), passando os objetos inicializados e o parâmetro conf_th com valor igual a 0.3:

Geramos dois vídeos exemplos, usando trechos de vídeos disponíveis no Youtube, um com o modelo de placa antigo (lplate_agora_MT.mp4) e outro Mercosul (lplate_fusca_mercosul.mp4). Os créditos dos vídeos originais estão inseridos no rodapé de cada um dos vídeos. Para gerar estes vídeos, adaptamos o código apresentado para receber um vídeo como entrada e realizar o reconhecimento automático de placas a partir desse stream de vídeo. Este  código e o código  completo  desenvolvido   no artigo,  assim  como  as  dependências   necessárias, estão disponíveis no arquivo ALPR_TRT.zip. O arquivo alprnet_cam.py é composto pelos códigos apresentados ao longo do artigo, enquanto que alprnet_video.py é o código adaptado para receber um vídeo como entrada. 

O FPS calculado a cada frame é apresentado no topo esquerdo de cada um, em ambos os vídeos. O FPS médio dos vídeos ficou em torno de 6, o qual é menor do que o FPS médio usando a câmera (em torno de 8), pois a leitura do vídeo em memória aumenta o tempo de processamento do frame. 

Considerações finais 

Nesta série de artigos apresentamos o workflow completo para reconhecimento automático de placas de identificação de veículos, desde o treinamento dos modelos de detecção da placa usando YOLOv4-tiny e de identificação dos caracteres usando LPRNet até implementação e avaliação destes modelos na Jetson Nano. Com isso, objetivamos fornecer uma solução edge para este problema, em que todo o processamento ocorre na ponta, em tempo real, sem necessidade de comunicação com a nuvem para realizar a detecção da placa e o reconhecimento dos respectivos caracteres.

Autores

  • Geise Santos
  • Matheus Kowalczuk Ferst
  • Elton de Alencar
  • Lucas Coutinho
  • Murilo Pechoto

Outros artigos da série

<< Modelo para identificação dos caracteres da placa

JUNTE-SE HOJE À COMUNIDADE EMBARCADOS

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.
Home » Software » Implementação na Jetson Nano
Comentários:
Notificações
Notificar
guest

0 Comentários
Inline Feedbacks
View all comments
Talvez você goste:
Nenhum resultado encontrado.