Como ler placa de carro em Node.js em 2026
Guia prático para integrar OCR de placa veicular em Node.js. Comparativo de bibliotecas, código pronto com fetch e Express, e quanto custa rodar em produção no Brasil.
Reconhecer a placa de um carro a partir de uma foto dentro de uma aplicação Node.js parece simples até você esbarrar em três coisas: (1) Tesseract via wrapper consome muita memória, (2) modelos próprios em ONNX exigem GPU pra latência aceitável, (3) qualquer solução open-source out-of-the-box ignora completamente o padrão Mercosul brasileiro.
Este post é o guia que eu queria ter encontrado quando comecei a integrar leitura de placa em Node.js no Brasil. Vou cobrir as três abordagens viáveis em 2026, com código pronto pra rodar, comparação de precisão, e o custo real de cada uma.
TL;DR
tesseract.js(Tesseract via WASM): zero custo de servidor, ~70% de precisão depois de muito tuning. Bom pra protótipo, ruim pra produção.onnxruntime-nodecom modelo YOLOv8 + OCR: 90% de precisão, mas você vai gastar pelo menos uma semana montando a pipeline e depois pagar GPU em produção.- API gerenciada via fetch (
leituradeplaca, Plate Recognizer, etc.): 95%+ de precisão, sem infra, custa entre R$ 0,003 e R$ 0,80 por leitura. Pra entregar produto, é o caminho.
Se você quer ir direto pro código com a API, pula pra abordagem 3.
O problema na prática
A placa veicular brasileira tem dois formatos:
- Padrão antigo:
ABC1234— 3 letras + 4 dígitos - Mercosul (a partir de 2018):
ABC1D34— 3 letras + 1 dígito + 1 letra + 2 dígitos
A pipeline de OCR precisa distinguir os dois padrões e normalizar a saída. E precisa lidar com:
- Iluminação variável (foto à noite, foto contra o sol)
- Ângulo da câmera (raramente perpendicular)
- Resolução baixa (foto de celular comprimida pelo WhatsApp)
- Reflexo de farol e sujeira na placa
- Partes ocluídas pela traseira de outro veículo
Em Node.js especificamente, o desafio extra é que o ecossistema de visão computacional é mais magro do que em Python. As ferramentas existem, mas são menos maduras e menos documentadas em PT-BR.
Abordagem 1: tesseract.js (gratuito, baixa precisão)
tesseract.js é o port WASM do Tesseract. Roda 100% no Node sem dependências nativas.
npm install tesseract.js sharp
import Tesseract from 'tesseract.js'
import sharp from 'sharp'
async function readPlateTesseract(imagePath) {
// Pré-processamento essencial: grayscale, contraste, threshold
const buffer = await sharp(imagePath)
.greyscale()
.normalize()
.threshold(128)
.toBuffer()
const { data } = await Tesseract.recognize(buffer, 'eng', {
tessedit_char_whitelist: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
})
// Limpa whitespace e extrai grupos plausíveis de 7 caracteres
const clean = data.text.replace(/\s+/g, '').toUpperCase()
const match = clean.match(/[A-Z]{3}\d[A-Z0-9]\d{2}/) // Mercosul ou antigo
return match ? match[0] : null
}
Precisão real: em fotos de celular bem enquadradas, ~70%. Em fotos de câmera de segurança real (ângulo, baixa luz), cai pra ~40%. Você vai precisar de muito tuning de threshold por ambiente.
Custo: zero (CPU only).
Quando faz sentido: protótipo de fim de semana, hackathon, prova de conceito interna. Não use em produção.
Abordagem 2: onnxruntime-node + modelo customizado
Se você quer rodar inferência local com qualidade, o caminho moderno é exportar um modelo treinado (YOLOv8 pra detecção de placa + um OCR head próprio) pra ONNX e rodar com onnxruntime-node.
npm install onnxruntime-node sharp
import * as ort from 'onnxruntime-node'
import sharp from 'sharp'
const detector = await ort.InferenceSession.create('./yolov8-plate.onnx')
const recognizer = await ort.InferenceSession.create('./crnn-plate-br.onnx')
async function readPlateONNX(imagePath) {
// 1. Detecta a região da placa
const inputTensor = await preprocessForYolo(imagePath)
const detectorOut = await detector.run({ images: inputTensor })
const bbox = parseYoloOutput(detectorOut.output0)
if (!bbox) return null
// 2. Recorta e passa para o reconhecedor
const plateCrop = await sharp(imagePath)
.extract({ left: bbox.x, top: bbox.y, width: bbox.w, height: bbox.h })
.resize(160, 32)
.toBuffer()
const ocrTensor = bufferToTensor(plateCrop, [1, 1, 32, 160])
const ocrOut = await recognizer.run({ input: ocrTensor })
return decodeCTC(ocrOut.output)
}
O problema: as funções preprocessForYolo, parseYoloOutput, bufferToTensor, decodeCTC são ~300 linhas de código que você precisa escrever (ou copiar de um repo desatualizado). E o modelo crnn-plate-br.onnx precisa ser treinado por você num dataset brasileiro — os modelos prontos do GitHub geralmente foram treinados em placas EU/US e erram feio em Mercosul.
Precisão real: com modelo bem treinado, ~90%. Mas chegar nesses 90% leva pelo menos 2 semanas de trabalho.
Custo: uma máquina com GPU custa ~R$ 600/mês na DigitalOcean. Sem GPU, latência sobe pra 4-6 segundos por imagem em CPU decente.
Quando faz sentido: você tem volume gigante (>500k leituras/mês), tem ML engineer no time, e o custo unitário da API começa a doer. Abaixo disso, é prejuízo de tempo.
Abordagem 3: API gerenciada {#abordagem-3-api-gerenciada}
Aqui é onde 95% dos projetos Node.js que precisam de leitura de placa terminam — e por bom motivo: você integra em 5 minutos, não mantém infra, e a precisão é maior que qualquer modelo open-source out-of-the-box.
Exemplo com leituradeplaca + fetch nativo
import { readFileSync } from 'node:fs'
async function readPlate(imagePath) {
const imageBuffer = readFileSync(imagePath)
const base64 = imageBuffer.toString('base64')
const res = await fetch('https://leituradeplaca.com.br/api/v1/read-plate', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.LDP_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ image_base64: base64 }),
})
if (!res.ok) throw new Error(`API error ${res.status}: ${await res.text()}`)
return res.json()
}
// Uso
const result = await readPlate('./carro.jpg')
console.log(result)
// → { plate: 'INO1102', confidence: 0.94, error: null, latency_ms: 2410 }
Sem SDK, sem dependência. fetch é nativo no Node 18+.
Variante com multipart (arquivo direto, sem base64)
Se você está recebendo upload via Express/Fastify e quer evitar o overhead do base64:
import express from 'express'
import multer from 'multer'
const app = express()
const upload = multer({ storage: multer.memoryStorage() })
app.post('/leitura', upload.single('foto'), async (req, res) => {
const formData = new FormData()
formData.append('image', new Blob([req.file.buffer]), req.file.originalname)
const apiRes = await fetch('https://leituradeplaca.com.br/api/v1/read-plate', {
method: 'POST',
headers: { 'Authorization': `Bearer ${process.env.LDP_API_KEY}` },
body: formData,
})
res.json(await apiRes.json())
})
app.listen(3000)
Tratamento de erros e retry
Em produção, sempre encapsule em retry com backoff exponencial — qualquer API HTTP vai ter um 502 ocasional:
async function readPlateWithRetry(imagePath, maxAttempts = 3) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await readPlate(imagePath)
} catch (err) {
if (attempt === maxAttempts) throw err
await new Promise(r => setTimeout(r, 2 ** attempt * 200))
}
}
}
Precisão real (leituradeplaca): 95%+ em fotos com placa visível, mesmo com ângulo torto e baixa luz. Suporta Mercosul e padrão antigo nativamente.
Latência: entre 1.000 e 4.200 ms. Se a sua aplicação não pode bloquear, processa numa fila (BullMQ, SQS) e responde via webhook.
Custo: R$ 0,05 por leitura no plano Trial (100 leituras = R$ 4,99), descendo até R$ 0,003/leitura no Diamond (150k/mês). Pra contexto: o equivalente self-hosted custa R$ 600+/mês de GPU antes de bater na precisão equivalente.
Comparativo
| Critério | tesseract.js | ONNX próprio | API gerenciada |
|---|---|---|---|
| Precisão (real) | ~70% | ~90% (com treino) | 95%+ |
| Latência | 800-2000 ms | 200-800 ms (GPU) / 4-6s (CPU) | 1000-4200 ms |
| Custo R$/1000 leituras | 0 (só CPU) | ~R$ 50 (GPU amortizada) | R$ 6 a R$ 50 |
| Tempo até produção | 1 dia | 2-4 semanas | 1 hora |
| Manutenção | Tuning constante | Retreino, monitor de drift | Zero |
| Mercosul out-of-the-box | Não | Não (precisa treinar) | Sim |
Qual escolher
- Pra prototipar / brincar:
tesseract.js. Você descobre rápido o que funciona e o que não. - Pra produção até ~50k leituras/mês: API gerenciada. Custo previsível, sem dor de cabeça operacional.
- Pra produção acima de 500k leituras/mês com ML engineer disponível: considere ONNX self-hosted, mas faça as contas honestas — incluindo o tempo de quem mantém.
Próximos passos
Se você quer testar a API agora, crie uma conta em leituradeplaca.com.br/signup e ganhe 100 leituras de trial por R$ 4,99. A documentação completa está em /docs com exemplos em Node.js, Python, PHP e cURL.
Para pegar o código de exemplo deste post como repositório completo (com testes e CI), me avisa em contato@leituradeplaca.com.br — posso publicar como referência.
Pronto para integrar?
Crie uma conta e ganhe acesso à API em menos de 60 segundos.