LPR – License Plate Recognition Brazil – Parte 1

Existem alguns poucos acrônimos para reconhecimento de placas de carro como o LPR, ANPR (Automatic Number Plate Recognition), AVI (Automatic Vehicle Identification) e APD (Automatic Plate Detection, que é a primeira fase).

Bem, aqui se somam diversas ferramentas para chegar ao objetivo e ao final, esse recurso poderia ser utilizado para algo mais além de dar multas; poderia abrir o portão de sua casa automaticamente, com uma segunda chave de segurança como o beacon; Chegou a pé, o portão não se abre. Chegou de carro com o beacon, o reconhecimento da placa é feito e então o portão aberto. E muitas outras utilidades, como determinar se o morador do condominio se dirigiu corretamente à sua vaga (fazendo tracking após tê-lo reconhecido pela placa).

As demais aplicações e enlaces com IoT ficarão por sua conta. Nesse tutorial pretendo apenas deixar de presente o código para você integrar em seu sistema de forma livre e gratuita.

Fases do reconhecimento

Quando se está fazendo reconhecimento facial, apenas o face detection precede a fase do face recognition. Não que a tarefa seja mais fácil, mas para o LPR o conjunto de ferramentas difere. A arte agora é definir o quê deverá ser empregado para obter um bom resultado. Primeiro, vamos considerar as variáveis de ambiente.

Luz

A luz sempre variará mas para a detecção de placa isso é praticamente irrelevante. Porém, para fazer a leitura da placa à noite, será necessário um controle extra, devido ao alto nível de reflexibilidade. Por enquanto, vamos considerar o melhor dos mundos.

ângulo

A câmera só está de frente com a placa do carro em casos de portaria e pedágio. Quando a câmera está na rua, seja radar ou comércio, para identificar a placa o processo pode ser um pouco mais trabalhoso devido ao ângulo da câmera em relação ao carro ou a inclinação do carro em relação à câmera.

Gatilho

Não é viável correr todo um processo sobre um frame se podem existir intervalos de segundos; considerando 10 frames por segundo, imagine a quantidade de processamento disperdiçado e o possível delay que isso poderia gerar. Para evitar esse tipo de problema, a melhor opção é utilizar um gatilho para que o processo só ocorra quando disparado. Isso pode ser feito de forma mecânica, eletrônica ou digital, criando uma cerca virtual.

ROI

O processamento sobre um frame pode se tornar uma tarefa complexa, dependendo principalmente da resolução. Considerando que você tenha uma câmera em um lugar alto, obviamente a resolução de captura precisará ser maior ou os detalhes se perderão. Por exemplo, você pode seguramente utilizar full HD na câmera para ter uma área imensa de pixels , o que certamente fará o objeto distante se tornar mais próximo para a análise. Porém, analisar um frame desse tamanho pode ser a morte para o sistema, então aliado ao gatilho (que também é um ROI), uma região de interesse (ROI) extraida do frame de trabalho pode ser a salvação nesse caso.

Juntando tudo

Então o fluxo do programa para o reconhecimento de placas deve ser:

  • Gatilho – onde um evento é gerado quando uma região linear for cruzada.
  • Recorte – uma cópia da região da imagem principal é repassada adiante.
  • Plate Detection – Escaneamento da região copiada para separar a placa.
  • Plate Recognition – A conversão da placa em caracteres interpretáveis.

Se falhar o plate detection, o plate recognition nem precisa ser executado. Isso é importante, porque processamento poderá ser economizado em casos de falso-positivo.




O melhores resultados podem ser obtidos através de uma câmera com IR, pela clareza oferecida na imagem, uma vez que a iluminação por IR se sobressai com o reflexo da placa, que reflete toda a luz na área branca e absorve a luz no preto.

Essa imagem abaixo é de um anti-radar, por isso que faltam partes na placa, mas é assim que sua placa é vista no comércio de multas de São Paulo:

Infravermelho sobre a placa
Infravermelho sobre a placa

Características da placa

Todos, exceto moto
Placa ainda em 2016
Placa ainda em 2016

O Brasil já passou por uma sacolada de tipos de placa. De 2000 em diante, ainda algumas outras mudanças aconteceram e a atual placa tem o seguinte padrão:

  • Altura de 130mm
  • Largura de 400mm
  • Caracteres na altura de 53mm (tem outra resolução mais antiga que diz 63mm)
  • Fonte Mandatory
  • Largura do traço de 10mm
Apenas moto
Placa de moto
Placa de moto

