OCR na edge
Nesta página
A tecnologia avançada virou rotina, e a visão computacional virou uma ferramenta quase obrigatória, com aplicações por todo lado.
Ela identifica objetos, padrões, pessoas, plantas e bichos de estimação. Dá informação útil para o usuário, ajuda a decidir e facilita a garimpagem de conjuntos enormes de dados.
Junte isso à demanda crescente por análise em tempo real e respostas de baixa latência, e a edge compute virou o melhor lugar para rodar o seu modelo treinado.
O que é OCR?
OCR (Optical Character Recognition, ou reconhecimento óptico de caracteres) é o processo de converter imagens de texto em texto codificado para máquina. Costuma servir para transformar imagens de documentos em texto que dá para pesquisar e indexar.
Você pode usar para escanear documentos, recibos, cartões de visita e mais. Dá para ver por que isso é útil na Blinq.
O que é edge compute?
Edge compute é rodar o seu código o mais perto possível do usuário. Normalmente isso acontece em uma CDN, como o Cloudflare Workers ou o AWS Lambda@Edge.
É diferente da computação em nuvem tradicional, em que o código roda em um data center que costuma ficar longe do usuário.
Por que edge compute?
Dá para rodar o modelo no próprio dispositivo, mas deixar na edge tem algumas vantagens:
- Baixa latência: o modelo fica perto do usuário, então a resposta é mais rápida. Parece que está no aparelho.
- Baixo custo: você não paga pelos recursos de computação do modelo, só pelas requisições.
- Fácil de atualizar: você atualiza o modelo sem atualizar o app. Isso ajuda muito quando muita gente usa versões antigas do app.
Na Blinq a gente mantém uma cauda longa de versões do app para celular. Poder atualizar o modelo sem atualizar o app é uma vitória enorme para a gente.
Como rodar o seu modelo na edge
Runtimes de edge como o Cloudflare Workers e o Lambda@Edge são bem diferentes de runtimes tradicionais como o Node.js e o Golang. Eles são feitos para ser rápidos e leves, então não têm acesso às mesmas bibliotecas e APIs.
Lembram muito um código rodando no navegador. Você não pode copiar e colar o seu código e esperar que funcione.
O modelo
Neste exemplo uso um modelo de OCR simples, o Keras OCR. É um modelo do TensorFlow que recebe uma imagem e devolve o texto que encontra nela.
Também conto com o recurso Constellation da Cloudflare (em beta), que permite rodar modelos ONNX na edge. É uma ótima opção nova, porque deixa você rodar modelos que o TensorFlow.js não suporta e que não esbarram na cota de 10MB de um Worker da Cloudflare.
O Lambda@Edge tem limites bem mais altos, mas ainda vale manter o modelo o menor possível para reduzir o tempo de cold start.
Neste exemplo usei o modelo crnn_kurapan, disponível aqui.
Convertendo Keras para ONNX
O primeiro passo é converter o modelo Keras para ONNX. O processo é simples, mas tem alguns passos. Estou supondo que você tenha o Conda instalado e use o Python 3.10.
Se você usa Apple Silicon, também vai precisar de:
tensorflow-macos
Primeiro, crie um ambiente conda.
$ conda create -n edge-onnx python=3.10 pip
$ conda activate edge-onnx
Depois, instale as dependências.
$ python -m tensorflow tf2onnx keras-ocr
Se você usa Apple Silicon, instale o tensorflow-macos no lugar do tensorflow.
$ python -m pip install tensorflow-macos
Por fim, converta o modelo com este trecho.
import tf2onnx
import onnx
import keras_ocr
recognition = keras_ocr.recognition.Recognizer(
alphabet='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', weights='kurapan')
output_path = 'crnn_kurapan.onnx'
model_proto, _ = tf2onnx.convert.from_keras(
recognition.model, opset=None, output_path=output_path)
onnx.save(model_proto, output_path)
Isso cria um modelo ONNX no diretório atual, em crnn_kurapan.onnx.
Rodando o modelo na edge
Para rodar o modelo ONNX na edge, usei o Cloudflare Constellation, porque o modelo é grande demais para o Cloudflare Workers.
Dá para fazer pelo painel da Cloudflare ou pela CLI. Estou usando a CLI porque é mais fácil de automatizar.
Primeiro, precisamos criar um projeto.
$ npx wrangler constellation project create "ocr" ONNX
$ npx wrangler constellation project list
Anote o ID do projeto.
Depois, envie o modelo:
$ wrangler constellation model upload ocr crnn_kurapan crnn_kurapan.onnx
$ npx wrangler constellation model list ocr
Anote o ID do modelo.
Em seguida, monte o projeto e inicialize o wrangler.
$ npm create cloudflare@2
Chamei o projeto de ocr e usei o template ‘Hello World’.
No arquivo wrangler.toml, adicione o binding do constellation e ative o node_compat.
name = "ocr"
main = "src/index.js"
node_compat = true
workers_dev = true
compatibility_date = "2023-07-01"
constellation = [
{binding = 'OCR', project_id = '{{ project ID from earlier }}'},
]
Por fim, instale o cliente do constellation e as dependências para mandar as imagens em stream para o modelo.
npm install --save @cloudflare/constellation string-to-stream pngjs
Agora podemos escrever o código que roda o modelo.
import str from 'string-to-stream';
import { PNG } from 'pngjs/browser';
import { Tensor, run } from '@cloudflare/constellation';
const MODEL_ID = '{{ model ID from earlier }}';
function normalizeImage(data) {
return new Promise(async (resolve, reject) => {
const stream = str(data);
const png = new PNG({ filterType: 4 });
stream
.pipe(png)
.on('parsed', function () {
const [r, g, b] = new Array(3).fill([]);
for (let i = 0; i < this.data.length; i += 4) {
r.push(this.data[i] / 255.0);
g.push(this.data[i + 1] / 255.0);
b.push(this.data[i + 2] / 255.0);
}
resolve({
input: [...r, ...g, ...b],
shape: [3, this.height, this.width, 3],
});
})
.on('error', function (error) {
reject({ err: error.toString() });
});
});
}
export default {
async fetch(request, env) {
if (request.method !== 'POST') {
return new Response('Method not allowed', { status: 405 });
}
const formData = await request.formData();
const image = formData.get('image');
if (!image) {
return new Response('No image found', { status: 400 });
}
const buffer = await image.arrayBuffer();
try {
const normalized = await normalizeImage(buffer);
if (!normalized) {
return new Response('Unable to normalize image', { status: 500 });
}
const input = new Tensor(
'float32',
normalized.shape,
normalized.input,
'input_1'
);
const output = await run(env.OCR, MODEL_ID, input);
return new Response(JSON.stringify(output), { status: 200 });
} catch (err) {
return new Response(err, { status: 500 });
}
},
};
Enquanto estiver em beta, talvez você precise desativar os avisos do constellation com export NO_CONSTELLATION_WARNING=true.
Quando estiver pronto para o deploy, rode wrangler deploy.
Testando o modelo
Agora você pode fazer uma requisição HTTP para o Worker e testar o modelo.
$ curl -X POST -F "
image=@/path/to/image.png" https://ocr.example.workers.dev
Isso devolve uma resposta JSON com a saída do modelo.
{
"output_1": [...]
}
A saída do modelo é um array de probabilidades para cada caractere. Para converter em texto, você pode usar algo parecido com este trecho.
const charset =
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
const text = output
.map((probabilities) => {
const max = Math.max(...probabilities);
const index = probabilities.indexOf(max);
return charset[index];
})
.join('');
Por que fazer isso?
Eu queria ver se dava para rodar um modelo grandinho na edge. Também queria saber como ele se sairia.
Esse modelo dificilmente vai para produção, mas mostra bem o que o Cloudflare Workers e o Constellation já permitem. Modelos de OCR maiores costumam render melhor, mas dá para usar modelos menores se você aceitar perder precisão. Para isso, aliás, sempre existe o Tesseract.js.
Desempenho
O modelo leva cerca de 1,5 segundo para rodar na edge. É bem mais lento que rodar local, mas ainda é rápido o bastante para produção. No mundo real, você também pode usar as ofertas do Google e da Amazon Web Services, que são bem mais precisas. Só que também são bem mais caras.
Precisão
O modelo é razoavelmente preciso, mas não é perfeito. Ele tem dificuldade com algumas fontes e vai mal com texto em imagens com muito ruído. Também só aceita imagens pequenas, mas dá para melhorar isso com uma janela deslizante.
Teste você mesmo
A Cloudflare está desenvolvendo o Constellation ativamente, e ele ainda está em beta. Se quiser testar, você pode se inscrever no beta aqui.
Também existe um canal no Discord onde você tira dúvidas e pede ajuda ao time da Cloudflare.