Como usamos os algoritmos de visão computacional: processamento de vídeo em um navegador móvel usando o OpenCV.js

Já existem todas as possibilidades para identificar uma pessoa online, mas até agora elas raramente são usadas. Talvez tenhamos sido os primeiros a implementar o cenário ideal para o usuário - faça login no site usando um smartphone, tire uma foto da sua carteira de motorista ou passaporte e envie dados ao sistema.

Vamos considerar como os algoritmos de visão computacional ajudam a reconhecer documentos em um fluxo de vídeo diretamente em navegadores móveis. Neste artigo, compartilhamos nossa experiência de como usamos o OpenCV.js para isso no SimbirSoft, quais são as dificuldades possíveis, como garantir velocidade e obter um UX "suave" sem diminuir a velocidade.




Qual foi a tarefa


O cenário de negócios para o algoritmo que está sendo desenvolvido é o seguinte. Um usuário que acessa o site a partir de um telefone celular deve poder fotografar seus documentos e enviá-los ao sistema para processamento adicional. Isso pode fazer parte do processo de identidade ao solicitar o uso de qualquer serviço.

Um aplicativo Web neste cenário é preferível a um aplicativo móvel devido à sua disponibilidade e tempo reduzido para concluir a operação. A página da web não precisa de instalação e está pronta para funcionar imediatamente após o carregamento. O usuário pode prosseguir para executar as ações necessárias - enviando um aplicativo - imediatamente após receber o link, sem se distrair com ações adicionais. Da perspectiva dos negócios, esses fatores aumentam a conversão e a eficácia comercial do processo.

Do ponto de vista arquitetural, o algoritmo é necessário para detectar diretamente os limites do documento e cortar o excesso de fundo na imagem. A verificação de identidade, autenticação e verificações de fraude serão implementadas por outros componentes. No entanto, é aconselhável realizar pelo menos verificações mínimas para excluir o envio de cartões de visita, retângulos de papel vazios e outras imagens obviamente irrelevantes para o processamento de imagens.

Exigências


Como parte do nosso projeto, havia os seguintes requisitos adicionais para o algoritmo:

  • a capacidade de trabalhar em tempo real: o fluxo de vídeo da câmera não deve "desacelerar" durante a operação do algoritmo;
  • a capacidade de trabalhar em uma ampla variedade de contraste e textura de fundo: baixo contraste e contraste, fundo homogêneo e heterogêneo;
  • Suporte para uma ampla gama de modelos de smartphones, incluindo modelos de orçamento lançados há vários anos.

Por fim, não havia um conjunto de dados para o treinamento de algoritmos de aprendizado de máquina no projeto e não havia como coletar e marcar. Tivemos apenas algumas amostras de teste dos resultados de pesquisa do Google.

Dada essa afirmação do problema, decidimos desenvolver com base nos algoritmos clássicos de visão computacional da biblioteca opencv. Uma possibilidade alternativa era o uso de algoritmos de aprendizado de máquina e redes neurais, no entanto, ele já era descartado nos estágios iniciais do trabalho devido a requisitos de desempenho: quando aplicado, não seria possível fornecer processamento de quadro em tempo real em todos os dispositivos de destino.

Abordagem geral e estrutura de algoritmos


A idéia principal do algoritmo é um quadro de referência, ao longo do qual é necessário alinhar o documento. Seu uso persegue vários objetivos ao mesmo tempo. Em primeiro lugar, fornecerá um tamanho de imagem adequado, suficiente para o processamento posterior de documentos. Em segundo lugar, como veremos mais adiante, ele pode ser usado como um dos filtros candidatos ao procurar bordas do documento. Em terceiro lugar, ele pode ser usado para capturar e cortar a imagem se as bordas do documento não puderem ser encontradas.



FIG. 1. A estrutura geral do algoritmo

A estrutura geral do algoritmo é mostrada na Fig. 1. Os quadros do fluxo de vídeo são processados ​​em um ciclo, entre as iterações em que o tempo limite é definido para atender ao FPS desejado - paramos a 30 quadros por segundo. Isso permite evitar "lentidão" e reduzir a carga no processador e o consumo de energia do dispositivo.

