Migrando do AWS Textract para o IronOCR
Este guia descreve uma migração completa doAWS Textractpara o IronOCR para desenvolvedores .NET que precisam de processamento de documentos local sem dependências da nuvem. Este documento aborda a remoção das credenciais, a eliminação do pipeline assíncrono, a eliminação do S3 e as substituições de código específicas necessárias para migrar cada operação do Textract para seu equivalente no IronOCR.
Por que migrar do AWS Textract?
OAWS Textracté um serviço gerenciado eficiente, mas sua arquitetura impõe custos — financeiros e operacionais — que aumentam conforme o volume de trabalho de documentos cresce. As equipes que desenvolvem fluxos de trabalho robustos para processamento de documentos acabam se deparando com todos esses pontos de atrito.
A infraestrutura de credenciais da AWS complica cada implantação. Todo ambiente que executa código Textract precisa de credenciais da AWS: usuário ou função do IAM, chave de acesso e segredo, configuração de região e — para processamento de PDF — permissões de bucket do S3. Isso representa cinco etapas de configuração distintas antes do processamento da primeira página. Implantações em produção requerem políticas de rotação de credenciais, injeção segura em contêineres Docker ou pods Kubernetes, e monitoramento para AccessDeniedException quando o desvio de política IAM causa falhas silenciosas. O IronOCR requer apenas uma sequência de caracteres: uma chave de licença, definida uma única vez na inicialização.
Preço por página não tem teto. A tarifa base de $0.0015 por página para DetectDocumentText é o piso, não o custo. Qualquer documento com tabelas aciona AnalyzeDocument a $0.015 por página — dez vezes a tarifa base. Os formulários adicionam mais US$ 0,05 por página. Um fluxo de trabalho misto de documentos, com 100.000 páginas por mês e extração de tabelas, custa US$ 18.000 por ano, e esse valor é zerado todo mês de janeiro. A licença Professional do IronOCR custa US$ 2.999,00 em uma única compra. Depois disso, o custo por página é zero, independentemente do volume.
O Pipeline PDF Assíncrono é um Passivo de Manutenção. Processar qualquer PDF de várias páginas através do Textract requer cinco fases distintas: upload S3, StartDocumentTextDetection, loop de polling, recuperação paginada de resultados, e limpeza S3. Cada fase é um modo de falha independente que requer seu próprio tratamento de erro, estratégia de tentativa e gerenciamento de tempo limite. As equipes que implementam esse pipeline em produção relatam consistentemente gastar mais tempo em sua manutenção do que gastaram em sua construção. O IronOCR lê um PDF com duas linhas de código.
Sem acesso à internet, não há Textract. Contêineres Docker em segmentos de rede isolados, servidores locais, ambientes de pesquisa isolados da internet e sistemas industriais com restrições de tráfego de saída compartilham uma característica: o Textract está indisponível. O IronOCR é instalado como um pacote NuGet e funciona sem qualquer chamada de rede após o download inicial do pacote.
Os dados saem da sua infraestrutura a cada chamada. Cada operação de OCR com o Textract transmite o conteúdo do documento para os servidores da Amazon. Para organizações de saúde que processam informações de saúde protegidas (PHI), empresas contratadas pela área de defesa que lidam com informações controladas não classificadas (CUI), equipes jurídicas que processam comunicações privilegiadas e instituições financeiras que processam imagens de cartões de pagamento, isso não é uma opção de configuração — é uma restrição arquitetônica que proíbe completamente o uso do Textract em ambientes regulamentados. O IronOCR é executado no seu hardware. Não existe um modo nuvem para desativar; O processamento local é o único modo.
Limites de Taxa Limitam o Throughput em Lote. O limite de TPS padrão StartDocumentTextDetection é de 5 requisições por segundo. Trabalhos em lote processando centenas de documentos requerem SemaphoreSlim de limitação, retrocesso exponencial em ProvisionedThroughputExceededException, e temporizadores de reabastecimento de taxa. Solicitar um aumento de TPS requer a abertura de um chamado formal de suporte da AWS, sem garantia de resultado.IronOCR processa tão rápido quanto a CPU local permite — um servidor de 16 núcleos processa 16 documentos simultaneamente com um simples Parallel.ForEach, sem necessidade de negociação de camada de serviço.
O problema fundamental
Toda implementação do Textract começa com essa cerimônia — antes mesmo de uma única página ser processada:
// Textract: five environment variables, an IAM role, and an S3 bucket
// — all required before the first OCR call
// Environment must have:
// AWS_ACCESS_KEY_ID=AKIA...
// AWS_SECRET_ACCESS_KEY=...
// AWS_DEFAULT_REGION=us-east-1
// IAM role: textract:DetectDocumentText, textract:AnalyzeDocument
// IAM role: s3:PutObject, s3:DeleteObject (for any PDF processing)
// S3 bucket: my-textract-staging-bucket (with lifecycle policy to prevent cost accumulation)
public class TextractOcrService
{
private readonly AmazonTextractClient _textractClient;
private readonly AmazonS3Client _s3Client; // required for PDFs
public TextractOcrService()
{
// Fails with AmazonClientException if credentials not found in chain
_textractClient = new AmazonTextractClient(Amazon.RegionEndpoint.USEast1);
_s3Client = new AmazonS3Client(Amazon.RegionEndpoint.USEast1);
}
}
O IronOCR substitui toda a cadeia de credenciais por uma única atribuição:
// IronOCR: one line, one string, done
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
// Or from environment (no IAM, no rotation, no S3)
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
##IronOCR vs AWS Textract: Comparação de Recursos
A tabela a seguir mapeia as capacidades de ambas as bibliotecas nas dimensões mais relevantes para as decisões de migração.
| Recurso | AWS Textract | IronOCR |
|---|---|---|
| Local de processamento | Nuvem da Amazon (obrigatório) | Somente local/no local |
| É necessário ter acesso à internet. | Sempre | Nunca |
| Configuração de credenciais | Usuário/função do IAM + bucket S3 opcional | string de chave de licença única |
| PDF com várias páginas | Requer ambiente de preparação S3 + tarefa assíncrona | Execução direta síncrona input.LoadPdf() |
| PDF protegido por senha | Não suportado | input.LoadPdf(path, Password: "x") |
| TIFF de múltiplos quadros | Não suportado | input.LoadImageFrames("multi.tiff") |
| Sondagem assíncrona necessária | Sim (para todos os PDFs) | Não — síncrono por padrão |
| Limites de taxa | 5 TPS padrão (StartDocumentTextDetection) | Nenhum — somente os que dependem da CPU |
| Custo por página | US$ 0,0015 a US$ 0,065 por página | US$ 0 após a compra da licença |
| Pré-processamento automático | Interno, não configurável | Correção de distorção, redução de ruído, contraste, binarização, nitidez, escala |
| Saída em PDF pesquisável | Não disponível | result.SaveAsSearchablePdf() |
| Exportação hOCR | Não disponível | result.SaveAsHocrFile() |
| Leitura de código de barras | Não disponível | ocr.Configuration.ReadBarCodes = true |
| OCR baseado em região | Por meio de AnalyzeDocument + relações de bloco | CropRectangle(x, y, width, height) |
| Resultados Estruturados | Plano List<Block> filtrado por BlockType | Digitado Pages, Paragraphs, Lines, Words |
| Idiomas suportados | Inglês como língua dominante (selecione outras opções) | Mais de 125 pacotes de idiomas via NuGet |
| Simultâneo em vários idiomas | Não suportado | OcrLanguage.French + OcrLanguage.German |
| Implantação isolada da internet | Não é possível. | Suporte completo |
| HIPAA sem BAA | Não é possível. | Sem processador externo — somente para instalação local |
| Implantação do Docker | Requer credenciais da AWS injetadas. | Sem credenciais — pacote NuGet simples |
| Multiplataforma | Gerenciado pela AWS (somente Linux) | Windows, Linux, macOS, Docker, Azure, AWS EC2 |
| Modelo de licenciamento | Cobrança por página (gasto contínuo) | Perpétuo por única vez ($999 / $1,499 / $2,999) |
Guia rápido: Migração doAWS Textractpara o IronOCR
Passo 1: Substitua o pacote NuGet
Remova os pacotes do SDK da AWS:
dotnet remove package AWSSDK.Textract
dotnet remove package AWSSDK.S3
dotnet remove package AWSSDK.Core
Instale o IronOCR a partir do NuGet :
Etapa 2: Atualizar Namespaces
Substitua todas as importações de namespace da AWS pelo namespace único do IronOCR:
// Before (AWS Textract)
using Amazon.Textract;
using Amazon.Textract.Model;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.Runtime;
// After (IronOCR)
using IronOcr;
Etapa 3: Inicializar a licença
Adicione a inicialização da licença uma única vez na inicialização do aplicativo. Remova todas as configurações de variáveis de ambiente de credenciais da AWS.
// Remove from startup:
// AWS_ACCESS_KEY_ID environment variable
// AWS_SECRET_ACCESS_KEY environment variable
// AWS_DEFAULT_REGION environment variable
// ~/.aws/credentials file entries
// IAM role bindings on the execution context
// Add instead:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
Exemplos de migração de código
Substituindo o construtor do AmazonTextractClient e a configuração do IAM
A mudança mais visível durante a migração é a inicialização do cliente. O Textract requer um cliente com injeção de dependência e resolução de credenciais subjacente — o que significa que tanto o cliente quanto todas as suas dependências devem ser pré-configurados em cada ambiente de implantação. Qualquer configuração incorreta falha silenciosamente até a primeira chamada de OCR lançar AmazonClientException.
Abordagem do AWS Textract:
// Requires: NuGet AWSSDK.Textract, AWSSDK.S3
// Requires: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION in environment
// Requires: IAM policy with textract:* and s3:PutObject, s3:DeleteObject
using Amazon.S3;
using Amazon.Textract;
public class DocumentOcrService
{
private readonly AmazonTextractClient _textractClient;
private readonly AmazonS3Client _s3Client;
private readonly string _stagingBucket;
public DocumentOcrService(IConfiguration config)
{
// Credential chain: environment → ~/.aws/credentials → EC2 instance profile
// Fails if none found — runtime exception, not compile-time
_textractClient = new AmazonTextractClient(Amazon.RegionEndpoint.USEast1);
_s3Client = new AmazonS3Client(Amazon.RegionEndpoint.USEast1);
_stagingBucket = config["Textract:StagingBucket"]; // bucket must exist and have permissions
}
public async Task<string> ReadImageAsync(string imagePath)
{
var bytes = File.ReadAllBytes(imagePath);
var response = await _textractClient.DetectDocumentTextAsync(
new DetectDocumentTextRequest
{
Document = new Document { Bytes = new MemoryStream(bytes) }
});
return string.Join("\n", response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
}
}
Abordagem IronOCR:
// Requires: NuGet IronOcr
// Requires: one license key string — nothing else
using IronOcr;
public class DocumentOcrService
{
private readonly IronTesseract _ocr;
public DocumentOcrService()
{
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; // or set at Program.cs startup
_ocr = new IronTesseract();
}
public string ReadImage(string imagePath)
{
// No credential chain, no region, no bucket — just read
return _ocr.Read(imagePath).Text;
}
}
Toda a infraestrutura de credenciais AWS — funções IAM, variáveis de ambiente, arquivos ~/.aws/credentials, políticas de ciclo de vida de bucket S3, e configuração de região — é removida. O construtor DocumentOcrService passa de um local de injeção de 3 dependências com modos de falha em tempo de execução para uma única atribuição de licença. Para obter detalhes sobre os padrões de implantação, consulte o guia de configuração do IronTesseract .
Substituindo a detecção de texto do documento inicial pelo processamento de múltiplos quadros TIFF.
O API de trabalho assíncrono do Textract (StartDocumentTextDetection) é necessário para qualquer documento de múltiplas páginas. Um padrão comum nos fluxos de trabalho de digitalização de documentos é o recebimento de documentos digitalizados como TIFFs multiframe — um frame por página digitalizada. O Textract não aceita entrada TIFF diretamente; O documento deve ser convertido para PDF e carregado no S3 antes que uma tarefa possa ser iniciada. O IronOCR lê TIFFs com múltiplos quadros nativamente.
Abordagem do AWS Textract:
// Textract cannot accept TIFF directly — requires PDF conversion + S3 + async job
// This shows the conversion overhead plus the minimum async pipeline
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.Textract;
using Amazon.Textract.Model;
public async Task<string> ProcessScannedTiffAsync(string tiffPath, string s3Bucket)
{
// Step 0: Convert TIFF to PDF first (Textract does not accept TIFF for async jobs)
// Requires a separate PDF conversion library — not shown here
var pdfPath = Path.ChangeExtension(tiffPath, ".pdf");
ConvertTiffToPdf(tiffPath, pdfPath); // external dependency required
// Step 1: Upload converted PDF to S3
var s3Key = $"staging/{Guid.NewGuid()}.pdf";
using (var stream = File.OpenRead(pdfPath))
{
await _s3Client.PutObjectAsync(new PutObjectRequest
{
BucketName = s3Bucket,
Key = s3Key,
InputStream = stream
});
}
try
{
// Step 2: Start async detection job
var startResp = await _textractClient.StartDocumentTextDetectionAsync(
new StartDocumentTextDetectionRequest
{
DocumentLocation = new DocumentLocation
{
S3Object = new S3Object { Bucket = s3Bucket, Name = s3Key }
}
});
// Step 3: Poll every 5 seconds — can block for 30–120 seconds on large files
GetDocumentTextDetectionResponse pollResp;
do
{
await Task.Delay(5000);
pollResp = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest { JobId = startResp.JobId });
} while (pollResp.JobStatus == JobStatus.IN_PROGRESS);
if (pollResp.JobStatus != JobStatus.SUCCEEDED)
throw new Exception($"Job failed: {pollResp.StatusMessage}");
// Step 4: Collect paginated results
var text = new StringBuilder();
string token = null;
do
{
var page = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest
{
JobId = startResp.JobId,
NextToken = token
});
foreach (var block in page.Blocks.Where(b => b.BlockType == BlockType.LINE))
text.AppendLine(block.Text);
token = page.NextToken;
} while (token != null);
return text.ToString();
}
finally
{
// Step 5: Clean up S3 — orphaned objects accumulate storage costs
await _s3Client.DeleteObjectAsync(s3Bucket, s3Key);
File.Delete(pdfPath); // clean up intermediate PDF
}
}
Abordagem IronOCR:
//IronOCR reads multi-frame TIFF directly — no conversion, no S3, no polling
using IronOcr;
public string ProcessScannedTiff(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // reads all frames natively
var ocr = new IronTesseract();
var result = ocr.Read(input);
// Access per-page results if needed
foreach (var page in result.Pages)
Console.WriteLine($"Page {page.PageNumber}: {page.Words.Length} words detected");
return result.Text;
}
A etapa de conversão de TIFF para PDF, o upload para o S3, o loop de verificação assíncrona, o acúmulo de resultados paginados e a limpeza do S3 são todos eliminados. A implementação do IronOCR lê quadros diretamente e fornece acesso por página através de result.Pages sem qualquer conversão de formato intermediário. O guia de entrada TIFF e GIF aborda a seleção de quadros para casos em que apenas um subconjunto de quadros é necessário.
Substituindo o armazenamento temporário S3 por saída em PDF pesquisável
Um caso de uso comum do Textract é a conversão de arquivos PDF digitalizados em formato pesquisável. A abordagem Textract exige o upload de cada PDF para o S3, a execução da tarefa assíncrona, a coleta do texto extraído e, em seguida, o uso de uma biblioteca PDF separada para incorporar esse texto como uma camada pesquisável. O IronOCR realiza a digitalização e gera um PDF pesquisável em uma única chamada.
Abordagem do AWS Textract:
// Textract extracts text but cannot produce searchable PDFs
// Requires: S3 upload + async job + separate PDF library to embed the text layer
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.Textract;
using Amazon.Textract.Model;
public async Task<string> ExtractTextForSearchableLayerAsync(
string scannedPdfPath,
string s3Bucket)
{
// Must upload to S3 — cannot pass PDF bytes directly for async jobs
var key = $"ocr-input/{Guid.NewGuid()}.pdf";
await using (var fs = File.OpenRead(scannedPdfPath))
{
await _s3Client.PutObjectAsync(new PutObjectRequest
{
BucketName = s3Bucket,
Key = key,
InputStream = fs,
ContentType = "application/pdf"
});
}
string jobId;
try
{
var startResp = await _textractClient.StartDocumentTextDetectionAsync(
new StartDocumentTextDetectionRequest
{
DocumentLocation = new DocumentLocation
{
S3Object = new S3Object { Bucket = s3Bucket, Name = key }
}
});
jobId = startResp.JobId;
}
catch
{
await _s3Client.DeleteObjectAsync(s3Bucket, key);
throw;
}
// Poll — minimum 5s wait; typical 15–60s for a 10-page PDF
GetDocumentTextDetectionResponse pollResp;
int pollAttempts = 0;
const int maxAttempts = 120; // 10 minute timeout
do
{
await Task.Delay(5000);
pollResp = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest { JobId = jobId });
if (++pollAttempts >= maxAttempts)
throw new TimeoutException("Textract job timed out after 10 minutes");
} while (pollResp.JobStatus == JobStatus.IN_PROGRESS);
await _s3Client.DeleteObjectAsync(s3Bucket, key); // cleanup regardless of outcome
if (pollResp.JobStatus != JobStatus.SUCCEEDED)
throw new Exception($"Textract job status: {pollResp.JobStatus} — {pollResp.StatusMessage}");
// Return extracted text — caller must use a separate library to produce searchable PDF
return string.Join("\n", pollResp.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
// Caller still needs: iTextSharp, PdfSharp, or similar to embed text layer
}
Abordagem IronOCR:
//IronOCR reads the PDF and produces a searchable PDF output directly
// No S3, no polling, no external PDF library needed
using IronOcr;
public void ConvertToSearchablePdf(string scannedPdfPath, string outputPath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf(scannedPdfPath);
var result = ocr.Read(input);
// Embed extracted text as searchable layer in one call
result.SaveAsSearchablePdf(outputPath);
Console.WriteLine($"Pages processed: {result.Pages.Length}");
Console.WriteLine($"Overall confidence: {result.Confidence:F1}%");
}
A lógica de tempo limite de sondagem, o padrão de upload e limpeza do S3 e a dependência da biblioteca PDF externa foram todos removidos. SaveAsSearchablePdf incorpora o texto reconhecido diretamente no arquivo de saída, tornando cada página escaneada totalmente pesquisável em texto sem uma segunda biblioteca. O guia em PDF pesquisável aborda seleção de páginas, opções de compressão e incorporação de metadados. Para um contexto mais amplo sobre fluxos de trabalho de OCR em PDF, consulte o guia de entrada de PDF .
Substituindo a travessia do grafo de blocos do AnalyzeDocument por resultados de página estruturados
O AnalyzeDocument do Textract com TABLES e FORMS retorna um List<Block> plano onde todos os elementos — linhas, palavras, células, cabeçalhos de tabelas, chaves de formulários, valores de formulários — são do mesmo tipo Block distinguidos apenas por BlockType e vinculados por arrays de IDs RelationshipType.CHILD. Reconstruir a estrutura lógica de um documento requer percorrer esse grafo de relacionamentos. O IronOCR retorna uma hierarquia tipada: páginas, parágrafos, linhas, palavras, cada um com acesso por coordenadas.
Abordagem do AWS Textract:
// AnalyzeDocument with TABLES returns blocks that must be graph-traversed
// to determine which words belong to which paragraphs
using Amazon.Textract;
using Amazon.Textract.Model;
public async Task<List<DocumentSection>> ExtractDocumentStructureAsync(string imagePath)
{
var bytes = File.ReadAllBytes(imagePath);
var response = await _textractClient.AnalyzeDocumentAsync(
new AnalyzeDocumentRequest
{
Document = new Document { Bytes = new MemoryStream(bytes) },
FeatureTypes = new List<string> { "TABLES", "FORMS" }
// Note: AnalyzeDocument (TABLES) costs $0.015/page — 10x DetectDocumentText
});
var sections = new List<DocumentSection>();
// Filter LINE blocks for paragraph reconstruction
// LINE blocks are not grouped into paragraphs — must infer from Y proximity
var lineBlocks = response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.OrderBy(b => b.Geometry?.BoundingBox?.Top ?? 0)
.ToList();
// Group lines into pseudo-paragraphs by vertical gap heuristic
var currentParagraph = new List<Block>();
float? lastBottom = null;
const float paragraphGapThreshold = 0.02f; // 2% of page height
foreach (var line in lineBlocks)
{
var top = line.Geometry?.BoundingBox?.Top ?? 0;
var height = line.Geometry?.BoundingBox?.Height ?? 0;
if (lastBottom.HasValue && (top - lastBottom.Value) > paragraphGapThreshold)
{
if (currentParagraph.Any())
{
sections.Add(new DocumentSection
{
Text = string.Join(" ", currentParagraph.Select(b => b.Text)),
LineCount = currentParagraph.Count,
// Confidence: average of all LINE block confidence values
Confidence = (float)currentParagraph.Average(b => b.Confidence ?? 0)
});
currentParagraph.Clear();
}
}
currentParagraph.Add(line);
lastBottom = top + height;
}
if (currentParagraph.Any())
sections.Add(new DocumentSection
{
Text = string.Join(" ", currentParagraph.Select(b => b.Text)),
LineCount = currentParagraph.Count,
Confidence = (float)currentParagraph.Average(b => b.Confidence ?? 0)
});
return sections;
}
public class DocumentSection
{
public string Text { get; set; }
public int LineCount { get; set; }
public float Confidence { get; set; }
}
Abordagem IronOCR:
//IronOCR returns a typed hierarchy — paragraphs are first-class objects
// No block graph, no heuristic gap detection, no confidence averaging
using IronOcr;
public List<DocumentSection> ExtractDocumentStructure(string imagePath)
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
var sections = new List<DocumentSection>();
foreach (var page in result.Pages)
{
foreach (var paragraph in page.Paragraphs)
{
sections.Add(new DocumentSection
{
Text = paragraph.Text,
LineCount = paragraph.Lines.Length,
// Coordinate access: exact pixel position on the page
LocationX = paragraph.X,
LocationY = paragraph.Y,
Width = paragraph.Width,
Height = paragraph.Height,
Confidence = paragraph.Confidence
});
}
}
return sections;
}
public class DocumentSection
{
public string Text { get; set; }
public int LineCount { get; set; }
public int LocationX { get; set; }
public int LocationY { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public double Confidence { get; set; }
}
Parágrafos, linhas e palavras são diretamente acessíveis como coleções digitadas com propriedades .X, .Y, .Width, .Height, e .Confidence. Sem normalização de BoundingBox, sem heurísticas de limite de lacuna, sem filtragem de tipo de bloco. O guia de leitura de resultados documenta toda a hierarquia OcrResult incluindo acesso a coordenadas em nível de caractere. Para fluxos de trabalho de faturas e recibos que dependem dessa estrutura, consulte o tutorial de OCR de faturas .
Substituindo o processamento em lote com taxa limitada pela execução paralela irrestrita.
Processamento de imagens em lote é onde os limites de TPS do Textract produzem o custo operacional mais visível. O DetectDocumentText síncrono da API é limitado por taxa; enviar mais de 5 requisições por segundo produz ProvisionedThroughputExceededException. O tratamento correto em lote requer SemaphoreSlim de limitação, lógica de tentativa com retrocesso, e contabilidade cuidadosa para requisições em andamento. O IronOCR não possui limite de taxa — a capacidade de processamento é limitada apenas pelos núcleos de CPU disponíveis.
Abordagem do AWS Textract:
// Batch processing requires SemaphoreSlim throttle + retry on rate limit exceeded
using Amazon.Textract;
using Amazon.Textract.Model;
using System.Collections.Concurrent;
public async Task<Dictionary<string, string>> ProcessImageBatchAsync(
IReadOnlyList<string> imagePaths,
IProgress<(int Completed, int Total)> progress = null)
{
var results = new ConcurrentDictionary<string, string>();
var semaphore = new SemaphoreSlim(5); // 5 concurrent — Textract default TPS limit
var tasks = new List<Task>();
int completed = 0;
foreach (var path in imagePaths)
{
await semaphore.WaitAsync();
tasks.Add(Task.Run(async () =>
{
try
{
string text = null;
int retryCount = 0;
while (text == null && retryCount < 3)
{
try
{
var bytes = await File.ReadAllBytesAsync(path);
var response = await _textractClient.DetectDocumentTextAsync(
new DetectDocumentTextRequest
{
Document = new Document { Bytes = new MemoryStream(bytes) }
});
text = string.Join("\n", response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
}
catch (AmazonTextractException ex)
when (ex.ErrorCode == "ProvisionedThroughputExceededException")
{
// Exponential backoff on rate limit — blocks the entire thread
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, retryCount)));
retryCount++;
}
}
results[path] = text ?? string.Empty;
var done = Interlocked.Increment(ref completed);
progress?.Report((done, imagePaths.Count));
}
finally
{
semaphore.Release();
}
}));
}
await Task.WhenAll(tasks);
return results.ToDictionary(x => x.Key, x => x.Value);
}
Abordagem IronOCR:
// No rate limits, no semaphore, no retry logic — Parallel.ForEach saturates all cores
using IronOcr;
using System.Collections.Concurrent;
public Dictionary<string, string> ProcessImageBatch(
IReadOnlyList<string> imagePaths,
IProgress<(int Completed, int Total)> progress = null)
{
var ocr = new IronTesseract();
var results = new ConcurrentDictionary<string, string>();
int completed = 0;
Parallel.ForEach(imagePaths, path =>
{
var result = ocr.Read(path);
results[path] = result.Text;
var done = Interlocked.Increment(ref completed);
progress?.Report((done, imagePaths.Count));
});
return results.ToDictionary(x => x.Key, x => x.Value);
}
O SemaphoreSlim, o loop de tentativa, o retrocesso exponencial, e os blocos de captura ProvisionedThroughputExceededException são todos removidos. IronTesseract é seguro para threads e uma única instância compartilhada entre threads lida com a carga paralela completa sem bloqueios. Em uma máquina com 8 núcleos, isso processa 8 documentos simultaneamente sem nenhuma configuração; Em 32 núcleos, 32 simultaneamente. O exemplo de multithreading mostra o padrão completo e o guia de otimização de velocidade aborda o ajuste da configuração do mecanismo para implantações focadas em throughput.
Substituindo a extração de formulários do AnalyzeDocument pelo OCR CropRectangle baseado em região.
O AnalyzeDocument do Textract com FORMS retorna blocos KEY_VALUE_SET vinculados por relacionamentos RelationshipType.VALUE. Reconstruir pares chave-valor de campos de formulário requer filtragem por EntityTypes.Contains("KEY"), seguindo IDs de relacionamento VALUE, e então buscando blocos filhos WORD para montar o texto. Para formulários de formato fixo onde as posições dos campos são conhecidas, o CropRectangle do IronOCR extrai cada campo diretamente sem travessia de gráfico de relacionamento — e custa zero por página.
Abordagem do AWS Textract:
// FORMS mode costs $0.05/page — the most expensive Textract tier
// Relationship graph traversal required to reconstruct key-value pairs
using Amazon.Textract;
using Amazon.Textract.Model;
public async Task<Dictionary<string, string>> ExtractInvoiceFieldsAsync(string imagePath)
{
var bytes = File.ReadAllBytes(imagePath);
// $0.05/page for FORMS analysis
var response = await _textractClient.AnalyzeDocumentAsync(
new AnalyzeDocumentRequest
{
Document = new Document { Bytes = new MemoryStream(bytes) },
FeatureTypes = new List<string> { "FORMS" }
});
var allBlocks = response.Blocks;
var fields = new Dictionary<string, string>();
// Find KEY blocks only
var keyBlocks = allBlocks.Where(b =>
b.BlockType == BlockType.KEY_VALUE_SET &&
b.EntityTypes?.Contains("KEY") == true);
foreach (var keyBlock in keyBlocks)
{
// Assemble key text from CHILD WORD blocks
var keyText = GetTextFromBlock(allBlocks, keyBlock);
// Find associated VALUE block via VALUE relationship
var valueBlockId = keyBlock.Relationships?
.FirstOrDefault(r => r.Type == RelationshipType.VALUE)?
.Ids.FirstOrDefault();
if (valueBlockId == null) continue;
var valueBlock = allBlocks.FirstOrDefault(b => b.Id == valueBlockId);
if (valueBlock == null) continue;
var valueText = GetTextFromBlock(allBlocks, valueBlock);
fields[keyText.TrimEnd(':')] = valueText;
}
return fields;
}
private string GetTextFromBlock(IList<Block> allBlocks, Block block)
{
if (block.Relationships == null) return string.Empty;
var childWordIds = block.Relationships
.Where(r => r.Type == RelationshipType.CHILD)
.SelectMany(r => r.Ids);
return string.Join(" ", allBlocks
.Where(b => b.BlockType == BlockType.WORD && childWordIds.Contains(b.Id))
.Select(b => b.Text));
}
Abordagem IronOCR:
// CropRectangle extracts each field zone directly — no relationship graph
// Works for any fixed-format form where field positions are known
using IronOcr;
// Define the field zones for your form template once
private static readonly Dictionary<string, CropRectangle> InvoiceFieldZones =
new Dictionary<string, CropRectangle>
{
{ "InvoiceNumber", new CropRectangle(450, 80, 300, 35) },
{ "InvoiceDate", new CropRectangle(450, 120, 300, 35) },
{ "VendorName", new CropRectangle(50, 160, 400, 35) },
{ "TotalAmount", new CropRectangle(450, 680, 200, 35) },
{ "DueDate", new CropRectangle(450, 720, 200, 35) }
};
public Dictionary<string, string> ExtractInvoiceFields(string imagePath)
{
var ocr = new IronTesseract();
var fields = new Dictionary<string, string>();
foreach (var zone in InvoiceFieldZones)
{
using var input = new OcrInput();
input.LoadImage(imagePath, zone.Value); // load only the defined region
var result = ocr.Read(input);
fields[zone.Key] = result.Text.Trim();
}
return fields;
}
A filtragem de bloco KEY_VALUE_SET, a travessia RelationshipType.VALUE, e o assistente GetTextFromBlock são eliminados. Cada campo lê exatamente a região de pixels que o contém — nem mais, nem menos. O guia de OCR baseado em região cobre coordenadas CropRectangle e como definir zonas a partir de medidas de template. Para fluxos de trabalho específicos de faturas, a postagem do blog sobre OCR de faturas e o tutorial de digitalização de recibos demonstram pipelines completos de extração de campos.
Referência de mapeamento da APIAWS Textractpara o IronOCR
| AWS Textract | Equivalente de IronOCR |
|---|---|
AmazonTextractClient | IronTesseract |
AmazonS3Client | Não é necessário |
DetectDocumentTextRequest | OcrInput com input.LoadImage() |
DetectDocumentTextResponse | OcrResult |
AnalyzeDocumentRequest (TABELAS/FÓRMS) | OcrInput com CropRectangle opcional |
StartDocumentTextDetectionRequest | OcrInput com input.LoadPdf() — síncrono, sem início de trabalho |
GetDocumentTextDetectionRequest | Não aplicável — os resultados são imediatos. |
Document.Bytes | input.LoadImage(byteArray) ou input.LoadImage(stream) |
S3Object (encenação de documento) | Caminho do arquivo ou fluxo — sem necessidade de preparação prévia |
Block (BlockType.LINE) | result.Lines (coleção digitada) |
Block (BlockType.WORD) | result.Words (coleção digitada) |
Block (BlockType.TABLE) | result.Words agrupado por proximidade de coordenadas |
Block (BlockType.KEY_VALUE_SET) | CropRectangle extração por campo de região |
Block.Confidence | word.Confidence / result.Confidence |
Block.Geometry.BoundingBox | word.X, word.Y, word.Width, word.Height |
RelationshipType.CHILD de travessia | Acesso direto a propriedades em objetos de resultado tipados |
JobStatus.IN_PROGRESS | Não aplicável — nenhum estado assíncrono |
JobStatus.SUCCEEDED | Não aplicável — retorno síncrono |
response.NextToken (paginaçação) | Não aplicável — resultados não paginados |
ProvisionedThroughputExceededException | Não aplicável — sem limites de TPS |
AccessDeniedException | Não aplicável — sem cadeia de credenciais |
input.LoadImageFrames() | Suporte direto a TIFF com múltiplos quadros (sem equivalente em Textract) |
result.SaveAsSearchablePdf() | Saída em PDF pesquisável (sem equivalente em Textract) |
Problemas e soluções comuns em migrações
Problema 1: Credenciais mal configuradas em novos ambientes de implantação
AWS Textract: O construtor AmazonTextractClient resolve credenciais de uma cadeia: variáveis de ambiente, ~/.aws/credentials, perfil de instância EC2, papel de tarefa ECS. Em um novo contêiner Docker ou ambiente CI, se nenhum desses estiver configurado, o cliente é construído sem erro mas cada chamada de API lança AmazonClientException: No credentials specified. A falha é invisível até o momento da execução.
Solução: Remova completamente a cadeia de credenciais. Defina a chave de licença do IronOCR a partir de uma variável de ambiente, que é mais simples de configurar e não requer permissões do IAM para gerenciar:
// Single environment variable — no IAM, no rotation, no chain resolution
var key = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
if (string.IsNullOrEmpty(key))
throw new InvalidOperationException("IRONOCR_LICENSE environment variable is not set");
IronOcr.License.LicenseKey = key;
Problema 2: Locais de Chamada Assíncrona em Toda a Base de Código
AWS Textract: Porque todo o SDK é apenas assíncrono (DetectDocumentTextAsync, StartDocumentTextDetectionAsync, GetDocumentTextDetectionAsync), todos os locais de chamada do Textract propagam async Task<t> através da cadeia de chamadas. A migração para a API síncrona do IronOCR requer uma decisão em cada local.
Solução: Para serviços de processamento em segundo plano e ações do controlador, as chamadas síncronas do IronOCR são seguras em threads em segundo plano. Envolva em Task.Run apenas quando a cadeia de chamadas existente deve continuar assíncrona:
// If the call site must remain async (e.g., an ASP.NET controller action):
public async Task<IActionResult> ProcessUpload(IFormFile file)
{
using var ms = new MemoryStream();
await file.CopyToAsync(ms);
// Offload synchronous OCR to thread pool — keeps controller non-blocking
var text = await Task.Run(() =>
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage(ms.ToArray());
return ocr.Read(input).Text;
});
return Ok(text);
}
Para processadores em lote e serviços de fundo que já estão sendo executados em threads não-UI, chame ocr.Read() de forma síncrona — Task.Run adiciona sobrecarga sem benefício nesses contextos. O guia de OCR assíncrono aborda os padrões recomendados.
Problema 3: Lógica de limpeza do bucket S3 espalhada pelo código
AWS Textract: Qualquer código que seja carregado para o S3 para processamento pelo Textract também deve ser excluído do S3 após a conclusão da tarefa. Esta limpeza frequentemente reside em blocos finally e às vezes é omitida em caminhos de erro, causando objetos órfãos que acumulam custos de armazenamento. Durante a migração, o código de limpeza do S3 pode passar despercebido facilmente, pois não está conceitualmente relacionado ao OCR.
Solução: Todo o padrão de upload e limpeza do S3 não possui equivalente no IronOCR. Exclua todos os blocos PutObjectAsync, DeleteObjectAsync, e relacionados ao S3, try/finally inteiramente. O IronOCR lê diretamente de caminhos ou fluxos locais:
// Before: upload → job → poll → extract → cleanup (50+ lines)
// After: two lines, no cleanup required
using var input = new OcrInput();
input.LoadPdf(localPdfPath);
var text = new IronTesseract().Read(input).Text;
Desative o bucket de preparação S3 após a migração. O guia de entrada de PDF e o guia de entrada de fluxo abrangem todos os padrões de entrada que substituem o acesso a documentos armazenados em estágios no S3.
Problema 4: O código de percurso em grafo de blocos não possui equivalente direto.
AWS Textract: O código que atravessa Block.Relationships para reconstruir células de tabela, campos de formulário, ou agrupamentos de parágrafos a partir de arrays de IDs RelationshipType.CHILD é o código mais demorado para migrar. Não existe uma substituição direta porque o IronOCR retorna coleções tipadas em vez de um grafo de relacionamento.
Solução: Substitua cada padrão de percurso pela propriedade tipada apropriada. A consulta de IDs de relacionamento passa a permitir o acesso direto à propriedade:
// Before: filter by BlockType + traverse relationship IDs
var paragraphText = string.Join(" ", allBlocks
.Where(b => b.BlockType == BlockType.WORD &&
childIds.Contains(b.Id))
.Select(b => b.Text));
// After: direct paragraph text (no filtering, no relationship lookup)
foreach (var page in result.Pages)
foreach (var para in page.Paragraphs)
Console.WriteLine(para.Text); // already assembled
Para reconstrução de tabelas que dependia de BlockType.CELL e de propriedades ColumnIndex, use agrupamento de coordenadas de palavras em result.Words. O guia de leitura de tabelas aborda a abordagem baseada em coordenadas.
Problema 5: Blocos Catch da exceção ProvisionedThroughputExceededException
AWS Textract: Código robusto de processamento em lote captura AmazonTextractException com ErrorCode == "ProvisionedThroughputExceededException" e implementa tentativas com retrocesso. Este código de erro é um conceito específico do Textract, sem equivalente no IronOCR.
Solução: Excluir todos os blocos de captura ProvisionedThroughputExceededException. O IronOCR não possui limite de TPS. Substitua todo o padrão de lote limitado com Parallel.ForEach:
// Delete: SemaphoreSlim, retry loops, ProvisionedThroughputExceededException handlers
// Replace with:
Parallel.ForEach(imagePaths, path =>
{
var result = new IronTesseract().Read(path);
results[path] = result.Text;
});
Problema 6: Configuração de região integrada aos construtores do cliente
AWS Textract: new AmazonTextractClient(Amazon.RegionEndpoint.USEast1) define codificadamente uma região. Implantações em várias regiões exigem múltiplas instâncias de cliente ou seleção dinâmica de região. Quando o Textract adiciona suporte a novas regiões, o código precisa ser atualizado.
Solução: O IronOCR não possui o conceito de região. Remova todas as referências Amazon.RegionEndpoint. O construtor IronTesseract não requer configuração de rede — o processamento é local. Para implantações em várias regiões na infraestrutura da AWS, onde cada região executa seu próprio poder computacional, cada instância do IronOCR processa os documentos localmente dentro desse limite computacional, sem chamadas entre regiões.
Lista de verificação de migração do AWS Textract
Pré-migração
Audite todo o uso dos SDKs Textract e S3 na base de código:
grep -rn "Amazon.Textract\|AmazonTextractClient\|DetectDocumentText" --include="*.cs" .
grep -rn "StartDocumentTextDetection\|GetDocumentTextDetection\|JobStatus" --include="*.cs" .
grep -rn "AmazonS3Client\|PutObjectRequest\|DeleteObjectAsync" --include="*.cs" .
grep -rn "BlockType\|RelationshipType\|KEY_VALUE_SET" --include="*.cs" .
grep -rn "ProvisionedThroughputExceededException\|AmazonTextractException" --include="*.cs" .
grep -rn "AWSSDK\|Amazon.RegionEndpoint" --include="*.csproj" .
Faça um inventário dos seguintes itens antes de escrever qualquer código de substituição:
- Conte todos os arquivos contendo
AmazonTextractClient— cada um é um alvo de substituição - Liste todos os locais de chamada
AnalyzeDocument— note seTABLES,FORMS, ou ambos são usados - Identifique todos os loops de polling assíncronos (
do { await Task.Delay } while JobStatus == IN_PROGRESS) - Encontre todos os blocos de limpeza S3
finally— eles são excluídos totalmente, não substituídos - Conte todos os blocos de captura
ProvisionedThroughputExceededException— excluídos, não substituídos - Note toda a lógica de travessia
BlockType.TABLE,BlockType.CELL,BlockType.KEY_VALUE_SET— cada uma precisa de substituição de saída estruturada
Migração de código
- Remova
AWSSDK.Textract,AWSSDK.S3, eAWSSDK.Corede todos os arquivos.csproj - Execute
dotnet add package IronOcrem cada projeto que executa OCR - Adicione
IronOcr.License.LicenseKey = ...uma vez no início do aplicativo (Program.csou equivalente) - Remova todas as declarações
using Amazon.Textract;,using Amazon.Textract.Model;,using Amazon.S3;,using Amazon.Runtime; - Adicione
using IronOcr;para cada arquivo que anteriormente importou namespaces Textract - Substitua chamadas de construtor
AmazonTextractClientcomnew IronTesseract() - Substitua a instanciação
AmazonS3Cliente todas as chamadasPutObjectAsync/DeleteObjectAsynccom leituras diretas de caminho de arquivo ou fluxo - Substitua todas as chamadas
DetectDocumentTextAsync:var text = ocr.Read(path).Text - Substitua todos os loops de polling de
StartDocumentTextDetectionAsync+ cominput.LoadPdf(path)+ocr.Read(input) - Substitua todos os pipelines TIFF de múltiplos frames (TIFF → PDF → S3 → async) com
input.LoadImageFrames(tiffPath) - Substitua cadeias de filtro
BlockType.LINE/BlockType.WORDcomresult.Lines/result.Words - Substitua a travessia de relacionamento
BlockType.KEY_VALUE_SETcom extração de zonaCropRectangle - Exclua todos os blocos de captura
ProvisionedThroughputExceededExceptioneSemaphoreSlimdas limitações - Substitua padrões de lote limitados com
Parallel.ForEachusando uma instânciaIronTesseractcompartilhada - Remova todas as configurações de variáveis de ambiente de credenciais da AWS dos manifestos de implantação e pipelines de CI.
- Desative os buckets de preparação do S3 após a migração e verificação de todo o código de processamento.
Pós-migração
- Verifique se o aplicativo inicia limpo com apenas
IRONOCR_LICENSEdefinido e todas as variáveis de ambiente AWS removidas - Execute o OCR nas mesmas imagens de amostra previamente processadas pelo Textract e compare o texto extraído para verificar a precisão.
- Processar um PDF com várias páginas diretamente e verificar se todas as páginas foram extraídas sem usar S3 ou sondagem assíncrona.
- Processar um TIFF com vários quadros e verificar se a contagem de páginas corresponde à saída do Textract para o mesmo documento.
- Teste o processamento em lote com um conjunto de 50+ imagens e confirme que não ocorre equivalente
ProvisionedThroughputExceededExceptionExecute o aplicativo em um contêiner Docker sem acesso à internet e confirme se o OCR foi concluído com sucesso. - Verifique se o PDF pesquisável abre corretamente no Adobe Reader e se permite pesquisa de texto completo.
- Confirme que remover
AWS_ACCESS_KEY_IDeAWS_SECRET_ACCESS_KEYdo ambiente de implantação não quebra nada - Teste qualquer fluxo de trabalho de extração de formulários ou faturas comparando-o com a saída conhecida do Textract para validar a precisão dos campos.
- Medir a latência de processamento para um conjunto de documentos representativo e confirmar a eliminação da espera mínima de 5 segundos entre as consultas.
Principais benefícios da migração para o IronOCR
Consolidação da infraestrutura em um único pacote NuGet . Três pacotes do AWS SDK, um bucket do S3 com políticas de ciclo de vida, funções do IAM em todos os ambientes de implantação e procedimentos de rotação de credenciais da AWS são substituídos por um único pacote NuGet . A mudança .csproj é dotnet remove package AWSSDK.Textract seguida por dotnet add package IronOcr. Todas as consequências subsequentes do gerenciamento de credenciais da AWS — auditorias de segurança, cronogramas de rotação, higienização de variáveis de ambiente, configuração de segredos do Docker — desaparecem com ele.
Custo Total de Propriedade Previsível. O modelo de cobrança por página gera custos variáveis que aumentam indefinidamente com o volume de documentos. Após a migração para o IronOCR, o custo por página adicional processada é zero, independentemente do volume. Uma equipe processando 200.000 páginas por mês na taxa AnalyzeDocument do Textract para tabelas gasta $36.000 por ano apenas com OCR. A licença IronOCR Enterprise , a US$ 2.999, recupera esse custo em menos de 31 dias de faturamento evitado. Consulte a página de licenciamento do IronOCR para obter detalhes completos sobre os níveis de licenciamento e os termos de redistribuição do SaaS.
O processamento síncrono elimina a complexidade assíncrona de cinco fases. O upload para o S3, o início da tarefa, o loop de polling, a coleta de resultados paginados e o bloco de limpeza final representam cinco modos de falha independentes no pipeline de PDF do Textract. Após a migração, um PDF é lido em duas linhas. O tratamento de erro é reduzido a uma única tentativa/captura em torno da chamada ocr.Read(). A lógica de tempo limite do polling, o loop do/while JobStatus == IN_PROGRESS, o acumulador de paginação nextToken — nenhum desses conceitos existe na base de código do IronOCR. A carga de manutenção diminui proporcionalmente ao código que foi removido.
Flexibilidade total de implantação. O Textract requer acesso à internet em todas as chamadas de OCR. O IronOCR requer acesso à internet apenas para baixar o pacote NuGet durante a instalação. Depois disso, ele opera em redes isoladas (air-gapped), contêineres Docker sem regras de saída, servidores locais e instâncias AWS EC2 sem acesso ao Textract. O mesmo binário que roda em produção no Windows Server também roda em um contêiner Linux. O guia de implantação do Docker e o guia de implantação do Linux abordam a configuração específica para ambientes conteinerizados que anteriormente eram impedidos de usar o Textract.
Soberania de dados alcançada por meio da arquitetura, não da configuração. O IronOCR não possui um modo de transmissão de rede que possa ser desativado — o processamento local é o único modo disponível. Os documentos processados pelo IronOCR nunca saem da máquina host. Para aplicações de saúde que processam PHI, aplicações de defesa que processam CUI e aplicações financeiras que processam imagens de cartões de pagamento, essa postura de conformidade não exige negociação de BAA, configuração de residência de dados regional nem auditoria das políticas de tratamento de dados da Amazon. O âmbito de conformidade se limita à sua própria infraestrutura, que você já controla. A página do produto IronOCR fornece um resumo completo dos recursos e a documentação de implantação para equipes que realizam uma revisão de conformidade.
Pré-processamento configurável para controle de qualidade de documentos. O pré-processamento interno do Textract não é acessível nem configurável. Quando um documento digitalizado produz resultados insatisfatórios, a única solução é aceitar o resultado ou digitalizar novamente.IronOCR expõe todo o pipeline de pré-processamento: Deskew(), DeNoise(), Contrast(), Binarize(), Sharpen(), Scale(), Dilate(), Erode(), e DeepCleanBackgroundNoise(). As equipes que migraram documentos do Textract devido a resultados de baixa confiabilidade em digitalizações complexas podem agora abordar a causa raiz diretamente com filtros específicos para cada defeito — rotação, ruído, baixo contraste ou resolução insuficiente. A página sobre recursos de pré-processamento e o guia de correção da qualidade da imagem documentam os filtros disponíveis e seus respectivos casos de uso.
