regex4ocr: um projeto open source para extração de dados de OCR
A empresa Juntos Somos Mais (JS+) foi lançada oficialmente como empresa independente no dia 28/11/2018, tendo a Votorantim, Tigre e Gerdau como acionistas, para encarar o grande desafio de fortalecer e desenvolver o varejo da construção através de um programa de fidelidade que gera incentivos e benefícios para donos de lojas de construção, vendedores e profissionais de obra.
Um dos grandes desafios tecnológicos encontrados no programa encontra-se no momento da pontuação dos profissionais de obra: estes devem enviar fotos dos comprovantes de compra (notas fiscais, recibos, etc.) para que os itens da compra sejam analisados (é verificado se o produto pertence a uma das empresas do grupo, sua quantidade adquirida, etc.) e, assim, transformados em pontos para o profissional, o qual poderá trocá-los por produtos voltados para seu desenvolvimento como equipamentos, ferramentas, etc.
O problema dos comprovantes
Para lidar com a extração de dados das fotos dos comprovantes que são enviados pelos profissionais de obra, dois problemas iniciais precisam ser resolvidos:
1. É necessário uma ferramenta de visão computacional para atuar no reconhecimento de caracteres na imagem. Este é um problema clássico conhecido como OCR (optical character recognition). Existem soluções open source como o Tesseract OCR (https://github.com/tesseract-ocr/tesseract) bem como soluções pagas disponibilizadas pelas plataformas Cloud como Amazon AWS, Microsoft Azure e o Google Cloud Platform.
Na JS+ utilizamos o Google Vision para interpretar os pixels de uma imagem como uma string de texto não estruturada.
2. É necessário uma forma de transformar o resultado do sistema de OCR em uma forma estruturada de informação, isto é, é preciso interpretar a string de saída do OCR parar extrair campos importantes como o cnpj da loja, por exemplo.
Já para este problema, utilizamos nossa biblioteca open source: regex4ocr, a qual permite extrair informações da string de resultado do sistema de OCR através de arquivos de configuração que permitem modelar os comprovantes e documentos através de uma série de expressões regulares , definidas em modelos regexp de documentos (DRMs), fato que torna simples extrair informações de diversos formatos de comprovantes através da adição de novos modelos regexp.
regex4ocr
A biblioteca é baseada na função parse que pode ser aplicada com qualquer string na qual é desejado extrair informações. Tal fato é importante pois a responsabilidade de transformar pixels em strings (OCR) fica completamente isolada da responsabilidade de transformação em dados estruturados, fato que permite que o usuário use a sua biblioteca de OCR preferida (Tessertact, Google Vision, Azure, etc.), seja ela qual for, e simplesmente plugue a regex4ocr para estruturar os dados obtidos.
Os modelos regexp de documento (DRM = Document Regexp Models) são arquivos yml que permitem que o usuário ‘modele’ os documentos que são enviados ao OCR através da definição de campos e expressões regulares que sejam de seu interesse. Assim, caso surja um novo modelo de comprovante, basta criar um novo arquivo yml e os dados desejados serão extraídos normalmente.
A imagem a seguir mostra um esquema do uso da biblioteca:
Veja que basta importar a biblioteca, usar a função parse no resultado do OCR do usuário apontando o diretório onde as DRMs são encontradas. Tal função irá buscar o DRM correto (o diretório pode ter vários DRMs) para o documento em questão através do campo identifiers que será explicado logo mais. Para instalar, basta utilizar o gerenciador de pacotes Python, o pip:
pip install regex4ocr
DRMs
Para modelar um documento através de expressões regulares e extrair corretamente as informações desejadas da string de resultado do OCR utilizado, o usuário deve criar um arquivo yml com o seguinte formato:
identifiers
O campo identifiers é uma lista de expressões regulares utilizadas pela função parse na hora de buscar por um DRM adequado para interpretar a string do OCR. Tal campo é uma lista e cada palavra chave deve, obrigatoriamente, ser encontrada dentro da string de OCR. Caso algum identificador não seja encontrado, a função parse tentará aplicar o próximo DRM presente na pasta de DRMS fornecido pelo usuário.
Caso nenhum DRM seja adequado, o documento não será interpretado e um dicionário Python vazio será retornado.
Assim, para criar um DRM que seja mais específico, basta adicionar mais chaves no campo identifiers para que nenhum conflito entre DRM exista e para que a função utilize o DRM apropriado para o documento em questão.
fields
O campo fields deve ser uma lista de definições chave:valor. Cada chave conterá o nome do campo que é desejado extrair e estará presente no resultado final caso a expressão regular do valor seja encontrada no documento. É importante observar que, caso uma expressão regular contenha um grupo de captura, apenas o dado presente em tal grupo será retornado.
Por exemplo:
fields:
cnpj: 'cnpj:\s*(\d{2}\.\d{3}\.\d{3}\/\d{4}(-)?\d{2})'
date: '\d{2}\/\d{2}\/\d{4}\s*\d{2}:\d{2}:\d{2}'
Note que a expressão regular da chave cnpj contém um grupo de captura, denotado pela expressão dentro dos parênteses. Assim, apenas o valor dentro deste grupo de captura será retornado. Já no campo date, como não há um grupo de captura, será retornado todo match da expressão regular.
options
O campo options aceita algumas opções para pré-processar o a string do OCR antes de extrair qualquer campo. Assim, é importante ter em mente que, por exemplo, caso a opção lowercase estiver ativada, todas as expressões regulares nos campos fields ou table (mencionados a seguir) devem estar com letras minúsculas uma vez que o pré-processamento ocorre antes da aplicação das expressões regulares para extração de dados.
Alguns exemplos de opções:
- remover whitespaces;
- transformar a string do OCR em letras minúsculas;
- converter caracteres non-ascii para o mais próximo de ascii (á para a, etc.).
Por fim, a chave replace aceita uma listas de substituições para fazer na string do OCR. O primeiro membro de cada lista é uma expressão regular e o segundo é uma string de substituição desejada caso a expressão regular seja encontrada.
Por exemplo:
replace:
- ['c10', 'coo']
Caso a string de resultado do OCR possua a palavra "c10", este será substituído pela string "coo", antes de qualquer extração de dado. Assim, poderíamos definir uma expressão para extrair o campo "coo" sem se preocupar se este foi lido como "c10":
fields:
coo: 'coo: (/d+)' # any c10, if present, was converted to coo
table
Para o caso de comprovantes como recibos e notas fiscais, um campo table pode ser usado para definir um região tipo tabela (região onde ficam o códigos do produtos, nomes, etc. em diversas linhas).
Para tal, define-se uma regexp para o início da tabela: header e uma para delimitar o fim desta região: footer. Por fim, o campo line_start é utilizado para marcar o início de cada linha e é utilizada pra extrair cada linha dos produtos.
Exemplo
Por fim, apresentamos um exemplo em Python que transforma um string não estruturada vinda de um OCR em um dicionário com campos definidos:
E o resultado final da string de OCR é apresentado a seguir:
Melhorias para o futuro
Além da correção de possíveis erros, mais campos ou estruturas gerais podem ser introduzidas nos DRM para extrair mais dados e contemplar novos modelos de documentos.
Por fim é importante ressaltar que a abordagem utilizada na regex4ocr utiliza algoritmos tradicionais, isto é, um step-by-step de instruções para manipular os dados da string de OCR do usuário e estruturar os dados
Entretanto, já existem planos para introduzir algoritmos de aprendizado estatístico e de processamento de linguagem natural para ajudar na identificação de campos ou na correção de pequenos erros que podem surgir no processo de OCR.
E é isso aí! Quem tiver interesse em contribuir, todo código é aberto e encontra-se no repositório da JS+:
Em caso de dúvida, ou caso eu tenha dito alguma besteira (rs), peço que deixe um comentário! Dúvidas, sugestões e críticas são sempre bem vindas!
Nos vemos no próximo post!