Cada quadro processado passa por pré-processamento, durante o qual duas operações principais são executadas. Primeiramente, é criada uma cópia de um quadro de tamanho fixo de 640x480, com o qual as etapas adicionais do algoritmo funcionam. A imagem original também permanece, o documento detectado será cortado dela. Isso salvará a qualidade da imagem final. Em segundo lugar, a cópia criada é traduzida em tons de cinza. A cor do documento que está sendo processado é ignorada pelo algoritmo, pois pode variar de país para país e até em diferentes regiões do país - um exemplo é uma carteira de motorista nos Estados Unidos.

O primeiro passo para detectar um documento é procurar o rosto na imagem. O uso dessa heurística elimina a captura de cartões de visita e outras imagens obviamente irrelevantes. A pesquisa é realizada usando o padrão cascadeClassifier.detectMultiScale () do opencv e a cascata pré- treinada haarcascade_frontalface_default . Os tamanhos mínimo e máximo de faces detectadas são limitados, o que reduz os custos computacionais e também limita ainda mais a escala do documento na imagem. Um rosto é considerado detectado na imagem quando está no lado esquerdo - ou no lado esquerdo inferior, para passaportes - parte da área dentro do quadro de referência (Fig. 2). Essa é uma medida adicional para garantir o alinhamento correto do documento na imagem.

Os exemplos neste artigo não contêm dados pessoais.



FIG. 2. A área da posição esperada do rosto na imagem. O quadro de suporte é mostrado em vermelho, as bordas da área da localização esperada da face são mostradas em

verde.Após a detecção de faces, prosseguimos para a detecção de bordas. Muitas vezes, findContours () é usado aqui . No entanto, essa abordagem funciona bem apenas em casos contrastantes, por exemplo, para uma folha de papel sobre uma mesa escura. Se o contraste for mais baixo, ou a iluminação piorar, ou alguém estiver segurando uma folha nas mãos, cobrindo parte da borda com os dedos, os contornos detectados se dividem em componentes separados, “perdem” seções significativas ou não são detectados.

Portanto, adotamos uma abordagem diferente. Após a binarização, passamos a imagem pelo filtro de borda usando Canny () e, em seguida, observamos a imagem resultante da linha usando a transformação Huff HoughLines () . O parâmetro threshold é definido imediatamente grande o suficiente, igual a 30 - para filtrar segmentos curtos detectados e outros irrelevantes.

O conjunto de linhas resultante é filtrado adicionalmente, deixando apenas linhas próximas ao quadro de referência. Para fazer isso, primeiro traduzimos as equações das linhas de quadro em pontos no sistema de coordenadas polares (rho, theta) - theta sempre será 0 ou pi / 2 e rho será exclusivo para cada linha. Depois disso, selecionamos a partir das linhas obtidas a partir da transformação Huff apenas aquelas que se encontram nas proximidades dos pontos de controle - de acordo com a métrica euclidiana, levando em consideração a diferença na escala dos valores.

Distribuímos o conjunto de linhas obtido após a filtragem em quatro grupos correspondentes às quatro linhas do quadro de referência, encontramos as interseções das linhas em pares entre os grupos, calculamos a média e obtemos as coordenadas dos quatro pontos - os cantos do documento detectado (Fig. 3).



FIG. 3. Filtrando linhas e definindo cantos do documento. Linhas verdes - o resultado da filtragem, pontos amarelos - detectaram os cantos do documento.Em

seguida, você precisa garantir a qualidade do quadro. Para fazer isso, verificamos que o quadro permaneceu parado pela última vez. Para fazer isso, subtraia o quadro no início do período do quadro atual usando absdiff () e compare-o com o limite. Antes da subtração, suavizamos as imagens com um filtro Gaussian GaussianBlur () para reduzir a influência do ruído e outros fatores aleatórios. Também avaliamos o foco do quadro calculando seu Laplaciano Laplaciano () , estimando sua variância e comparando o valor obtido com um limiar.

Se todas as verificações forem bem-sucedidas, você poderá prosseguir para a parte final. Recalculamos as coordenadas detectadas dos ângulos no sistema de coordenadas da imagem original subexposta e cortamos a região resultante usando o método roi () . O documento foi detectado com sucesso.