As motos possuem um formato mais compacto, onde as letras ficam em cima e os números embaixo. Seu padrão:

  • Altura de 170mm
  • Largura de 200mm
  • Caracteres na altura de 53mm
  • Fonte Mandatory
  • Largura do traço de 6mm

Atualmente a placa é branca refletiva com o nome da cidade, mas já estão previstas as mudanças para a próxima placa no padrão Mercosul, onde o topo da placa recebe o nome do país. Essa não será a única mudança, porque o país ficará sobre uma faixa azul de lado a lado da placa. Pra piorar, não haverá mais o padrão letra/número, mas será uma sequência aleatória de 7 caracteres alfanuméricos. Também haverá desenho da bandeira do Brasil, bandeira da federação e brasão do municipio com nome por extenso. Só vai faltar o escudo do seu time de futebol.

A cor da borda da placa e a cor dos caracteres variarão conforme a categoria do veículo. A fonte será a FE Engschrift, já adotada em placas na alemanhã. A fonte é coisa linda:

Fonte dos infernos
Fonte dos infernos

E a placa ficará essa belezura:

Circo
Circo

Download das fontes

Se quiser as fontes pra fazer seus .jpg para teste, você pode baixar a Mandatory nesse link e a FE-Font nesse link.

Algorítmo de reconhecimento da placa

Como dito anteriormente na sequência de passos, devemos primeiro desenvolver uma classe de detecção, depois uma de reconhecimento. Vamos manter o foco nisso por enquanto porque é possível que seu interesse seja exclusivamente nessas duas partes, mas provavelmente em outro artigo detalho os passos precedentes à detecção e reconhecimento.

A lógica do fluxo é bastante simples.

  • Inicio do programa
  • Localizar placa no frame atual
  • SE não existe, encerra o passo atual do loop aqui. SENAO
  • Faz o reconhecimento da placa
  • Exibe o resultado

No código a coisa é um pouco mais complicada. Isso porque entram novas técnicas que não são necessárias no face detection/recognition (por exemplo), que é a segmentação das áreas que compõe a placa. Eu, como novato na área, posso dizer que para mim não foi nada simples chegar nesse resultado, ainda que tendo comprado alguns livros sobre OpenCV e um sobre redes neurais. Como dizem, “no pain, no gain”.

Plate detection





Se você não está aprendendo também visão computacional e redes neurais, nesse ponto você já deve ter em mente que a visão computacional trabalha sobre cada frame recebido em um loop, como se fosse uma imagem .jpg (a extensão é para exemplificar) sendo lida por um editor de imagens. Você deve acomodar esse conjunto de dados em uma variável e trabalhar em uma cópia dela.  Nessa cópia convertida em grayscale passamos um algorítmo que irá fazer a primeira análise e separar a placa.

O primeiro passo é  a segmentação para validar um conjunto de características que definam uma região como sendo uma placa de carro. Esse passo é um pouco duro, porque exige uma série de filtros e o conjunto de resultados deve decidir se aquilo é ou não uma placa.

O segundo passo é a classificação. Para reforçar o resultado, utilizaremos um SVM sobre a imagem. Aqui o trabalho é ainda mais intenso.

Um ponto importantíssimo a considerar é a distância entre a câmera e a placa, por isso você deverá definir um gatilho. Porém o carro em movimento pode diferir a distância conforme sua velocidade. Veremos mais detalhes, mas por ora vamos pensar na situação mais simples, que é o carro servindo-nos de modelo fotográfico.

Segmentação

A tarefa aqui é particionar a imagem de forma que se torne simples a análise e extração de dados. Uma das mais importantes características na segmentação da placa do carro é o número elevado de vertices verticais quando vista de forma frontal. Além, disso, não deverá haver rotação ou distorção de perspectiva, mas calma, no final a tolerância será maior.

Como quase sempre, devemos trabalhar na imagem em grayscale e remover os ruídos de câmera e ambiente. Um conjunto de filtros pode ser aplicado como a contração e dilatação subsequente para eliminar minimamente pontos extras. Além disso, um gaussian blur – ou apenas ele, de modo que a melhor opção é criar 3 métodos para aplicar os filtros por experimentação e utilizar apenas os que interessam no final.

O OpenCV possui 3 filtros de passa-alta: Sobel, Scharr e Laplacian.

As operações do filtro de Sobel somam o gaussian smoothing com a operação de diferenciação, o que o torna mais resistente a ruído. A direção da derivativa pode ser dado pelo argumento X ou Y. O kernel size também é customizável. Se o kernel for 3×3, o Scharr pode ser mais adequado mas enfim, experimentação é a chave.

