Migrando do Veryfi para o IronOCR
Este guia orienta os desenvolvedores .NET na substituição da API de processamento de documentos em nuvem da Muito bom pelo IronOCR , uma biblioteca OCR local. Este documento aborda a troca de pacotes, a limpeza do namespace e quatro exemplos completos de migração de código focados nos padrões mais comumente utilizados no Veryfi: inicialização do cliente, extração de campos baseada em região, categorização de despesas com dados estruturados e substituição de webhooks. Não é necessário ler o artigo comparativo previamente.
Por que migrar do Veryfi?
Os documentos financeiros fluem pelo sistema da Muito bom em uma única direção: da sua infraestrutura para a deles. Esse fato arquitetônico impulsiona a maioria das migrações. Aqui estão os principais problemas que levam as equipes a fazer a mudança.
Cada solicitação de documento transmite dados financeiros confidenciais para um servidor de terceiros. Os recibos contêm os quatro últimos dígitos do cartão e informações sobre o fornecedor. As faturas contêm números de contas bancárias, números de roteamento e números de identificação fiscal do fornecedor. Os extratos bancários contêm o histórico completo das transações. Com a Veryfi, cada chamada de ProcessDocumentAsync faz upload desses bytes para api.veryfi.com, processa-os na infraestrutura da Muito bom e retorna JSON. Seu controle sobre esses dados termina no momento em que a solicitação HTTP é enviada.
Quatro credenciais são necessárias e devem ser mantidas em sincronia em todos os ambientes. VeryfiClient requer clientId, clientSecret, username e apiKey - quatro segredos separados para armazenar na configuração, rodar conforme o cronograma, injetar em pipelines CI/CD e auditar quanto à exposição. Uma única falha de credencial compromete a autenticação de todos os documentos processados em toda a aplicação. O IronOCR requer uma única chave de licença.
O preço por documento é cumulativo, sem limite máximo. Os recibos custam aproximadamente de US$ 0,05 a US$ 0,15 cada, as faturas de US$ 0,10 a US$ 0,25 e os extratos bancários de US$ 0,15 a US$ 0,30. Com 50.000 documentos por mês, isso representa um custo de US$ 5.000 a US$ 15.000 por mês em faturamento por consumo, sem redução no segundo ou terceiro ano. A licença IronOCR Professional , a US$ 2.999, cobre um número ilimitado de documentos de forma perpétua - o ponto de equilíbrio em relação aos US$ 5.000 gastos mensais com a Muito bom ocorre em menos de três semanas.
A API é exclusivamente assíncrona porque o trabalho subjacente é remoto. ProcessDocumentAsync não é assíncrono porque o processamento é computacionalmente longo; É assíncrono porque o documento precisa ser enviado para um servidor, entrar em fila atrás de outras solicitações, concluir a inferência e retornar uma resposta pela rede. A latência não é determinística. A limitação de taxa de requisições HTTP 429 requer lógica de repetição. Erros de pagamento HTTP 402 interrompem completamente o processamento em lote. Erros HTTP 500 na infraestrutura da Muito bom comprometem seu fluxo de trabalho.
O escopo de documentos do Muito bom termina no limite do documento de despesa. Os modelos treinados retornam campos estruturados de forma confiável para recibos, faturas, cheques, extratos bancários, formulários W-2 e cartões de visita. Fora dessa lista - documentos comerciais gerais, contratos, registros médicos, documentos de remessa, formulários internos personalizados - os resultados são inferiores ou exigem treinamento pago em modelos personalizados. Organizações que adotam o Muito bom para automação de despesas normalmente descobrem, dentro de 6 a 12 meses, que outras equipes precisam de OCR para documentos que o Muito bom não foi projetado para processar.
O esquema JSON proprietário da Muito bom acopla toda a lógica de extração a um único fornecedor. Cada linha de código que lê response.Vendor?.Name, response.BankAccount?.RoutingNumber ou response.LineItems é código que só funciona com a Veryfi. Mudar de fornecedor - ou optar por um sistema OCR local - significa reescrever toda a lógica de extração do zero.
O problema fundamental
// Veryfi: financial data leaves your infrastructure on every call
var client = new VeryfiClient(clientId, clientSecret, username, apiKey); // 4 secrets
var bytes = File.ReadAllBytes("invoice-with-routing-number.pdf");
var response = await client.ProcessDocumentAsync(bytes); // bank details transmitted
var routingNumber = response.BankAccount?.RoutingNumber; // arrived via Muito bom cloud
// IronOCR: routing numbers never leave your server
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; // 1 key
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf("invoice-with-routing-number.pdf"); // processed locally
var result = ocr.Read(input);
var routingNumber = Regex.Match(result.Text, @"Routing\s*#?\s*:?\s*(\d{9})").Groups[1].Value;
##IronOCR vs Veryfi: Comparação de Recursos
A tabela abaixo mapeia as funcionalidades de ambos os produtos para auxiliar na avaliação técnica.
| Recurso | Muito bom | IronOCR |
|---|---|---|
| Local de processamento | Servidores em nuvem Muito bom | Sua infraestrutura |
| Modelo de implantação | Somente API em nuvem | Local, Docker, Azure, AWS, Linux |
| Suporte offline | Não | Sim |
| É necessário ter acesso à internet. | Sim (todos os documentos) | Não |
| Os dados saem da infraestrutura. | Sim (em todas as chamadas) | Nunca |
| Compatível com HIPAA sem BAA | Não | Sim |
| Suporte para ambiente isolado (air-gapped) | Não é possível. | Suporte completo |
| Modelo de preços | Por documento (US$ 0,05 a US$ 0,30) | Licença perpétua ($999–$2,399) |
| Credenciais necessárias | 4 (clientId, clientSecret, username, apiKey) | 1 chave de licença |
| API síncrona | Não (somente assíncrono) | Sim |
| Limitação de taxa | Sim (HTTP 429) | None |
| Escopo do documento | Recibos, faturas, cheques, extratos bancários, formulários W-2, cartões de visita. | Qualquer tipo de documento |
| Tipos de documentos personalizados | Treinamento remunerado para modelos é obrigatório. | Qualquer layout via extração de regex/padrão |
| Entrada de PDF | Sim (upload de bytes) | Sim (nativo, local) |
| Saída em PDF pesquisável | Não | Sim (result.SaveAsSearchablePdf()) |
| OCR baseado em região | Não | Sim (CropRectangle) |
| Leitura de código de barras | Não | Sim (mesma aprovação do OCR) |
| Acesso estruturado aos resultados | Campos JSON pré-analisados | Páginas, parágrafos, linhas, palavras com coordenadas |
| Pontuação de confiança | Por campo (proprietário) | Por palavra e no geral (result.Confidence) |
| Suporte para mais de 125 idiomas | Limitado | Sim (pacotes de idiomas NuGet ) |
| Processamento paralelo seguro para threads | Aplicam-se limites de simultaneidade HTTP. | Completo (um IronTesseract por thread) |
| Testes unitários sem mocks | Requer simulação HTTP | Testes locais diretos |
Guia rápido: Migração do Muito bom para o IronOCR
Passo 1: Substitua o pacote NuGet
Remova o SDK Veryfi:
dotnet remove package Veryfi
Instale o IronOCR a partir do NuGet :
Etapa 2: Atualizar Namespaces
Substitua os namespaces Muito bom pelo namespace IronOCR:
// Before (Veryfi)
using Veryfi;
using Veryfi.Models;
// After (IronOCR)
using IronOcr;
using System.Text.RegularExpressions;
Etapa 3: Inicializar a licença
Adicione esta linha uma única vez na inicialização do aplicativo, antes de qualquer chamada de OCR:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"Exemplos de migração de código
Substituição de Cliente de Processamento de Documentos
Os serviços Muito bom são construídos em torno da injeção de construtores de VeryfiClient. O construtor de quatro credenciais é uma solução natural para injeção de dependência, mas cria quatro segredos que precisam ser gerenciados e rotacionados. Substituir isso pelo IronOCR consolida as credenciais em uma única chave de licença e move a instanciação do mecanismo de processamento para a própria classe de serviço.
Abordagem Veryfi:
using Veryfi;
using Microsoft.Extensions.Configuration;
public class ExpenseDocumentService
{
private readonly VeryfiClient _client;
// Four credentials injected — four secrets to manage, store, rotate
public ExpenseDocumentService(IConfiguration config)
{
_client = new VeryfiClient(
config["Veryfi:ClientId"], // secret 1
config["Veryfi:ClientSecret"], // secret 2
config["Veryfi:Username"], // secret 3
config["Veryfi:ApiKey"] // secret 4
);
}
public async Task<string> GetVendorNameAsync(string documentPath)
{
var bytes = File.ReadAllBytes(documentPath);
// Document uploaded to Muito bom on this call
var response = await _client.ProcessDocumentAsync(bytes);
return response.Vendor?.Name;
}
public async Task<decimal?> GetTotalAsync(string documentPath)
{
var bytes = File.ReadAllBytes(documentPath);
var response = await _client.ProcessDocumentAsync(bytes);
return response.Total;
}
}
Abordagem IronOCR:
using IronOcr;
using System.Text.RegularExpressions;
public class ExpenseDocumentService
{
private readonly IronTesseract _ocr;
// One license key — set once at startup, not per-instance
public ExpenseDocumentService()
{
_ocr = new IronTesseract();
}
public string GetVendorName(string documentPath)
{
// All processing local — document bytes never leave this server
var result = _ocr.Read(documentPath);
// Vendor is typically the first non-whitespace line on a receipt
return result.Pages[0].Paragraphs
.OrderBy(p => p.Y)
.Select(p => p.Text.Trim())
.FirstOrDefault(t => t.Length > 3);
}
public decimal? GetTotal(string documentPath)
{
var result = _ocr.Read(documentPath);
var match = Regex.Match(result.Text,
@"(?:Total|Grand Total|Amount Due):?\s*\$?\s*([\d,]+\.\d{2})",
RegexOptions.IgnoreCase);
return match.Success
? decimal.Parse(match.Groups[1].Value.Replace(",", ""))
: (decimal?)null;
}
}
A alteração do construtor elimina quatro entradas de configuração de cada ambiente: appsettings.json, segredos do Docker, referências do Azure Key Vault e variáveis de pipeline CI/CD. A instância IronTesseract é reutilizável em várias chamadas na mesma thread. Consulte o guia de configuração do IronTesseract para obter informações sobre padrões de registro singleton em contêineres de injeção de dependência do ASP.NET Core .
Extração de campos de recibo com OCR baseado em região
O Muito bom extrai os campos do recibo executando seus modelos de aprendizado de máquina treinados em toda a imagem do documento e retornando uma resposta JSON pré-estruturada. O equivalente do IronOCR é OCR baseado em região usando CropRectangle, que visa zonas específicas da imagem de recibo - zona de cabeçalho para fornecedor, zona de rodapé para totais - ao invés de executar uma passagem de página completa e procurar padrões na saída. Este método é mais rápido para layouts conhecidos e mais preciso quando a área de interesse está bem definida.
Abordagem Veryfi:
using Veryfi;
public class ReceiptFieldExtractor
{
private readonly VeryfiClient _client;
public ReceiptFieldExtractor(VeryfiClient client)
{
_client = client;
}
public async Task<(string Vendor, decimal? Total, decimal? Tax)>
ExtractReceiptFieldsAsync(string imagePath)
{
var bytes = File.ReadAllBytes(imagePath);
// Full document uploaded — Veryfi's ML returns structured fields
var response = await _client.ProcessDocumentAsync(bytes);
return (
Vendor: response.Vendor?.Name,
Total: response.Total,
Tax: response.Tax
);
}
}
Abordagem IronOCR:
using IronOcr;
using System.Text.RegularExpressions;
public class ReceiptFieldExtractor
{
private readonly IronTesseract _ocr = new IronTesseract();
public (string Vendor, decimal? Total, decimal? Tax)
ExtractReceiptFields(string imagePath)
{
// Region 1: Header zone — vendor name typically in top 15% of receipt
var headerRegion = new CropRectangle(0, 0, 800, 150);
using var headerInput = new OcrInput();
headerInput.LoadImage(imagePath, headerRegion);
headerInput.Deskew();
var headerResult = _ocr.Read(headerInput);
// Region 2: Footer zone — totals typically in bottom 20% of receipt
var footerRegion = new CropRectangle(0, 650, 800, 200);
using var footerInput = new OcrInput();
footerInput.LoadImage(imagePath, footerRegion);
footerInput.DeNoise();
var footerResult = _ocr.Read(footerInput);
var vendor = headerResult.Pages[0].Paragraphs
.OrderBy(p => p.Y)
.Select(p => p.Text.Trim())
.FirstOrDefault(t => t.Length > 3);
var footerText = footerResult.Text;
var totalMatch = Regex.Match(footerText,
@"(?:Total|Grand Total):?\s*\$?\s*([\d,]+\.\d{2})",
RegexOptions.IgnoreCase);
var taxMatch = Regex.Match(footerText,
@"(?:Tax|Sales Tax|VAT):?\s*\$?\s*([\d,]+\.\d{2})",
RegexOptions.IgnoreCase);
return (
Vendor: vendor,
Total: totalMatch.Success
? decimal.Parse(totalMatch.Groups[1].Value.Replace(",", ""))
: (decimal?)null,
Tax: taxMatch.Success
? decimal.Parse(taxMatch.Groups[1].Value.Replace(",", ""))
: (decimal?)null
);
}
}
CropRectangle leva (x, y, width, height) em pixels. Processar apenas o cabeçalho e o rodapé é mais rápido do que ler a página inteira e evita correspondências falsas devido aos valores dos itens no corpo do recibo. O guia de OCR baseado em regiões aborda estratégias de medição de coordenadas para documentos de tamanho variável, e o exemplo de recorte de região mostra o padrão completo.
Categorização de despesas com dados estruturados em parágrafos
Veryfi retorna response.LineItems como um array pré-estruturado de objetos com Description, Quantity, UnitPrice, e Total já analisados.IronOCR fornece o equivalente através de result.Pages[0].Paragraphs e result.Lines, que expõem cada bloco de texto com suas coordenadas X/Y. A lógica de categorização de despesas - que decide se um item de linha é uma despesa com refeição, viagem, suprimentos ou software - opera no mesmo texto em ambos os casos. A diferença é que, com o IronOCR, a lógica de categorização é sua, você pode ajustá-la e estendê-la sem precisar pagar por um novo ciclo de treinamento de aprendizado de máquina.
Abordagem Veryfi:
using Veryfi;
public class ExpenseCategorizer
{
private readonly VeryfiClient _client;
public ExpenseCategorizer(VeryfiClient client)
{
_client = client;
}
public async Task<Dictionary<string, decimal>> CategorizeExpensesAsync(string receiptPath)
{
var bytes = File.ReadAllBytes(receiptPath);
var response = await _client.ProcessDocumentAsync(bytes);
var categories = new Dictionary<string, decimal>();
// Line items arrive pre-parsed from Veryfi's ML pipeline
foreach (var item in response.LineItems ?? Enumerable.Empty<dynamic>())
{
var category = response.Category ?? "Uncategorized";
var amount = (decimal)(item.Total ?? 0m);
if (!categories.ContainsKey(category))
categories[category] = 0m;
categories[category] += amount;
}
return categories;
}
}
Abordagem IronOCR:
using IronOcr;
using System.Text.RegularExpressions;
public class ExpenseCategorizer
{
private readonly IronTesseract _ocr = new IronTesseract();
// Keyword-based categorization — tune these for your expense policy
private static readonly Dictionary<string, string[]> CategoryKeywords = new()
{
["Meals & Entertainment"] = new[] { "restaurant", "cafe", "coffee", "lunch", "dinner", "food", "bar" },
["Travel"] = new[] { "airline", "hotel", "uber", "lyft", "taxi", "parking", "gas", "fuel" },
["Office Supplies"] = new[] { "staples", "office depot", "paper", "ink", "toner", "supplies" },
["Software & Subscriptions"] = new[] { "adobe", "microsoft", "github", "aws", "azure", "slack" }
};
public Dictionary<string, decimal> CategorizeExpenses(string receiptPath)
{
var result = _ocr.Read(receiptPath);
// Use paragraph coordinates to isolate line items
// Line items typically appear in the middle vertical band of the receipt
var lineItemParagraphs = result.Pages[0].Paragraphs
.Where(p => p.Y > 150 && p.Y < 650) // skip header/footer regions
.OrderBy(p => p.Y)
.ToList();
var categories = new Dictionary<string, decimal>();
var pricePattern = new Regex(@"\$?([\d,]+\.\d{2})$");
var vendorText = result.Text.ToLower();
// Determine top-level category from vendor name
var topCategory = "Uncategorized";
foreach (var (cat, keywords) in CategoryKeywords)
{
if (keywords.Any(kw => vendorText.Contains(kw)))
{
topCategory = cat;
break;
}
}
// Extract individual line item amounts
foreach (var para in lineItemParagraphs)
{
var priceMatch = pricePattern.Match(para.Text.Trim());
if (!priceMatch.Success)
continue;
if (!decimal.TryParse(priceMatch.Groups[1].Value.Replace(",", ""), out var amount))
continue;
// Classify individual items where keywords appear in the description
var itemCategory = topCategory;
var descriptionText = para.Text.ToLower();
foreach (var (cat, keywords) in CategoryKeywords)
{
if (keywords.Any(kw => descriptionText.Contains(kw)))
{
itemCategory = cat;
break;
}
}
if (!categories.ContainsKey(itemCategory))
categories[itemCategory] = 0m;
categories[itemCategory] += amount;
}
return categories;
}
}
A coleção Paragraphs fornece a coordenada Y de cada bloco de texto, o que facilita isolar a zona vertical onde os itens de linha aparecem em um layout padrão de recibo. O guia de acesso a dados estruturados explica a hierarquia completa de Pages, Paragraphs, Lines, Words, e Characters com suas propriedades de coordenadas. Para recibos com baixa qualidade de digitalização - papel amassado, impressão térmica de baixo contraste - o guia de correção da qualidade da imagem abrange filtros de pré-processamento que melhoram a precisão antes da execução da lógica de categorização.
Eliminação de Webhooks e Substituição Síncrona em Lote
Em situações de alto volume de documentos, a Muito bom recomenda notificações baseadas em webhooks em vez de polling. O padrão requer um endpoint HTTPS acessível publicamente, um segredo webhook para verificação de assinatura, uma fila para armazenar os resultados até que o webhook seja acionado e uma lógica de repetição para entregas perdidas. Trata-se de uma infraestrutura significativa para o que, em última análise, é uma solução alternativa para o fato de o OCR na nuvem ser lento em comparação com o processamento local. O IronOCR processa de forma síncrona. Não existe nenhuma lacuna assíncrona a ser preenchida com um webhook.
Abordagem Veryfi:
using Veryfi;
using Microsoft.AspNetCore.Mvc;
// Muito bom webhook receiver — required for high-volume reliable processing
[ApiController]
[Route("webhooks")]
public class VeryfiWebhookController : ControllerBase
{
private readonly IDocumentResultQueue _queue;
public VeryfiWebhookController(IDocumentResultQueue queue)
{
_queue = queue;
}
[HttpPost("veryfi")]
public IActionResult ReceiveWebhook([FromBody] VeryfiWebhookPayload payload,
[FromHeader(Name = "X-Veryfi-Token")] string token)
{
// Validate webhook signature — prevents spoofed payloads
if (!IsValidSignature(token, payload))
return Unauthorized();
// Enqueue result for async downstream consumption
_queue.Enqueue(new DocumentResult
{
DocumentId = payload.Id,
Vendor = payload.Data?.Vendor?.Name,
Total = payload.Data?.Total
});
return Ok();
}
private bool IsValidSignature(string token, VeryfiWebhookPayload payload) =>
// HMAC validation against webhook secret — infrastructure requirement
token == ComputeHmac(payload, Environment.GetEnvironmentVariable("VERYFI_WEBHOOK_SECRET"));
}
// Document batch submission — fire and forget, results arrive via webhook
public class VeryfiDocumentBatchSubmitter
{
private readonly VeryfiClient _client;
public async Task SubmitBatchAsync(string[] documentPaths)
{
foreach (var path in documentPaths)
{
var bytes = File.ReadAllBytes(path);
// Submit — result arrives asynchronously via webhook, not here
await _client.ProcessDocumentAsync(bytes);
}
}
}
Abordagem IronOCR:
using IronOcr;
using System.Text.RegularExpressions;
using System.Collections.Concurrent;
// Não webhook controller needed — results are synchronous and local
public class DocumentBatchProcessor
{
// IronTesseract is thread-safe when one instance is created per thread
public List<DocumentResult> ProcessBatch(string[] documentPaths)
{
var results = new ConcurrentBag<DocumentResult>();
Parallel.ForEach(documentPaths, documentPath =>
{
// One IronTesseract per thread — thread-safe pattern
var ocr = new IronTesseract();
var result = ocr.Read(documentPath);
results.Add(new DocumentResult
{
FilePath = documentPath,
Vendor = ExtractVendor(result),
Total = ExtractTotal(result.Text),
Confidence = result.Confidence,
// Result is available immediately — no queue, no webhook
ProcessedAt = DateTime.UtcNow
});
});
return results.OrderBy(r => r.FilePath).ToList();
}
private string ExtractVendor(OcrResult result)
{
// Vendor: first substantive paragraph ordered by vertical position
return result.Pages[0].Paragraphs
.OrderBy(p => p.Y)
.Select(p => p.Text.Trim())
.FirstOrDefault(t => !string.IsNullOrWhiteSpace(t) && t.Length > 3);
}
private decimal? ExtractTotal(string text)
{
var match = Regex.Match(text,
@"(?:Total|Grand Total|Amount Due):?\s*\$?\s*([\d,]+\.\d{2})",
RegexOptions.IgnoreCase);
return match.Success
? decimal.Parse(match.Groups[1].Value.Replace(",", ""))
: (decimal?)null;
}
}
public class DocumentResult
{
public string FilePath { get; set; }
public string Vendor { get; set; }
public decimal? Total { get; set; }
public double Confidence { get; set; }
public DateTime ProcessedAt { get; set; }
}
A remoção da camada de webhook elimina o endpoint HTTPS, a exigência de rotação do segredo do webhook, a fila de resultados, a lógica de validação HMAC e a configuração de repetição. Toda a infraestrutura subsequente só existe porque os resultados do Muito bom chegam de forma assíncrona de um servidor remoto. Com IronOCR, Parallel.ForEach substitui tudo isso. O exemplo de multithreading demonstra o padrão IronTesseract por thread em detalhes, e o guia de OCR assíncrono cobre a integração de Task.Run para a responsividade da interface do usuário. O guia de otimização de velocidade aborda a configuração da instância para obter o máximo desempenho em cargas de trabalho em lote.
Referência de mapeamento da API Muito bom para o IronOCR
| Muito bom | Equivalente de IronOCR |
|---|---|
new VeryfiClient(clientId, clientSecret, username, apiKey) | new IronTesseract() + IronOcr.License.LicenseKey = "key" |
_client.ProcessDocumentAsync(bytes) | ocr.Read(filePath) ou ocr.Read(ocrInput) |
_client.ProcessDocumentAsync(bytes, categories: new[] { "invoices" }) | input.LoadPdf(caminho); ocr.Read(entrada) |
_client.ProcessDocumentAsync(bytes, categories: new[] { "bank_statements" }) | input.LoadPdf(caminho); ocr.Read(entrada) |
response.Vendor?.Name | Primeiro parágrafo ordenado por p.Y de result.Pages[0].Paragraphs |
response.Total | Regex.Match(result.Text, @"Total:?\s*\$?([\d,]+\.\d{2})") |
response.Tax | Regex.Match(result.Text, @"Tax:?\s*\$?([\d,]+\.\d{2})") |
response.Date | Regex.Match(result.Text, @"\d{1,2}/\d{1,2}/\d{4}") |
response.LineItems | result.Pages[0].Paragraphs filtrado por intervalo de coordenadas Y |
response.InvoiceNumber | Regex.Match(result.Text, @"Invoice\s*#?\s*:?\s*(\w+[-\w]*)") |
response.BankAccount?.AccountNumber | Regex.Match(result.Text, @"Account\s*#?\s*:?\s*(\d{4,})") |
response.BankAccount?.RoutingNumber | Regex.Match(result.Text, @"Routing\s*#?\s*:?\s*(\d{9})") |
response.ConfidenceScore | result.Confidence (no geral) ou word.Confidence (por palavra) |
response.Payment?.Last4 | Regex.Match(result.Text, @"\*{4}\s*(\d{4})") |
VeryfiApiException (401/402/429/500) | Exceções padrão do .NET - sem códigos de erro HTTP para processamento local. |
| Codificação Base64 antes do upload | Não requerido - ocr.Read(filePath) aceita caminhos de arquivos diretamente |
response.Category | Correspondência de palavra-chave personalizada contra result.Text |
| desserialização da carga útil do webhook | Não requerido - ocr.Read() retorna o resultado de forma síncrona |
ProcessDocumentAsync com tentativa/recuperação | Não é necessário - não há limites de taxa para processamento local. |
Problemas e soluções comuns em migrações
Problema 1: Campos pré-analisados ausentes
Veryfi: response.Vendor?.Name, response.Total, e response.LineItems chegam como campos estruturados de um modelo de ML pré-treinado. Nenhuma lógica de extração é necessária no lado do cliente.
Solução: Escreva padrões Regex para cada campo utilizado pela sua aplicação. O processo de migração normalmente leva de 8 a 24 horas, dependendo da quantidade de layouts de documentos distintos que você precisa processar. Para padrões comuns de recibos e faturas, o tutorial de OCR para faturas e o tutorial de digitalização de recibos fornecem implementações completas de padrões de extração.
// Map each Muito bom field to a Regex extraction
private static readonly Dictionary<string, string> FieldPatterns = new()
{
["InvoiceNumber"] = @"Invoice\s*#?\s*:?\s*(\w+[-\w]*)",
["PurchaseOrder"] = @"(?:PO|P\.O\.|Purchase Order)\s*#?\s*:?\s*(\w+)",
["DueDate"] = @"Due\s*(?:Date)?:?\s*(\d{1,2}/\d{1,2}/\d{4})",
["PaymentTerms"] = @"(?:Terms|Net)\s*:?\s*(\w+\s*\d+)"
};
public string ExtractField(string text, string fieldName)
{
if (!FieldPatterns.TryGetValue(fieldName, out var pattern))
return null;
var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase);
return match.Success ? match.Groups[1].Value.Trim() : null;
}
Problema 2: Assinaturas de Métodos Assíncronos em Toda a Base de Código
Veryfi: ProcessDocumentAsync é assíncrono no nível do SDK da Veryfi. As equipes tipicamente propagam await através de cada método de chamada na pilha de chamadas, significando que classes de serviços, controladores e trabalhos em segundo plano todos carregam assinaturas async Task<t>.
Solução: O Read() do IronOCR é síncrono. As assinaturas de método async existentes podem ser preservadas envolvidas com Task.Run durante o período de transição. Isso evita alterações em massa nas assinaturas em toda a base de código, eliminando ao mesmo tempo a dependência da nuvem.
// Preserve async signature during transition — no codebase-wide refactor needed
public async Task<string> GetVendorNameAsync(string documentPath)
{
return await Task.Run(() =>
{
var result = _ocr.Read(documentPath);
return result.Pages[0].Paragraphs
.OrderBy(p => p.Y)
.Select(p => p.Text.Trim())
.FirstOrDefault(t => t.Length > 3);
});
}
Problema 3: Configuração de credenciais dispersa em diferentes ambientes
Veryfi: Quatro credenciais (Veryfi:ClientId, Veryfi:ClientSecret, Veryfi:Username, Veryfi:ApiKey) aparecem em appsettings.json, blocos de variáveis de ambiente em arquivos Docker Compose, segredos de Ações GitHub, referências de Azure Key Vault e configurações de pipeline CI/CD.
Solução: Pesquise e remova todas as quatro entradas de credenciais de todos os ambientes. Adicionar uma única variável de ambiente IRONOCR_LICENSE_KEY. Carregue-o na inicialização.
# Find all Muito bom credential references
grep -r "Veryfi:ClientId\|Veryfi:ClientSecret\|Veryfi:Username\|Veryfi:ApiKey" \
--include="*.json" --include="*.yml" --include="*.yaml" --include="*.env" .
// Load from environment at startup
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
?? throw new InvalidOperationException("IRONOCR_LICENSE_KEY not set");
Problema 4: Problemas de qualidade de digitalização não visíveis anteriormente
Veryfi: O processamento em nuvem inclui aprimoramento de imagem no servidor antes da execução da inferência de aprendizado de máquina. As digitalizações de recibos de baixa qualidade - papel amassado, impressão térmica desbotada, fotos distorcidas tiradas com celular - foram corrigidas silenciosamente antes da extração dos dados.
Solução: Aplique explicitamente o pipeline de pré-processamento do IronOCR. Deskew(), DeNoise(), e Contrast() cobrem a maioria dos problemas de qualidade de escaneamento de recibos do mundo real.
using var input = new OcrInput();
input.LoadImage("receipt-phone-photo.jpg");
input.Deskew(); // correct rotation from angled phone capture
input.DeNoise(); // remove compression artifacts
input.Contrast(); // improve faded thermal print
input.Sharpen(); // recover edge detail
var result = _ocr.Read(input);
O guia de correção da qualidade da imagem e o tutorial de filtros de imagem abordam quais filtros aplicar para padrões específicos de degradação da digitalização.
Problema 5: Capacidade de Processamento em Lote de Alto Volume
Veryfi: Os limites de taxa restringem a velocidade de envio de documentos. As respostas HTTP 429 exigem lógica de recuo exponencial. A taxa de transferência é limitada pelo limite de taxa por plano da Veryfi, não pelo seu hardware.
Solução: O IronOCR é limitado apenas pelos núcleos da CPU. Use Parallel.ForEach com uma instância IronTesseract por thread. Em um servidor de 8 núcleos, a taxa de transferência aumenta aproximadamente de forma linear com o número de núcleos.
// One IronTesseract per thread — do not share instances across threads
Parallel.ForEach(
documentPaths,
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
path =>
{
var ocr = new IronTesseract();
var result = ocr.Read(path);
SaveResult(path, result.Text, result.Confidence);
});
Problema 6: Esquema JSON proprietário bloqueado para Veryfi
Veryfi: Todo o código de extração lê do esquema de resposta da Veryfi: response.Vendor?.Name, response.LineItems, response.BankAccount?.RoutingNumber. Este código funciona apenas com o SDK da Veryfi. Qualquer alteração no nome de um campo durante uma atualização da API Muito bom quebra o código do aplicativo.
Solução: A extração do IronOCR usa System.Text.RegularExpressions.Regex padrão do .NET contra texto simples. Os padrões são portáteis, testáveis sem a necessidade de simular nenhum SDK e estão sob seu controle. Os testes unitários são executados sem qualquer conexão de rede.
// Extraction logic that is fully portable and unit-testable
[Fact]
public void ExtractsRoutingNumberFromInvoiceText()
{
const string sampleText = "Routing Number: 021000021\nAccount: 1234567890";
var match = Regex.Match(sampleText, @"Routing\s*(?:Number)?:?\s*(\d{9})",
RegexOptions.IgnoreCase);
Assert.True(match.Success);
Assert.Equal("021000021", match.Groups[1].Value);
}
Lista de verificação para migração Veryfi
Pré-migração
Antes de modificar qualquer código, faça uma auditoria do código-fonte para inventariar todo o uso do Veryfi:
# Find all Muito bom using statements
grep -rn "using Veryfi" --include="*.cs" .
# Find all VeryfiClient instantiations
grep -rn "VeryfiClient\|ProcessDocumentAsync" --include="*.cs" .
# Find all Muito bom response field accesses
grep -rn "response\.Vendor\|response\.Total\|response\.LineItems\|response\.BankAccount" --include="*.cs" .
# Find all credential configuration references
grep -r "Veryfi:ClientId\|Veryfi:ClientSecret\|Veryfi:Username\|Veryfi:ApiKey" \
--include="*.json" --include="*.yml" --include="*.yaml" --include="*.env" .
# Find all webhook-related code
grep -rn "VeryfiWebhook\|X-Veryfi-Token\|webhook" --include="*.cs" .
Grave o total de sites de chamadas ProcessDocumentAsync, a lista de campos de resposta acessados por site de chamada e a lista de ambientes contendo credenciais Veryfi.
Migração de código
- Remova o pacote NuGet
Veryfide todos os projetos na solução. - Instale o pacote NuGet
IronOcrem todos os projetos que anteriormente referenciavamVeryfi. - Adicione
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";ao início do aplicativo (antes de qualquer chamada OCR). - Substitua todas as declarações
using Veryfi;eusing Veryfi.Models;porusing IronOcr;. - Substitua a injeção de construtor
VeryfiClientpor inicialização de campoIronTesseract. - Remova todas as quatro entradas de credenciais Muito bom de cada
appsettings.json,appsettings.*.json, e arquivo de configuração de segredos. - Converta chamadas
ProcessDocumentAsync(bytes)paraocr.Read(filePath)ouocr.Read(ocrInput). - Substitua os acessos
response.Vendor?.Namepor extração de texto ordenado por parágrafo deresult.Pages[0].Paragraphs. - Substitua os acessos aos campos estruturados
response.Total,response.Tax,response.InvoiceNumbere outros, por padrões Regex contraresult.Text. - Substitua a iteração
response.LineItemspor iteraçãoresult.Pages[0].Paragraphsfiltrado por coordenada Y. - Exclua as classes do controlador de webhook e remova os registros de endpoints de webhook.
- Remova as variáveis de ambiente secretas do webhook de todos os ambientes.
- Adicione
OcrInputcom pré-processamento (Deskew(),DeNoise(),Contrast()) para entradas de imagem escaneadas. - Substitua os loops sequenciais single-threaded por
Parallel.ForEachusando umIronTesseractpor thread. - Adicione
IRONOCR_LICENSE_KEYa todas as configurações de variáveis de ambiente e armazenamentos de segredos de CI/CD.
Pós-migração
- Verifique se não há chamadas de rede Muito bom nos registros de tráfego HTTP após a implantação da migração.
- Confirme se os nomes dos fornecedores extraídos correspondem aos valores esperados em uma amostra de 20 a 50 recibos.
- Confirme se os totais extraídos correspondem aos valores esperados dentro de uma tolerância de US$ 0,01 para o mesmo conjunto de amostras.
- Verificar se a extração do número da fatura é bem-sucedida para cada formato de fatura no conjunto de documentos.
- Testar a taxa de transferência do processamento em lote em comparação com a taxa de transferência de referência do Muito bom para confirmar a remoção do limite de taxa.
- Execute o Suite completo de testes sem qualquer conexão de rede para confirmar a ausência de dependência da nuvem.
- Confirme que as pontuações
result.Confidenceexcedem 80% para escaneamentos de documentos limpos; Valores abaixo de 80% indicam que uma etapa de pré-processamento deve ser adicionada. - Verifique se todas as quatro credenciais do Muito bom foram removidas de todos os ambientes (desenvolvimento, teste e produção).
- Confirme se os endpoints do webhook retornam 404 ou se foram removidos da tabela de roteamento.
- Testar o comportamento em digitalizações de recibos de baixa qualidade (amassados, desbotados, tortos) com o pipeline de pré-processamento ativo.
Principais benefícios da migração para o IronOCR
Os documentos financeiros processados localmente são documentos que não podem ser acessados por terceiros. Após a migração, os números de contas bancárias extraídos de faturas, os números de roteamento obtidos de cheques e os históricos de transações lidos de extratos bancários são todos processados em seu hardware. Nenhum incidente de segurança de terceiros, acesso a dados de subcontratados ou violação da infraestrutura da Muito bom pode expor documentos que nunca saíram dos seus servidores.
Os custos por documento caem para zero no dia em que a migração é implementada. Com 50.000 documentos por mês, o item de custo mensal do Veryfi, que variava de US$ 5.000 a US$ 15.000, desaparece. A licença IronOCR Professional , que custa US$ 2.999 e é adquirida uma única vez, é recuperada na primeira semana do primeiro mês. Em volumes maiores, as economias se acumulam a cada ano, sem necessidade de negociação de descontos por volume ou renovação de contrato.
A capacidade de processamento escala com o hardware, não com os limites de taxa do fornecedor. Respostas HTTP 429, limites de taxa de transferência por plano e cobranças sazonais por excesso de uso são artefatos arquitetônicos de APIs em nuvem. Com o IronOCR, adicionar núcleos de CPU aumenta a capacidade de processamento proporcionalmente. Um lote de 10.000 recibos é processado de acordo com o seu cronograma, e não com base na programação de limite de taxa da Veryfi.
Qualquer tipo de documento é processado com a mesma API. A organização não precisa mais de uma segunda ferramenta de OCR quando o RH solicita o processamento de formulários de integração, o departamento jurídico precisa extrair o texto de contratos ou a área de operações precisa de dados de documentos de remessa. ocr.Read() lida com todos eles. O tutorial de leitura de texto em imagens e os guias de documentos especializados abrangem toda a gama de formatos de documentos que o IronOCR suporta.
A lógica de extração torna-se parte integrante do código-fonte. Os padrões de expressões regulares estão no controle de versão, podem ser revisados em pull requests, testados em testes unitários sem a necessidade de simular nenhum SDK e ajustados com base no feedback da produção. Quando o modelo pré-treinado da Muito bom retorna um nome de fornecedor incorreto, não há nada para ajustar. Quando o padrão de extração do IronOCR retorna um nome de fornecedor incorreto, a correção consiste em uma alteração de expressão regular de uma linha com um teste unitário. A página de licenciamento do IronOCR aborda as opções de planos, incluindo a assinatura SaaS para equipes que preferem o faturamento anual em vez da compra perpétua.
A infraestrutura de implantação é reduzida a um único pacote NuGet que pode ser executado em qualquer lugar. O IronOCR é instalado como um único pacote, sem dependências externas, sem gerenciamento de binários nativos e sem configuração da pasta tessdata. A mesma referência de pacote é resolvida no Windows, Linux, macOS, Docker, Azure App Service e AWS Lambda sem código condicional à plataforma. Consulte o guia de implantação do Docker e o guia de implantação do Linux para ambientes conteinerizados onde o requisito de saída de rede do Muito bom representa um obstáculo à implantação.