Recursos de implementação


Durante o desenvolvimento do algoritmo, seus principais componentes foram montados em um script python. Depois disso, o algoritmo foi portado para opencv.js e javascript e, em seguida, para wasm. Essa abordagem é ditada por considerações de conveniência em todas as etapas. No python, era mais conveniente para nossa equipe experimentar várias variantes do algoritmo e realizar configurações aproximadas de parâmetros. Portar para javascript tornou possível testar a operação do algoritmo na plataforma de destino, inclusive em vários dispositivos e navegadores. Com base nos resultados dessas verificações, foi realizado o ajuste fino dos parâmetros do algoritmo. Finalmente, reescrever seções críticas de código no wasm nos permitiu obter um aumento adicional de desempenho.

Durante a migração, várias diferenças foram descobertas na API do OpenCV, o que resultou em pequenas alterações na implementação. Por exemplo, a variação de um Laplaciano em python é considerada simplesmente como Laplacian (). Var () . Com o OpenCV.js, não há como usar o NumPy, mas nenhuma implementação alternativa do método var () foi fornecida. Solução: conte a função meanStdDev () como o desvio padrão (Listagem 1).

private isImageBlurry(image: cv.Mat): boolean {
		const laplacian = new cv.Mat();
		cv.Laplacian(image, laplacian, cv.CV_64F);
		const s_mat = new cv.Mat();
		cv.meanStdDev(laplacian, new cv.Mat(), s_mat);
		const s = s_mat.data64F[0];
		const v = Math.pow(s, 2);
		return (v < this.laplacianVarianceThreshold);
	}

Listagem 1. Avaliando o foco na imagem através da variação do Laplacian no opencv.js (TypeScript)

Outro recurso foi a necessidade de reduzir o tamanho da biblioteca. Na sua forma original, o OpenCV.js tem uma capacidade de 7,9 MB. Seu download via Internet atrasa a inicialização do algoritmo. A solução para esse problema é “aparar” os módulos não utilizados durante o processo de montagem da biblioteca, o que pode reduzir significativamente o tamanho do arquivo de saída: conseguimos obter um tamanho de 1,8 MB. A lista de componentes incluídos na montagem pode ser configurada nas plataformas de arquivo de configuração / js / opencv_js.config.py (Listagem 2).

white_list = makeWhiteList([core, imgproc, objdetect, video, dnn, features2d, photo, aruco, calib3d])

Listagem 2. A lista branca original de módulos opencv incluídos no assembly para javascript

Finalmente, uma contribuição importante para garantir o desempenho necessário do algoritmo foi feita movendo-o para o Web Worker. Essa etapa, juntamente com a restrição do FPS, nos permitiu livrar-nos das "lentidões" do fluxo de vídeo durante a operação do algoritmo, que tiveram um efeito positivo no UX.

resultados


Exemplos de captura e corte de imagens são mostrados na Fig. 4. Pode-se observar que o corte da mais alta qualidade é obtido em um fundo uniforme escuro e a qualidade mais baixa é obtida com um fundo claro e não homogêneo. Esse é o efeito esperado associado aos gradientes obtidos em diferentes planos de fundo e usados ​​para detectar as bordas de um documento. Em um fundo escuro, os gradientes são maiores do que em um fundo claro; um fundo uniforme leva a uma menor variabilidade dos valores do gradiente. Isso leva a uma detecção confiável dos limites e, como resultado, a um melhor corte.




FIG. 4. Exemplos de corte de documentos usando um algoritmo

Conclusão


O artigo apresenta um algoritmo para detectar documentos em quadros de um fluxo de vídeo adequado para uso em navegadores móveis e também considera os recursos de sua implementação usando a biblioteca opencv.js. O algoritmo permite obter a imagem de saída dos documentos em uma qualidade suficiente para uso posterior por algoritmos para autenticação, verificação de identidade etc. A velocidade da implementação resultante permite obter um UX "suave" sem "lentidão" e perda de quadros.

Obrigado pela atenção! Esperamos que você ache este artigo útil.

All Articles