Veja meu teste inicial que fiz baseado em um exemplo que não tinha relação direta com esse artigo, mas era especificamente sobre filtros:

Amostra de filtros
Amostra de filtros

O mesmo para uma placa já com um zoom:

O mesmo exemplo com o alvo próximo
O mesmo exemplo com o alvo próximo

 

A simples adição de blur já deu uma melhorada significativa porque ajuda a reduzir o ruído, por isso não existe um padrão, você deve criar sua combinação de filtros para chegar no melhor resultado. Até esse ponto temos:

Uma "pitada" de blur
Uma “pitada” de blur
Threshold

A determinação dos limitrofes são feitas nesse ponto sobre o processamento prévio da imagem. Isto é, após removido o ruído e aplicado o filtro de Sobel, aplicam-se os thresholds. Utilizando o THRESH_OTSU, a função retorna o melhor valor obtido pelo algorítmo de Otsu. Essa operação como está sendo feita também é conhecida como “binarização”.

Para ter uma noção, os thresholds tem essas funções:

thresholds
thresholds

A operação foi feita sobre o Sobel de X, resultando nisso até agora:

Blur + Sobel + Threshold
Blur + Sobel + Threshold
Operação morfológica

Com a operação morfológica removemos espaços em branco entre cada linha vertical e conectamos todas as regiões que possuem um número significativo de bordas. Nesse passo teremos a possível região contendo a placa do carro.

Visualmente o resultado não parece nada bom, mas é como a arte de pintar um quadro; do borrões iniciais surgem as obras de arte no fim, tranquilize-se e tenha em mente que na detecção o que queremos é apontar ao próximo passo onde está localizada a placa no frame.

morphology
morphology

Com um pequeno ajuste, toda a região da placa ficará interligada, formando assim o retângulo que necessitamos para a detecção da região da placa. Mas supondo que fosse um caso onde não houvesse ainda uma condição de interligar essas regiões; utilizando contornos você pode fazer a marcação necessária para a conexão através da espessura do traço.

Encontrando contornos

Conforme a documentação do OpenCV, os contornos podem ser explicados como uma junção contínua de pontos em torno de uma região contendo a mesma cor ou intensidade. São principalmente úteis para detecção de uma região para detecção.

A documentação sugere que seja utilizada imagem binária, isto é, preto e branco; confere.

A operação de contorno altera a imagem alvo, portanto tenha certeza de que ela poderá ser descartada trabalhando em uma copia derivada.

Com um pequeno ajuste, não houve a necessidade da utilização de cntornos. De qualquer modo, exponho a imagem para que você veja o resultado:

Fases
Fases

Como esse artigo já se extendeu demais, vou deixar a finalização para a “Parte II” desse artigo, mas se quiser dar uma “cercada” na área, aproveite esse artigo sobre a detecção de retângulos para brincar. E não se preocupe, não vamos utilizar esse OCR simples desse outro artigo para fazer a leitura.

O código de experimentação é esse a seguir; pode ser que mude tudo ou mude nada. Mais uma vez, acompanhe no próximo artigo.

Se quiser utilizar a mesma imagem, eis:

Cobaia
Cobaia

 

Inscreva-se no nosso newsletter, alí em cima à direita e receba novos posts por email.

Siga-nos no Do bit Ao Byte no Facebook.

Prefere twitter? @DobitAoByte.

Inscreva-se no nosso canal Do bit Ao Byte Brasil no YouTube.

Nossos grupos:

Arduino BR – https://www.facebook.com/groups/microcontroladorarduinobr/
Raspberry Pi BR – https://www.facebook.com/groups/raspberrybr/
Orange Pi BR – https://www.facebook.com/groups/OrangePiBR/
Odroid BR – https://www.facebook.com/groups/odroidBR/
Sistemas Embarcados BR – https://www.facebook.com/groups/SistemasEmbarcadosBR/
MIPS BR – https://www.facebook.com/groups/MIPSBR/
Do Bit ao Byte – https://www.facebook.com/groups/dobitaobyte/

Próximo post a caminho!

Comments

comments

Djames Suhanko

Djames Suhanko é Perito Forense Digital. Já atuou com deployer em sistemas de missão critica em diversos países pelo mundão. Programador Shell, Python, C, C++ e Qt, tendo contato com embarcados ( ora profissionalmente, ora por lazer ) desde 2009.