Como instalar o Tesseract OCR no Windows em C#
Este guia orienta desenvolvedores .NET na substituição doGoogle Cloud Visionpelo IronOCR como um mecanismo OCR local de fácil implementação. Ele aborda a remoção de credenciais, a substituição da análise de anotações Protobuf, a simplificação do processamento em lote de anotações e o processamento de documentos com várias páginas — as quatro mudanças estruturais que representam a maior parte do trabalho de migração.
Por que migrar doGoogle Cloud VisionOCR?
A decisão de migrar quase sempre começa com uma das duas constatações: uma auditoria de conformidade bloqueia a transmissão de documentos para a nuvem, ou a complexidade operacional do gerenciamento de credenciais do GCP, buckets do GCS, consultas assíncronas e faturamento por imagem se torna mais cara do que o próprio OCR.
Ciclo de vida da chave JSON da conta de serviço. Cada implantação do seu aplicativo — máquina de desenvolvimento, pipeline de CI/CD, servidor de teste, servidor de produção, contêiner Docker, pod do Kubernetes — requer o mesmo arquivo JSON de chave da conta de serviço. O arquivo contém uma chave privada RSA. Nunca deve entrar no controle de versão, deve ser rotacionado de acordo com um cronograma, deve ser protegido com permissões do sistema de arquivos e deve ser atualizado em todos os ambientes simultaneamente quando a rotação ocorrer. Uma chave comprometida concede acesso à API até que seja revogada manualmente no Console do GCP. O IronOCR substitui toda essa superfície operacional por uma única sequência de chave de licença, definida uma única vez na inicialização do aplicativo.
Cobrança por solicitação em grande escala. A US$ 1,50 por 1.000 imagens e US$ 0,0015 por página de PDF, os custos são invisíveis durante o desenvolvimento e onerosos na produção. Um pipeline de processamento de documentos que lida com 200.000 páginas por mês custa US$ 300 por mês somente em taxas de API, antes das taxas de armazenamento do GCS e dos custos de saída. Essa quantia de 300 dólares se repete todo mês indefinidamente. A licença perpétua da IronOCR transforma o OCR de uma despesa operacional mensurável em um item de capital fixo que não tem custo de operação no segundo ou terceiro ano.
Pipeline assíncrono do GCS para PDFs. OGoogle Cloud Visionnão aceita PDFs como entrada direta da API. O pipeline completo requer um segundo pacote NuGet (Google.Cloud.Storage.V1), um bucket GCS provisionado, um upload assíncrono, uma chamada AsyncBatchAnnotateFilesAsync, um loop de polling, análise de saída JSON do GCS e uma etapa de limpeza. Esse pipeline abrange mais de 50 linhas de código antes de qualquer texto ser extraído. O IronOCR lê um PDF em três linhas, de forma síncrona, sem dependências externas.
Concatenação de Símbolos Protobuf. A resposta DOCUMENT_TEXT_DETECTION armazena texto no nível de símbolo dentro de uma hierarquia Protobuf de Páginas, Blocos, Parágrafos, Palavras e Símbolos. A leitura de texto de parágrafo requer a iteração de cinco loops aninhados e a chamada de .SelectMany(w => w.Symbols).Select(s => s.Text).IronOCR retorna texto de parágrafo como paragraph.Text — uma propriedade de string tipada.
Cota Padrão de 1.800 Solicitações Por Minuto. Cargas de trabalho em lote acima da cota padrão recebem respostas StatusCode.ResourceExhausted que paralisam o pipeline por 60 segundos por ultrapassagem. Aumentar a cota requer uma solicitação no Console do GCP e a aprovação do Google. O IronOCR processa os dados localmente na velocidade dos núcleos de CPU disponíveis — não há cota para gerenciar, nenhuma aprovação para buscar e nenhuma lógica de repetição necessária para limitar a taxa de processamento.
Sem suporte para ambientes offline ou isolados da internet. OGoogle Cloud Visionrequer conectividade com a internet nos endpoints do Google. Redes isoladas da internet, centros de dados classificados e sistemas de controle industrial não podem utilizá-lo em nenhum nível de complexidade arquitetônica. O IronOCR funciona sem nenhuma conectividade de rede de saída após a validação inicial da licença.
O problema fundamental
OGoogle Cloud Visionexige um arquivo de chave JSON no disco antes da execução da primeira linha de código OCR:
// Google Cloud Vision: JSON key file deployed to every server before this line works
// GOOGLE_APPLICATION_CREDENTIALS="/etc/secrets/service-account.json" must be set
// Key contains RSA private key — rotate manually, revoke if compromised
_client = ImageAnnotatorClient.Create();
var image = Image.FromFile("document.jpg");
var response = _client.DetectText(image); // document leaves your infrastructure
string text = response[0].Description;
// Google Cloud Vision: JSON key file deployed to every server before this line works
// GOOGLE_APPLICATION_CREDENTIALS="/etc/secrets/service-account.json" must be set
// Key contains RSA private key — rotate manually, revoke if compromised
_client = ImageAnnotatorClient.Create();
var image = Image.FromFile("document.jpg");
var response = _client.DetectText(image); // document leaves your infrastructure
string text = response[0].Description;
' Google Cloud Vision: JSON key file deployed to every server before this line works
' GOOGLE_APPLICATION_CREDENTIALS="/etc/secrets/service-account.json" must be set
' Key contains RSA private key — rotate manually, revoke if compromised
_client = ImageAnnotatorClient.Create()
Dim image = Image.FromFile("document.jpg")
Dim response = _client.DetectText(image) ' document leaves your infrastructure
Dim text As String = response(0).Description
O IronOCR inicia com uma única string e é executado inteiramente na máquina local:
// IronOCR: one string at startup, no key files, no environment variables
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
string text = new IronTesseract().Read("document.jpg").Text; // local, no cloud
// IronOCR: one string at startup, no key files, no environment variables
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
string text = new IronTesseract().Read("document.jpg").Text; // local, no cloud
Imports IronOcr
' IronOCR: one string at startup, no key files, no environment variables
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim text As String = New IronTesseract().Read("document.jpg").Text ' local, no cloud
IronOCR vsGoogle Cloud VisionOCR: Comparação de Recursos
A tabela abaixo mapeia as funcionalidades diretamente para as equipes que estão elaborando o plano de negócios para a migração.
| Recurso | OCR doGoogle Cloud Vision | IronOCR |
|---|---|---|
| Local de processamento | Google Cloud (remoto) | Não local (local) |
| Autenticação | Chave JSON da conta de serviço + variável de ambiente | String da chave de licença |
| Entrada de PDF | Carregar para o GCS + API assíncrona | input.LoadPdf() direta |
| PDF protegido por senha | Não suportado | LoadPdf(path, Password: "...") |
| Entrada TIFF de várias páginas | Limitado | input.LoadImageFrames() |
| Saída em PDF pesquisável | Não disponível | result.SaveAsSearchablePdf() |
| Acesso a dados estruturados | Protobuf: Páginas > Blocos > Parágrafos > Palavras > Símbolos | result.Paragraphs, result.Lines, result.Words (objetos .NET tipados) |
| Propriedade do texto do parágrafo | Não — requer concatenação de símbolos | paragraph.Text propriedade direta |
| Pontuações de confiança | Por símbolo (requer loop) | result.Confidence, word.Confidence |
| Pré-processamento automático de imagens | Nenhum (o aprendizado de máquina cuida disso) | Correção de distorção, redução de ruído, contraste, binarização, nitidez |
| OCR baseado em região | Sem cultivo nativo | CropRectangle em OcrInput |
| Leitura de código de barras | Recurso de API separado | ocr.Configuration.ReadBarCodes = true |
| Limites de taxa | 1.800 solicitações/minuto (padrão) | Nenhum (limitado pela CPU) |
| Offline / isolado da internet | Não | Sim |
| Custo por documento | US$ 1,50 por 1.000 imagens; US$ 0,0015/página em PDF | Nenhuma (licença perpétua) |
| Idiomas suportados | ~50 | 125+ |
| autorização FedRAMP | Não autorizado | Não aplicável (local) |
| caminho de conformidade com a HIPAA | É necessário um Acordo de Parceiro Comercial. | Não há tratamento de dados por terceiros. |
| Suporte ao .NET Framework | .NET Standard 2.0+ | .NET Framework 4.6.2+ e .NET 5/6/7/8/9 |
| Pacotes NuGet necessários | Google.Cloud.Vision.V1 + Google.Cloud.Storage.V1 |
IronOcr apenas |
| Modelo de preços | Cobrança por solicitação, conforme o consumo. | Perpetual ($999 Lite / $1,499 Plus / $2,999 Professional / $5,999 Unlimited) |
Guia rápido: Migração do OCR doGoogle Cloud Visionpara o IronOCR
Passo 1: Substitua o pacote NuGet
Remova os pacotes do Google Cloud:
dotnet remove package Google.Cloud.Vision.V1
dotnet remove package Google.Cloud.Storage.V1
dotnet remove package Google.Cloud.Vision.V1
dotnet remove package Google.Cloud.Storage.V1
Instale o IronOCR a partir da página de pacotes NuGet :
dotnet add package IronOcr
Etapa 2: Atualizar Namespaces
Substitua os namespaces do Google Cloud pelo namespace IronOCR:
// Before (Google Cloud Vision)
using Google.Cloud.Vision.V1;
using Google.Cloud.Storage.V1;
using Google.Protobuf;
using Grpc.Core;
// After (IronOCR)
using IronOcr;
// Before (Google Cloud Vision)
using Google.Cloud.Vision.V1;
using Google.Cloud.Storage.V1;
using Google.Protobuf;
using Grpc.Core;
// After (IronOCR)
using IronOcr;
Imports IronOcr
Etapa 3: Inicializar a licença
Adicione a inicialização da licença uma vez no início do aplicativo, antes de qualquer instância IronTesseract ser criada:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Em produção, leia a chave de uma variável de ambiente ou gerenciador de segredos:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
?? throw new InvalidOperationException("IRONOCR_LICENSE environment variable not set.");
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
?? throw new InvalidOperationException("IRONOCR_LICENSE environment variable not set.");
Imports System
Imports IronOcr
IronOcr.License.LicenseKey = If(Environment.GetEnvironmentVariable("IRONOCR_LICENSE"), Throw New InvalidOperationException("IRONOCR_LICENSE environment variable not set."))
Exemplos de migração de código
Eliminação da configuração de credenciais da conta de serviço
A inicialização do clienteGoogle Cloud Visionparece ser feita em uma única linha, mas requer uma infraestrutura prévia extensa. Todo desenvolvedor que entra no projeto, todo ambiente de implantação e todo pipeline de CI/CD precisa ter a configuração de credenciais completa antes que o construtor seja concluído sem lançar uma exceção.
Abordagem do Google Cloud Vision:
using Google.Cloud.Vision.V1;
// Prerequisites before this class can be instantiated:
// 1. GCP project created and Vision API enabled in GCP Console
// 2. Service account created with roles/cloudvision.user IAM role
// 3. JSON key file downloaded to every server that runs this code
// 4. GOOGLE_APPLICATION_CREDENTIALS env var pointing to the JSON file
// 5. JSON file excluded from source control via .gitignore
// 6. Key rotation schedule established (recommended: 90 days)
// 7. Separate credentials per environment (dev/staging/prod)
public class DocumentOcrService
{
private readonly ImageAnnotatorClient _client;
private readonly string _projectId;
public DocumentOcrService(string projectId)
{
_projectId = projectId;
// Throws RpcException(StatusCode.PermissionDenied) if any prerequisite is missing
_client = ImageAnnotatorClient.Create();
}
public string ReadDocument(string imagePath)
{
var image = Image.FromFile(imagePath);
var response = _client.DetectText(image);
return response.Count > 0 ? response[0].Description : string.Empty;
}
}
using Google.Cloud.Vision.V1;
// Prerequisites before this class can be instantiated:
// 1. GCP project created and Vision API enabled in GCP Console
// 2. Service account created with roles/cloudvision.user IAM role
// 3. JSON key file downloaded to every server that runs this code
// 4. GOOGLE_APPLICATION_CREDENTIALS env var pointing to the JSON file
// 5. JSON file excluded from source control via .gitignore
// 6. Key rotation schedule established (recommended: 90 days)
// 7. Separate credentials per environment (dev/staging/prod)
public class DocumentOcrService
{
private readonly ImageAnnotatorClient _client;
private readonly string _projectId;
public DocumentOcrService(string projectId)
{
_projectId = projectId;
// Throws RpcException(StatusCode.PermissionDenied) if any prerequisite is missing
_client = ImageAnnotatorClient.Create();
}
public string ReadDocument(string imagePath)
{
var image = Image.FromFile(imagePath);
var response = _client.DetectText(image);
return response.Count > 0 ? response[0].Description : string.Empty;
}
}
Imports Google.Cloud.Vision.V1
' Prerequisites before this class can be instantiated:
' 1. GCP project created and Vision API enabled in GCP Console
' 2. Service account created with roles/cloudvision.user IAM role
' 3. JSON key file downloaded to every server that runs this code
' 4. GOOGLE_APPLICATION_CREDENTIALS env var pointing to the JSON file
' 5. JSON file excluded from source control via .gitignore
' 6. Key rotation schedule established (recommended: 90 days)
' 7. Separate credentials per environment (dev/staging/prod)
Public Class DocumentOcrService
Private ReadOnly _client As ImageAnnotatorClient
Private ReadOnly _projectId As String
Public Sub New(projectId As String)
_projectId = projectId
' Throws RpcException(StatusCode.PermissionDenied) if any prerequisite is missing
_client = ImageAnnotatorClient.Create()
End Sub
Public Function ReadDocument(imagePath As String) As String
Dim image = Image.FromFile(imagePath)
Dim response = _client.DetectText(image)
Return If(response.Count > 0, response(0).Description, String.Empty)
End Function
End Class
Abordagem IronOCR:
using IronOcr;
// Prerequisites: set the license key once at app startup
// Não JSON files, no environment variables beyond the key, no GCP Console configuration
// Não key rotation, no IAM roles, no per-environment credential sets
public class DocumentOcrService
{
private readonly IronTesseract _ocr;
public DocumentOcrService()
{
// IronTesseract is ready immediately — no external validation required
_ocr = new IronTesseract();
}
public string ReadDocument(string imagePath)
{
return _ocr.Read(imagePath).Text;
}
}
using IronOcr;
// Prerequisites: set the license key once at app startup
// Não JSON files, no environment variables beyond the key, no GCP Console configuration
// Não key rotation, no IAM roles, no per-environment credential sets
public class DocumentOcrService
{
private readonly IronTesseract _ocr;
public DocumentOcrService()
{
// IronTesseract is ready immediately — no external validation required
_ocr = new IronTesseract();
}
public string ReadDocument(string imagePath)
{
return _ocr.Read(imagePath).Text;
}
}
Imports IronOcr
' Prerequisites: set the license key once at app startup
' Não JSON files, no environment variables beyond the key, no GCP Console configuration
' Não key rotation, no IAM roles, no per-environment credential sets
Public Class DocumentOcrService
Private ReadOnly _ocr As IronTesseract
Public Sub New()
' IronTesseract is ready immediately — no external validation required
_ocr = New IronTesseract()
End Sub
Public Function ReadDocument(imagePath As String) As String
Return _ocr.Read(imagePath).Text
End Function
End Class
A diferença operacional é concreta: oGoogle Cloud Visiongera cinco categorias de RpcException em tempo de execução — PermissionDenied, ResourceExhausted, Unavailable, DeadlineExceeded e Unauthenticated — cada uma representando um modo de falha de infraestrutura diferente. Os modos de falha do IronOCR são IOException (arquivo não encontrado ou bloqueado) e OcrException (falha de processamento). Consulte o guia de configuração do IronTesseract para opções de configuração e a página do produto IronOCR para detalhes de licenciamento.
Substituindo solicitações de anotação em lote por vários tipos de recursos
OGoogle Cloud Visionsuporta agrupar várias imagens em um único BatchAnnotateImagesRequest, cada imagem configurada com uma lista de tipos de Feature. Esse padrão é usado quando uma única chamada precisa coletar resultados TEXT_DETECTION e DOCUMENT_TEXT_DETECTION, ou ao submeter muitas imagens para minimizar a sobrecarga de ida e volta. A resposta Protobuf requer associar cada AnnotateImageResponse de volta à sua solicitação original por índice.
Abordagem do Google Cloud Vision:
using Google.Cloud.Vision.V1;
using System.Collections.Generic;
public class BatchAnnotationService
{
private readonly ImageAnnotatorClient _client;
public BatchAnnotationService()
{
_client = ImageAnnotatorClient.Create();
}
public List<string> BatchAnnotateImages(string[] imagePaths)
{
// Build one request per image with TEXT_DETECTION feature
var requests = imagePaths.Select(path => new AnnotateImageRequest
{
Image = Image.FromFile(path),
Features =
{
new Recurso { Type = Feature.Types.Type.TextDetection },
new Recurso { Type = Feature.Types.Type.DocumentTextDetection }
}
}).ToList();
// Single round-trip for all images in the batch
var batchResponse = _client.BatchAnnotateImages(requests);
// Match responses back to requests by index
var results = new List<string>();
for (int i = 0; i < batchResponse.Responses.Count; i++)
{
var response = batchResponse.Responses[i];
if (response.Error != null)
{
// Per-item error in batch — must handle individually
results.Add($"Error on {imagePaths[i]}: {response.Error.Message}");
continue;
}
// Prefer DOCUMENT_TEXT_DETECTION full text if available
var fullText = response.FullTextAnnotation?.Text
?? response.TextAnnotations.FirstOrDefault()?.Description
?? string.Empty;
results.Add(fullText);
}
return results;
}
}
using Google.Cloud.Vision.V1;
using System.Collections.Generic;
public class BatchAnnotationService
{
private readonly ImageAnnotatorClient _client;
public BatchAnnotationService()
{
_client = ImageAnnotatorClient.Create();
}
public List<string> BatchAnnotateImages(string[] imagePaths)
{
// Build one request per image with TEXT_DETECTION feature
var requests = imagePaths.Select(path => new AnnotateImageRequest
{
Image = Image.FromFile(path),
Features =
{
new Recurso { Type = Feature.Types.Type.TextDetection },
new Recurso { Type = Feature.Types.Type.DocumentTextDetection }
}
}).ToList();
// Single round-trip for all images in the batch
var batchResponse = _client.BatchAnnotateImages(requests);
// Match responses back to requests by index
var results = new List<string>();
for (int i = 0; i < batchResponse.Responses.Count; i++)
{
var response = batchResponse.Responses[i];
if (response.Error != null)
{
// Per-item error in batch — must handle individually
results.Add($"Error on {imagePaths[i]}: {response.Error.Message}");
continue;
}
// Prefer DOCUMENT_TEXT_DETECTION full text if available
var fullText = response.FullTextAnnotation?.Text
?? response.TextAnnotations.FirstOrDefault()?.Description
?? string.Empty;
results.Add(fullText);
}
return results;
}
}
Imports Google.Cloud.Vision.V1
Imports System.Collections.Generic
Imports System.Linq
Public Class BatchAnnotationService
Private ReadOnly _client As ImageAnnotatorClient
Public Sub New()
_client = ImageAnnotatorClient.Create()
End Sub
Public Function BatchAnnotateImages(imagePaths As String()) As List(Of String)
' Build one request per image with TEXT_DETECTION feature
Dim requests = imagePaths.Select(Function(path) New AnnotateImageRequest With {
.Image = Image.FromFile(path),
.Features = {
New Feature With {.Type = Feature.Types.Type.TextDetection},
New Feature With {.Type = Feature.Types.Type.DocumentTextDetection}
}
}).ToList()
' Single round-trip for all images in the batch
Dim batchResponse = _client.BatchAnnotateImages(requests)
' Match responses back to requests by index
Dim results = New List(Of String)()
For i As Integer = 0 To batchResponse.Responses.Count - 1
Dim response = batchResponse.Responses(i)
If response.Error IsNot Nothing Then
' Per-item error in batch — must handle individually
results.Add($"Error on {imagePaths(i)}: {response.Error.Message}")
Continue For
End If
' Prefer DOCUMENT_TEXT_DETECTION full text if available
Dim fullText = If(response.FullTextAnnotation?.Text, response.TextAnnotations.FirstOrDefault()?.Description, String.Empty)
results.Add(fullText)
Next
Return results
End Function
End Class
Abordagem IronOCR:
using IronOcr;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
public class BatchAnnotationService
{
public List<string> BatchAnnotateImages(string[] imagePaths)
{
var results = new ConcurrentDictionary<int, string>();
// Parallel processing — no batch size limit, no network round-trips
Parallel.For(0, imagePaths.Length, i =>
{
var ocr = new IronTesseract(); // thread-safe: one instance per thread
results[i] = ocr.Read(imagePaths[i]).Text;
});
// Reconstruct in original order
return Enumerable.Range(0, imagePaths.Length)
.Select(i => results[i])
.ToList();
}
public OcrResult BatchAsDocument(string[] imagePaths)
{
// Load all images into a single OcrInput for combined document output
using var input = new OcrInput();
foreach (var path in imagePaths)
input.LoadImage(path);
return new IronTesseract().Read(input);
}
}
using IronOcr;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
public class BatchAnnotationService
{
public List<string> BatchAnnotateImages(string[] imagePaths)
{
var results = new ConcurrentDictionary<int, string>();
// Parallel processing — no batch size limit, no network round-trips
Parallel.For(0, imagePaths.Length, i =>
{
var ocr = new IronTesseract(); // thread-safe: one instance per thread
results[i] = ocr.Read(imagePaths[i]).Text;
});
// Reconstruct in original order
return Enumerable.Range(0, imagePaths.Length)
.Select(i => results[i])
.ToList();
}
public OcrResult BatchAsDocument(string[] imagePaths)
{
// Load all images into a single OcrInput for combined document output
using var input = new OcrInput();
foreach (var path in imagePaths)
input.LoadImage(path);
return new IronTesseract().Read(input);
}
}
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Collections.Generic
Imports System.Threading.Tasks
Public Class BatchAnnotationService
Public Function BatchAnnotateImages(imagePaths As String()) As List(Of String)
Dim results = New ConcurrentDictionary(Of Integer, String)()
' Parallel processing — no batch size limit, no network round-trips
Parallel.For(0, imagePaths.Length, Sub(i)
Dim ocr = New IronTesseract() ' thread-safe: one instance per thread
results(i) = ocr.Read(imagePaths(i)).Text
End Sub)
' Reconstruct in original order
Return Enumerable.Range(0, imagePaths.Length) _
.Select(Function(i) results(i)) _
.ToList()
End Function
Public Function BatchAsDocument(imagePaths As String()) As OcrResult
' Load all images into a single OcrInput for combined document output
Using input As New OcrInput()
For Each path In imagePaths
input.LoadImage(path)
Next
Return New IronTesseract().Read(input)
End Using
End Function
End Class
O IronOCR executa o processamento em lote em paralelo em vários núcleos da CPU, sem qualquer sobrecarga de rede. Não há limite de BatchSize, não há correspondência de índice de resposta e não há tratamento de erro por item para falhas de rede ou credenciais. Para cargas de trabalho que combinam várias imagens em um único documento lógico — formulários multi-página escaneados enviados como JPEGs individuais, por exemplo — BatchAsDocument carrega todas as imagens em um OcrInput e retorna um OcrResult unificado com result.Pages indexado para cada imagem de entrada. O exemplo de multithreading mostra parâmetros de desempenho para processamento paralelo.
Migração da Extração de Anotações em Nível de Palavra do Protobuf
Os dados de caixa delimitadora e de confiança em nível de palavra doGoogle Cloud Visionexigem a navegação por toda a hierarquia do Protobuf: Páginas, depois Blocos, depois Parágrafos, depois Palavras e, por fim, Símbolos. A extração de texto de palavras requer a concatenação de Símbolos de cada Palavra — o objeto Word não tem propriedade .Text direta. As coordenadas da caixa delimitadora são armazenadas como um BoundingPoly com uma lista de Vertices em vez de como campos discretos de X, Y, Largura e Altura.
Abordagem do Google Cloud Vision:
using Google.Cloud.Vision.V1;
using System.Collections.Generic;
public record WordAnnotation(string Text, int X, int Y, int Width, int Height, float Confidence);
public class WordLevelExtractor
{
private readonly ImageAnnotatorClient _client;
public WordLevelExtractor()
{
_client = ImageAnnotatorClient.Create();
}
public List<WordAnnotation> ExtractWordAnnotations(string imagePath)
{
var image = Image.FromFile(imagePath);
var annotation = _client.DetectDocumentText(image);
var words = new List<WordAnnotation>();
// Navigate: Pages -> Blocks -> Paragraphs -> Words
foreach (var page in annotation.Pages)
{
foreach (var block in page.Blocks)
{
foreach (var paragraph in block.Paragraphs)
{
foreach (var word in paragraph.Words)
{
// Word.Text does not exist — must concatenate Symbols
var text = string.Concat(word.Symbols.Select(s => s.Text));
// BoundingPoly has Vertices, not X/Y/Width/Height
var vertices = word.BoundingBox.Vertices;
int x = vertices[0].X;
int y = vertices[0].Y;
int width = vertices.Count > 1 ? vertices[1].X - vertices[0].X : 0;
int height = vertices.Count > 2 ? vertices[2].Y - vertices[0].Y : 0;
words.Add(new WordAnnotation(text, x, y, width, height, word.Confidence));
}
}
}
}
return words;
}
}
using Google.Cloud.Vision.V1;
using System.Collections.Generic;
public record WordAnnotation(string Text, int X, int Y, int Width, int Height, float Confidence);
public class WordLevelExtractor
{
private readonly ImageAnnotatorClient _client;
public WordLevelExtractor()
{
_client = ImageAnnotatorClient.Create();
}
public List<WordAnnotation> ExtractWordAnnotations(string imagePath)
{
var image = Image.FromFile(imagePath);
var annotation = _client.DetectDocumentText(image);
var words = new List<WordAnnotation>();
// Navigate: Pages -> Blocks -> Paragraphs -> Words
foreach (var page in annotation.Pages)
{
foreach (var block in page.Blocks)
{
foreach (var paragraph in block.Paragraphs)
{
foreach (var word in paragraph.Words)
{
// Word.Text does not exist — must concatenate Symbols
var text = string.Concat(word.Symbols.Select(s => s.Text));
// BoundingPoly has Vertices, not X/Y/Width/Height
var vertices = word.BoundingBox.Vertices;
int x = vertices[0].X;
int y = vertices[0].Y;
int width = vertices.Count > 1 ? vertices[1].X - vertices[0].X : 0;
int height = vertices.Count > 2 ? vertices[2].Y - vertices[0].Y : 0;
words.Add(new WordAnnotation(text, x, y, width, height, word.Confidence));
}
}
}
}
return words;
}
}
Imports Google.Cloud.Vision.V1
Imports System.Collections.Generic
Imports System.Linq
Public Class WordAnnotation
Public Property Text As String
Public Property X As Integer
Public Property Y As Integer
Public Property Width As Integer
Public Property Height As Integer
Public Property Confidence As Single
Public Sub New(text As String, x As Integer, y As Integer, width As Integer, height As Integer, confidence As Single)
Me.Text = text
Me.X = x
Me.Y = y
Me.Width = width
Me.Height = height
Me.Confidence = confidence
End Sub
End Class
Public Class WordLevelExtractor
Private ReadOnly _client As ImageAnnotatorClient
Public Sub New()
_client = ImageAnnotatorClient.Create()
End Sub
Public Function ExtractWordAnnotations(imagePath As String) As List(Of WordAnnotation)
Dim image = Image.FromFile(imagePath)
Dim annotation = _client.DetectDocumentText(image)
Dim words As New List(Of WordAnnotation)()
' Navigate: Pages -> Blocks -> Paragraphs -> Words
For Each page In annotation.Pages
For Each block In page.Blocks
For Each paragraph In block.Paragraphs
For Each word In paragraph.Words
' Word.Text does not exist — must concatenate Symbols
Dim text = String.Concat(word.Symbols.Select(Function(s) s.Text))
' BoundingPoly has Vertices, not X/Y/Width/Height
Dim vertices = word.BoundingBox.Vertices
Dim x As Integer = vertices(0).X
Dim y As Integer = vertices(0).Y
Dim width As Integer = If(vertices.Count > 1, vertices(1).X - vertices(0).X, 0)
Dim height As Integer = If(vertices.Count > 2, vertices(2).Y - vertices(0).Y, 0)
words.Add(New WordAnnotation(text, x, y, width, height, word.Confidence))
Next
Next
Next
Next
Return words
End Function
End Class
Abordagem IronOCR:
using IronOcr;
using System.Collections.Generic;
public record WordAnnotation(string Text, int X, int Y, int Width, int Height, double Confidence);
public class WordLevelExtractor
{
private readonly IronTesseract _ocr;
public WordLevelExtractor()
{
_ocr = new IronTesseract();
}
public List<WordAnnotation> ExtractWordAnnotations(string imagePath)
{
var result = _ocr.Read(imagePath);
// Words are a flat collection — no hierarchy traversal, no symbol concatenation
return result.Words.Select(w => new WordAnnotation(
Text: w.Text, // direct string property
X: w.X,
Y: w.Y,
Width: w.Width,
Height: w.Height,
Confidence: w.Confidence // double, no conversion needed
)).ToList();
}
}
using IronOcr;
using System.Collections.Generic;
public record WordAnnotation(string Text, int X, int Y, int Width, int Height, double Confidence);
public class WordLevelExtractor
{
private readonly IronTesseract _ocr;
public WordLevelExtractor()
{
_ocr = new IronTesseract();
}
public List<WordAnnotation> ExtractWordAnnotations(string imagePath)
{
var result = _ocr.Read(imagePath);
// Words are a flat collection — no hierarchy traversal, no symbol concatenation
return result.Words.Select(w => new WordAnnotation(
Text: w.Text, // direct string property
X: w.X,
Y: w.Y,
Width: w.Width,
Height: w.Height,
Confidence: w.Confidence // double, no conversion needed
)).ToList();
}
}
Imports IronOcr
Imports System.Collections.Generic
Public Class WordAnnotation
Public Property Text As String
Public Property X As Integer
Public Property Y As Integer
Public Property Width As Integer
Public Property Height As Integer
Public Property Confidence As Double
Public Sub New(text As String, x As Integer, y As Integer, width As Integer, height As Integer, confidence As Double)
Me.Text = text
Me.X = x
Me.Y = y
Me.Width = width
Me.Height = height
Me.Confidence = confidence
End Sub
End Class
Public Class WordLevelExtractor
Private ReadOnly _ocr As IronTesseract
Public Sub New()
_ocr = New IronTesseract()
End Sub
Public Function ExtractWordAnnotations(imagePath As String) As List(Of WordAnnotation)
Dim result = _ocr.Read(imagePath)
' Words are a flat collection — no hierarchy traversal, no symbol concatenation
Return result.Words.Select(Function(w) New WordAnnotation(
Text:=w.Text, ' direct string property
X:=w.X,
Y:=w.Y,
Width:=w.Width,
Height:=w.Height,
Confidence:=w.Confidence ' double, no conversion needed
)).ToList()
End Function
End Class
O laço de concatenação de símbolos na versãoGoogle Cloud Visionnão é uma escolha de design — é um requisito do esquema Protobuf. Word.Text não é uma propriedade no objeto de resposta. Cada equipe que usa a API no nível de palavra escreve um loop equivalente. A coleção OcrResult.Words do IronOCR expõe Text, X, Y, Width, Height e Confidence como propriedades de primeira classe. O guia de resultados de leitura documenta o conjunto completo de propriedades disponíveis em cada nível de granularidade.
Processamento de TIFF de várias páginas para saída em PDF pesquisável
OGoogle Cloud Visiontrata arquivos TIFF com várias páginas como uma série sequencial de imagens, cada uma exigindo uma chamada de API separada. Não existe uma única chamada de API que aceite um arquivo TIFF e retorne uma saída estruturada para todos os quadros. Para gerar um PDF pesquisável a partir dos resultados do Google Cloud Vision, é necessária uma biblioteca de geração de PDF separada — a API retorna apenas texto.
Abordagem do Google Cloud Vision:
using Google.Cloud.Vision.V1;
using System.Collections.Generic;
using System.Drawing; // for TIFF frame extraction
using System.Drawing.Imaging;
public class TiffProcessingService
{
private readonly ImageAnnotatorClient _client;
public TiffProcessingService()
{
_client = ImageAnnotatorClient.Create();
}
public List<string> ProcessMultiPageTiff(string tiffPath)
{
var pageTexts = new List<string>();
// Load TIFF and extract frames manually using System.Drawing
using var tiff = System.Drawing.Image.FromFile(tiffPath);
var frameDimension = new FrameDimension(tiff.FrameDimensionsList[0]);
int frameCount = tiff.GetFrameCount(frameDimension);
for (int i = 0; i < frameCount; i++)
{
tiff.SelectActiveFrame(frameDimension, i);
// Save each frame to a temp file — Vision API does not accept TIFF frames directly
var tempPath = Path.Combine(Path.GetTempPath(), $"tiff-frame-{i}.jpg");
tiff.Save(tempPath, ImageFormat.Jpeg);
// One API call per frame — each call = one unit of quota
var visionImage = Google.Cloud.Vision.V1.Image.FromFile(tempPath);
var response = _client.DetectText(visionImage);
pageTexts.Add(response.FirstOrDefault()?.Description ?? string.Empty);
File.Delete(tempPath);
}
// Producing a searchable PDF requires a separate library (e.g., iTextSharp)
//Google Cloud Visionhas no PDF output capability
return pageTexts;
}
}
using Google.Cloud.Vision.V1;
using System.Collections.Generic;
using System.Drawing; // for TIFF frame extraction
using System.Drawing.Imaging;
public class TiffProcessingService
{
private readonly ImageAnnotatorClient _client;
public TiffProcessingService()
{
_client = ImageAnnotatorClient.Create();
}
public List<string> ProcessMultiPageTiff(string tiffPath)
{
var pageTexts = new List<string>();
// Load TIFF and extract frames manually using System.Drawing
using var tiff = System.Drawing.Image.FromFile(tiffPath);
var frameDimension = new FrameDimension(tiff.FrameDimensionsList[0]);
int frameCount = tiff.GetFrameCount(frameDimension);
for (int i = 0; i < frameCount; i++)
{
tiff.SelectActiveFrame(frameDimension, i);
// Save each frame to a temp file — Vision API does not accept TIFF frames directly
var tempPath = Path.Combine(Path.GetTempPath(), $"tiff-frame-{i}.jpg");
tiff.Save(tempPath, ImageFormat.Jpeg);
// One API call per frame — each call = one unit of quota
var visionImage = Google.Cloud.Vision.V1.Image.FromFile(tempPath);
var response = _client.DetectText(visionImage);
pageTexts.Add(response.FirstOrDefault()?.Description ?? string.Empty);
File.Delete(tempPath);
}
// Producing a searchable PDF requires a separate library (e.g., iTextSharp)
//Google Cloud Visionhas no PDF output capability
return pageTexts;
}
}
Imports Google.Cloud.Vision.V1
Imports System.Collections.Generic
Imports System.Drawing ' for TIFF frame extraction
Imports System.Drawing.Imaging
Imports System.IO
Public Class TiffProcessingService
Private ReadOnly _client As ImageAnnotatorClient
Public Sub New()
_client = ImageAnnotatorClient.Create()
End Sub
Public Function ProcessMultiPageTiff(tiffPath As String) As List(Of String)
Dim pageTexts As New List(Of String)()
' Load TIFF and extract frames manually using System.Drawing
Using tiff As System.Drawing.Image = System.Drawing.Image.FromFile(tiffPath)
Dim frameDimension As New FrameDimension(tiff.FrameDimensionsList(0))
Dim frameCount As Integer = tiff.GetFrameCount(frameDimension)
For i As Integer = 0 To frameCount - 1
tiff.SelectActiveFrame(frameDimension, i)
' Save each frame to a temp file — Vision API does not accept TIFF frames directly
Dim tempPath As String = Path.Combine(Path.GetTempPath(), $"tiff-frame-{i}.jpg")
tiff.Save(tempPath, ImageFormat.Jpeg)
' One API call per frame — each call = one unit of quota
Dim visionImage As Google.Cloud.Vision.V1.Image = Google.Cloud.Vision.V1.Image.FromFile(tempPath)
Dim response = _client.DetectText(visionImage)
pageTexts.Add(If(response.FirstOrDefault()?.Description, String.Empty))
File.Delete(tempPath)
Next
End Using
' Producing a searchable PDF requires a separate library (e.g., iTextSharp)
' Google Cloud Vision has no PDF output capability
Return pageTexts
End Function
End Class
Abordagem IronOCR:
using IronOcr;
public class TiffProcessingService
{
private readonly IronTesseract _ocr;
public TiffProcessingService()
{
_ocr = new IronTesseract();
}
public string ProcessMultiPageTiff(string tiffPath)
{
using var input = new OcrInput();
// LoadImageFrames handles all TIFF frames in one call — no temp files, no frame loop
input.LoadImageFrames(tiffPath);
var result = _ocr.Read(input);
return result.Text;
}
public void ProcessMultiPageTiffToSearchablePdf(string tiffPath, string outputPdfPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath);
var result = _ocr.Read(input);
//Google Cloud Visionhas no equivalent — this single call produces a searchable PDF
result.SaveAsSearchablePdf(outputPdfPath);
}
public void ProcessLowQualityTiff(string tiffPath, string outputPdfPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath);
// Preprocessing before OCR improves accuracy on degraded scans
input.Deskew();
input.DeNoise();
input.Contrast();
var result = _ocr.Read(input);
result.SaveAsSearchablePdf(outputPdfPath);
}
}
using IronOcr;
public class TiffProcessingService
{
private readonly IronTesseract _ocr;
public TiffProcessingService()
{
_ocr = new IronTesseract();
}
public string ProcessMultiPageTiff(string tiffPath)
{
using var input = new OcrInput();
// LoadImageFrames handles all TIFF frames in one call — no temp files, no frame loop
input.LoadImageFrames(tiffPath);
var result = _ocr.Read(input);
return result.Text;
}
public void ProcessMultiPageTiffToSearchablePdf(string tiffPath, string outputPdfPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath);
var result = _ocr.Read(input);
//Google Cloud Visionhas no equivalent — this single call produces a searchable PDF
result.SaveAsSearchablePdf(outputPdfPath);
}
public void ProcessLowQualityTiff(string tiffPath, string outputPdfPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath);
// Preprocessing before OCR improves accuracy on degraded scans
input.Deskew();
input.DeNoise();
input.Contrast();
var result = _ocr.Read(input);
result.SaveAsSearchablePdf(outputPdfPath);
}
}
Imports IronOcr
Public Class TiffProcessingService
Private ReadOnly _ocr As IronTesseract
Public Sub New()
_ocr = New IronTesseract()
End Sub
Public Function ProcessMultiPageTiff(tiffPath As String) As String
Using input As New OcrInput()
' LoadImageFrames handles all TIFF frames in one call — no temp files, no frame loop
input.LoadImageFrames(tiffPath)
Dim result = _ocr.Read(input)
Return result.Text
End Using
End Function
Public Sub ProcessMultiPageTiffToSearchablePdf(tiffPath As String, outputPdfPath As String)
Using input As New OcrInput()
input.LoadImageFrames(tiffPath)
Dim result = _ocr.Read(input)
' Google Cloud Vision has no equivalent — this single call produces a searchable PDF
result.SaveAsSearchablePdf(outputPdfPath)
End Using
End Sub
Public Sub ProcessLowQualityTiff(tiffPath As String, outputPdfPath As String)
Using input As New OcrInput()
input.LoadImageFrames(tiffPath)
' Preprocessing before OCR improves accuracy on degraded scans
input.Deskew()
input.DeNoise()
input.Contrast()
Dim result = _ocr.Read(input)
result.SaveAsSearchablePdf(outputPdfPath)
End Using
End Sub
End Class
A versão doGoogle Cloud Visionrequer System.Drawing para extração de quadros, arquivos JPEG temporários gravados em disco, uma chamada de API por quadro (consumindo a cota proporcionalmente à quantidade de quadros) e uma biblioteca PDF separada para gerar qualquer saída além de texto simples. O IronOCR manipula TIFFs de múltiplos quadros de forma nativa através de LoadImageFrames, processa todos os quadros em uma única chamada Read e produz uma saída PDF pesquisável através de SaveAsSearchablePdf. Para documentos TIFF arquivados digitalmente, o pipeline de pré-processamento em ProcessLowQualityTiff resolve os problemas de qualidade mais comuns em uma única passagem. Consulte a API completa para obter informações sobre a entrada em TIFF e GIF e a saída em PDF pesquisável .
Migração em lote com taxa limitada e acompanhamento do progresso
Em escala de produção, a cota padrão de 1.800 solicitações por minuto doGoogle Cloud Visionexige limitação de taxa ou lógica de repetição com recuo exponencial. Processar 5.000 documentos em uma única tarefa noturna excederá a cota várias vezes. Cada vez que a quota é excedida, o canal é bloqueado por um período obrigatório de espera de 60 segundos. O IronOCR não possui limite de taxa — o pipeline é limitado apenas pelos núcleos da CPU e pelos threads disponíveis.
Abordagem do Google Cloud Vision:
using Google.Cloud.Vision.V1;
using Grpc.Core;
using System.Collections.Generic;
public class ThrottledBatchProcessor
{
private readonly ImageAnnotatorClient _client;
private const int MaxRequestsPerMinute = 1800;
private const int RetryDelayMs = 60_000;
public ThrottledBatchProcessor()
{
_client = ImageAnnotatorClient.Create();
}
public async Task<Dictionary<string, string>> ProcessWithThrottlingAsync(
string[] imagePaths,
IProgress<(int completed, int total)> progress)
{
var results = new Dictionary<string, string>();
int completed = 0;
foreach (var path in imagePaths)
{
bool succeeded = false;
while (!succeeded)
{
try
{
var image = Google.Cloud.Vision.V1.Image.FromFile(path);
var response = _client.DetectText(image);
results[path] = response.FirstOrDefault()?.Description ?? string.Empty;
succeeded = true;
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.ResourceExhausted)
{
// Rate limit exceeded — wait and retry
await Task.Delay(RetryDelayMs);
}
}
progress.Report((++completed, imagePaths.Length));
}
return results;
}
}
using Google.Cloud.Vision.V1;
using Grpc.Core;
using System.Collections.Generic;
public class ThrottledBatchProcessor
{
private readonly ImageAnnotatorClient _client;
private const int MaxRequestsPerMinute = 1800;
private const int RetryDelayMs = 60_000;
public ThrottledBatchProcessor()
{
_client = ImageAnnotatorClient.Create();
}
public async Task<Dictionary<string, string>> ProcessWithThrottlingAsync(
string[] imagePaths,
IProgress<(int completed, int total)> progress)
{
var results = new Dictionary<string, string>();
int completed = 0;
foreach (var path in imagePaths)
{
bool succeeded = false;
while (!succeeded)
{
try
{
var image = Google.Cloud.Vision.V1.Image.FromFile(path);
var response = _client.DetectText(image);
results[path] = response.FirstOrDefault()?.Description ?? string.Empty;
succeeded = true;
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.ResourceExhausted)
{
// Rate limit exceeded — wait and retry
await Task.Delay(RetryDelayMs);
}
}
progress.Report((++completed, imagePaths.Length));
}
return results;
}
}
Imports Google.Cloud.Vision.V1
Imports Grpc.Core
Imports System.Collections.Generic
Imports System.Threading.Tasks
Public Class ThrottledBatchProcessor
Private ReadOnly _client As ImageAnnotatorClient
Private Const MaxRequestsPerMinute As Integer = 1800
Private Const RetryDelayMs As Integer = 60000
Public Sub New()
_client = ImageAnnotatorClient.Create()
End Sub
Public Async Function ProcessWithThrottlingAsync(
imagePaths As String(),
progress As IProgress(Of (completed As Integer, total As Integer))) As Task(Of Dictionary(Of String, String))
Dim results As New Dictionary(Of String, String)()
Dim completed As Integer = 0
For Each path In imagePaths
Dim succeeded As Boolean = False
While Not succeeded
Try
Dim image = Google.Cloud.Vision.V1.Image.FromFile(path)
Dim response = _client.DetectText(image)
results(path) = If(response.FirstOrDefault()?.Description, String.Empty)
succeeded = True
Catch ex As RpcException When ex.StatusCode = StatusCode.ResourceExhausted
' Rate limit exceeded — wait and retry
Await Task.Delay(RetryDelayMs)
End Try
End While
progress.Report((Threading.Interlocked.Increment(completed), imagePaths.Length))
Next
Return results
End Function
End Class
Abordagem IronOCR:
using IronOcr;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public class BatchProcessor
{
public Dictionary<string, string> ProcessBatch(
string[] imagePaths,
IProgress<(int completed, int total)> progress)
{
var results = new ConcurrentDictionary<string, string>();
int completed = 0;
// Sem limites de taxa — full parallelism, no retry logic needed
Parallel.ForEach(imagePaths, imagePath =>
{
var ocr = new IronTesseract();
results[imagePath] = ocr.Read(imagePath).Text;
progress.Report((Interlocked.Increment(ref completed), imagePaths.Length));
});
return new Dictionary<string, string>(results);
}
public void ProcessBatchToSearchablePdfs(
string[] imagePaths,
string outputDirectory)
{
Parallel.ForEach(imagePaths, imagePath =>
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
var outputPath = Path.Combine(
outputDirectory,
Path.GetFileNameWithoutExtension(imagePath) + "-searchable.pdf");
result.SaveAsSearchablePdf(outputPath);
});
}
}
using IronOcr;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public class BatchProcessor
{
public Dictionary<string, string> ProcessBatch(
string[] imagePaths,
IProgress<(int completed, int total)> progress)
{
var results = new ConcurrentDictionary<string, string>();
int completed = 0;
// Sem limites de taxa — full parallelism, no retry logic needed
Parallel.ForEach(imagePaths, imagePath =>
{
var ocr = new IronTesseract();
results[imagePath] = ocr.Read(imagePath).Text;
progress.Report((Interlocked.Increment(ref completed), imagePaths.Length));
});
return new Dictionary<string, string>(results);
}
public void ProcessBatchToSearchablePdfs(
string[] imagePaths,
string outputDirectory)
{
Parallel.ForEach(imagePaths, imagePath =>
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
var outputPath = Path.Combine(
outputDirectory,
Path.GetFileNameWithoutExtension(imagePath) + "-searchable.pdf");
result.SaveAsSearchablePdf(outputPath);
});
}
}
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks
Imports System.IO
Public Class BatchProcessor
Public Function ProcessBatch(imagePaths As String(), progress As IProgress(Of (completed As Integer, total As Integer))) As Dictionary(Of String, String)
Dim results As New ConcurrentDictionary(Of String, String)()
Dim completed As Integer = 0
' Sem limites de taxa — full parallelism, no retry logic needed
Parallel.ForEach(imagePaths, Sub(imagePath)
Dim ocr As New IronTesseract()
results(imagePath) = ocr.Read(imagePath).Text
progress.Report((Interlocked.Increment(completed), imagePaths.Length))
End Sub)
Return New Dictionary(Of String, String)(results)
End Function
Public Sub ProcessBatchToSearchablePdfs(imagePaths As String(), outputDirectory As String)
Parallel.ForEach(imagePaths, Sub(imagePath)
Dim ocr As New IronTesseract()
Dim result = ocr.Read(imagePath)
Dim outputPath = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(imagePath) & "-searchable.pdf")
result.SaveAsSearchablePdf(outputPath)
End Sub)
End Sub
End Class
A versão doGoogle Cloud Visioné sequencial porque as solicitações paralelas multiplicariam a exposição ao limite de taxa. Cada exceção ResourceExhausted adiciona uma paralisação completa de 60 segundos. Um lote de 5.000 documentos que atinge a cota 10 vezes adiciona 10 minutos de espera ociosa. A versão do IronOCR executa em paralelo em todos os núcleos disponíveis, sem tempo de espera. Para lotes de longa duração, a API de monitoramento de progresso fornece retornos de progresso integrados sem exigir a conexão manual Interlocked.Increment. Para questões de qualidade de imagem em digitalizações em lote, o guia de correção de qualidade de imagem cobre o pipeline de pré-processamento que pode ser adicionado antes de cada chamada Read.
Referência de mapeamento da API OCR doGoogle Cloud Visionpara IronOCR
| Google Cloud Vision | IronOCR | Notas |
|---|---|---|
ImageAnnotatorClient.Create() |
new IronTesseract() |
Inicialização do cliente; Não é necessário nenhum arquivo de credenciais. |
Image.FromFile(path) |
_ocr.Read(path) ou input.LoadImage(path) |
Leitura de caminho direto disponível em IronTesseract |
_client.DetectText(image) |
_ocr.Read(path).Text |
equivalente TEXT_DETECTION |
_client.DetectDocumentText(image) |
_ocr.Read(path) |
equivalente DOCUMENT_TEXT_DETECTION; O modo é automático. |
response[0].Description |
result.Text |
Texto completo do documento |
TextAnnotation |
OcrResult |
Contêiner de resultados de nível superior |
annotation.Text |
result.Text |
Texto completo |
annotation.Pages[i] |
result.Pages[i] |
Acesso por página |
page.Blocks[i].Paragraphs[j] |
result.Paragraphs[i] |
O IronOCR expõe parágrafos como uma coleção plana. |
paragraph.Words.SelectMany(w => w.Symbols).Select(s => s.Text) |
paragraph.Text |
Propriedade de string direta; nenhuma iteração de símbolo |
word.BoundingBox.Vertices |
word.X, word.Y, word.Width, word.Height |
Propriedades de inteiros discretos em vez de lista de vértices |
word.Confidence |
word.Confidence |
Pontuação de confiança por palavra |
page.Confidence |
result.Confidence |
Confiança geral nos resultados |
Feature.Types.Type.DocumentTextDetection |
Automático | O IronOCR seleciona automaticamente o modo de processamento. |
BatchAnnotateImagesRequest |
Parallel.ForEach + new IronTesseract() por thread |
Processamento local paralelo; sem limite de tamanho de lote |
_client.BatchAnnotateImages(requests) |
new IronTesseract().Read(input) com multi-imagem OcrInput |
Chamada única para entrada de múltiplas imagens |
AsyncBatchAnnotateFilesAsync() |
input.LoadPdf(); _ocr.Read(entrada) |
O processamento de PDF é síncrono; Não é necessário GCS |
StorageClient.Create() |
Não é necessário | Sem dependência do GCS |
storageClient.UploadObjectAsync() |
Não é necessário | Os PDFs são carregados diretamente do caminho local ou de um fluxo. |
operation.PollUntilCompletedAsync() |
Não é necessário | O processamento é síncrono. |
RpcException (StatusCode.ResourceExhausted) |
Não aplicável | Sem limites de taxa |
RpcException (StatusCode.PermissionDenied) |
Não aplicável | Sem autenticação em tempo de execução |
variável de ambiente GOOGLE_APPLICATION_CREDENTIALS |
IronOcr.License.LicenseKey |
Atribuição de string, não caminho de arquivo |
Problemas e soluções comuns em migrações
Problema 1: O construtor gera uma exceção sem as credenciais GOOGLE_APPLICATION_CREDENTIALS.
Google Cloud Vision: ImageAnnotatorClient.Create() lança RpcException com StatusCode.Unauthenticated ou StatusCode.PermissionDenied se a variável de ambiente não estiver definida ou apontar para um arquivo inválido. Essa falha ocorre na inicialização, não na primeira chamada da API, o que significa que todo o aplicativo não consegue inicializar se as credenciais estiverem ausentes em qualquer ambiente.
Solução: Após remover os pacotes do Google Cloud Vision, apague todas as referências a GOOGLE_APPLICATION_CREDENTIALS de suas configurações de ambiente, segredos do pipeline CI/CD, segredos do Kubernetes e arquivos do Docker Compose. Substitua por uma única variável de ambiente IRONOCR_LICENSE:
// Remove this from every deployment environment:
// GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
// Add this once at application startup:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
?? throw new InvalidOperationException("IRONOCR_LICENSE environment variable is required.");
// Remove this from every deployment environment:
// GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
// Add this once at application startup:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
?? throw new InvalidOperationException("IRONOCR_LICENSE environment variable is required.");
' Remove this from every deployment environment:
' GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
' Add this once at application startup:
IronOcr.License.LicenseKey = If(Environment.GetEnvironmentVariable("IRONOCR_LICENSE"), Throw New InvalidOperationException("IRONOCR_LICENSE environment variable is required."))
Problema 2: O código de concatenação de símbolos do Protobuf quebra após a remoção do namespace.
Google Cloud Vision: Cada local no seu código onde o texto de parágrafo ou palavra foi extraído usando .SelectMany(w => w.Symbols).Select(s => s.Text) produzirá erros de compilação após a remoção do namespace Google.Cloud.Vision.V1. Essas chamadas são distribuídas por todas as classes auxiliares ou de serviço que consumiram a resposta da API.
Solução: Pesquise todos os padrões SelectMany e w.Symbols em sua base de código e substitua-os por acesso direto a propriedades nos objetos de resultados do IronOCR. O guia de como ler resultados cobre todas as propriedades disponíveis em OcrResult, OcrResult.Page, OcrResult.Paragraph, OcrResult.Line e OcrResult.Word:
# Find all Protobuf symbol concatenation patterns
grep -rn "\.Symbols\." --include="*.cs" .
grep -rn "SelectMany.*Symbols" --include="*.cs" .
grep -rn "w\.Symbols\.Select" --include="*.cs" .
# Find all Protobuf symbol concatenation patterns
grep -rn "\.Symbols\." --include="*.cs" .
grep -rn "SelectMany.*Symbols" --include="*.cs" .
grep -rn "w\.Symbols\.Select" --include="*.cs" .
Substitua cada ocorrência:
// Before: symbol concatenation required by Protobuf schema
var text = string.Join("", paragraph.Words.SelectMany(w => w.Symbols).Select(s => s.Text));
// After: direct property on OcrResult.Paragraph
var text = paragraph.Text;
// Before: symbol concatenation required by Protobuf schema
var text = string.Join("", paragraph.Words.SelectMany(w => w.Symbols).Select(s => s.Text));
// After: direct property on OcrResult.Paragraph
var text = paragraph.Text;
' Before: symbol concatenation required by Protobuf schema
Dim text = String.Join("", paragraph.Words.SelectMany(Function(w) w.Symbols).Select(Function(s) s.Text))
' After: direct property on OcrResult.Paragraph
Dim text = paragraph.Text
Problema 3: O código de processamento de PDF não compila após a remoção do Storage.V1
Google Cloud Vision: Após remover Google.Cloud.Storage.V1, todo código que fazia referência a StorageClient, UploadObjectAsync, DeleteObjectAsync, AsyncAnnotateFileRequest, GcsSource, GcsDestination e PollUntilCompletedAsync falhará ao compilar. Este código pode abranger várias classes de serviço e tipicamente representa o maior bloco único de mudanças.
Solução: Exclua todo o pipeline do GCS. Substitua o método assíncrono de Plus de 50 linhas pelo equivalente de três linhas do IronOCR. Para código que mantinha uma assinatura assíncrona pela compatibilidade do chamador, envolva com Task.Run:
// Delete: StorageClient, GCS upload, AsyncBatchAnnotateFilesAsync,
// PollUntilCompletedAsync, output download, DeleteObjectAsync
// Replace with:
public async Task<string> ProcessPdfAsync(string pdfPath)
{
return await Task.Run(() =>
{
using var input = new OcrInput();
input.LoadPdf(pdfPath);
return new IronTesseract().Read(input).Text;
});
}
// Delete: StorageClient, GCS upload, AsyncBatchAnnotateFilesAsync,
// PollUntilCompletedAsync, output download, DeleteObjectAsync
// Replace with:
public async Task<string> ProcessPdfAsync(string pdfPath)
{
return await Task.Run(() =>
{
using var input = new OcrInput();
input.LoadPdf(pdfPath);
return new IronTesseract().Read(input).Text;
});
}
Imports System.Threading.Tasks
Public Async Function ProcessPdfAsync(pdfPath As String) As Task(Of String)
Return Await Task.Run(Function()
Using input As New OcrInput()
input.LoadPdf(pdfPath)
Return New IronTesseract().Read(input).Text
End Using
End Function)
End Function
Para novo código, use o suporte assíncrono nativo de OCR em vez do encapsulador Task.Run. O guia de entrada de PDF abrange a seleção do intervalo de páginas e o carregamento de PDFs protegidos por senha.
Problema 4: A lógica de repetição de limite de taxa não é mais necessária.
Google Cloud Vision: Qualquer código que captura RpcException com StatusCode.ResourceExhausted e implementa um padrão de espera e nova tentativa foi escrito para lidar com a cota de 1.800 solicitações por minuto. Essa lógica de repetição pode estar incorporada em middleware, etapas de pipeline ou loops de processamento em lote.
Solução: Remova toda a lógica de repetição associada a erros de cota. O IronOCR processa os dados localmente, sem necessidade de cota externa. O contrato de manipulador de erros muda de cinco casos RpcException para dois:
// Remove: all RpcException handlers for ResourceExhausted, PermissionDenied,
// Unavailable, DeadlineExceeded, Unauthenticated
//IronOCR error surface:
try
{
var result = new IronTesseract().Read(imagePath);
if (result.Confidence < 50)
input.DeNoise(); // add preprocessing for low-confidence results
return result.Text;
}
catch (IOException ex)
{
// File not found or locked
throw new InvalidOperationException($"Cannot read: {imagePath}", ex);
}
catch (IronOcr.Exceptions.OcrException ex)
{
// Processing failure — not a transient network error
throw new InvalidOperationException($"OCR failed: {ex.Message}", ex);
}
// Remove: all RpcException handlers for ResourceExhausted, PermissionDenied,
// Unavailable, DeadlineExceeded, Unauthenticated
//IronOCR error surface:
try
{
var result = new IronTesseract().Read(imagePath);
if (result.Confidence < 50)
input.DeNoise(); // add preprocessing for low-confidence results
return result.Text;
}
catch (IOException ex)
{
// File not found or locked
throw new InvalidOperationException($"Cannot read: {imagePath}", ex);
}
catch (IronOcr.Exceptions.OcrException ex)
{
// Processing failure — not a transient network error
throw new InvalidOperationException($"OCR failed: {ex.Message}", ex);
}
Imports IronOcr
Imports System.IO
' Remove: all RpcException handlers for ResourceExhausted, PermissionDenied,
' Unavailable, DeadlineExceeded, Unauthenticated
'IronOCR error surface:
Try
Dim result = New IronTesseract().Read(imagePath)
If result.Confidence < 50 Then
input.DeNoise() ' add preprocessing for low-confidence results
End If
Return result.Text
Catch ex As IOException
' File not found or locked
Throw New InvalidOperationException($"Cannot read: {imagePath}", ex)
Catch ex As IronOcr.Exceptions.OcrException
' Processing failure — not a transient network error
Throw New InvalidOperationException($"OCR failed: {ex.Message}", ex)
End Try
Problema 5: TIFF com várias páginas requer loop de extração de quadros
Google Cloud Vision: O código de processamento de TIFF existente provavelmente extrai quadros usando System.Drawing.Image, salva cada quadro como um JPEG em um diretório temporário, submete cada JPEG como uma chamada separada de API e exclui os arquivos temporários depois. Esse padrão consome uma unidade de cota por quadro e pode deixar arquivos temporários órfãos em caso de falha.
Solução: Substitua o loop de extração de quadros e gerenciamento de arquivos temporários com input.LoadImageFrames(). Todo o loop do frame System.Drawing foi excluído:
// Remove: System.Drawing frame extraction, temp file writes, per-frame API calls
// Replace with:
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // all frames, no temp files
var result = new IronTesseract().Read(input);
// Remove: System.Drawing frame extraction, temp file writes, per-frame API calls
// Replace with:
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // all frames, no temp files
var result = new IronTesseract().Read(input);
Imports IronOcr
Dim input As New OcrInput()
input.LoadImageFrames(tiffPath) ' all frames, no temp files
Dim result = (New IronTesseract()).Read(input)
Consulte o guia de entrada TIFF e GIF para opções de processamento de vários quadros, incluindo a seleção do intervalo de quadros.
Problema 6: Interrupção nos cálculos dos vértices do BoundingPoly
Google Cloud Vision: O código que lia coordenadas de caixas delimitadoras de annotation.BoundingPoly.Vertices calculava X, Y, Largura e Altura a partir de aritmética de índice de vértice: vertices[0].X para X, vertices[1].X - vertices[0].X para Largura, vertices[2].Y - vertices[0].Y para Altura. Após a migração, essas expressões não têm equivalente no IronOCR porque Vertices não existe.
Solução: Substitua a aritmética de vértices por propriedades int diretas. Não é necessário fazer nenhum cálculo:
// Before: vertex index arithmetic
int x = word.BoundingBox.Vertices[0].X;
int y = word.BoundingBox.Vertices[0].Y;
int width = word.BoundingBox.Vertices[1].X - word.BoundingBox.Vertices[0].X;
int height = word.BoundingBox.Vertices[2].Y - word.BoundingBox.Vertices[0].Y;
// After: direct properties
int x = word.X;
int y = word.Y;
int width = word.Width;
int height = word.Height;
// Before: vertex index arithmetic
int x = word.BoundingBox.Vertices[0].X;
int y = word.BoundingBox.Vertices[0].Y;
int width = word.BoundingBox.Vertices[1].X - word.BoundingBox.Vertices[0].X;
int height = word.BoundingBox.Vertices[2].Y - word.BoundingBox.Vertices[0].Y;
// After: direct properties
int x = word.X;
int y = word.Y;
int width = word.Width;
int height = word.Height;
' Before: vertex index arithmetic
Dim x As Integer = word.BoundingBox.Vertices(0).X
Dim y As Integer = word.BoundingBox.Vertices(0).Y
Dim width As Integer = word.BoundingBox.Vertices(1).X - word.BoundingBox.Vertices(0).X
Dim height As Integer = word.BoundingBox.Vertices(2).Y - word.BoundingBox.Vertices(0).Y
' After: direct properties
Dim x As Integer = word.X
Dim y As Integer = word.Y
Dim width As Integer = word.Width
Dim height As Integer = word.Height
Lista de verificação para migração do OCR do Google Cloud Vision
Pré-migração
Antes de fazer qualquer alteração, audite o código-fonte para identificar todas as dependências do Google Cloud Vision:
# Find allGoogle Cloud Visionnamespace imports
grep -rn "using Google.Cloud.Vision" --include="*.cs" .
grep -rn "using Google.Cloud.Storage" --include="*.cs" .
grep -rn "using Grpc.Core" --include="*.cs" .
# Find ImageAnnotatorClient usage
grep -rn "ImageAnnotatorClient" --include="*.cs" .
# Find GCS pipeline code
grep -rn "StorageClient\|UploadObjectAsync\|DeleteObjectAsync" --include="*.cs" .
grep -rn "AsyncBatchAnnotateFilesAsync\|PollUntilCompleted" --include="*.cs" .
# Find Protobuf symbol concatenation
grep -rn "\.Symbols\." --include="*.cs" .
grep -rn "SelectMany.*Symbols" --include="*.cs" .
# Find BoundingPoly vertex calculations
grep -rn "BoundingPoly\|BoundingBox\.Vertices" --include="*.cs" .
# Find rate limit retry handlers
grep -rn "ResourceExhausted\|StatusCode\." --include="*.cs" .
# Find environment variable references
grep -rn "GOOGLE_APPLICATION_CREDENTIALS" .
# Find allGoogle Cloud Visionnamespace imports
grep -rn "using Google.Cloud.Vision" --include="*.cs" .
grep -rn "using Google.Cloud.Storage" --include="*.cs" .
grep -rn "using Grpc.Core" --include="*.cs" .
# Find ImageAnnotatorClient usage
grep -rn "ImageAnnotatorClient" --include="*.cs" .
# Find GCS pipeline code
grep -rn "StorageClient\|UploadObjectAsync\|DeleteObjectAsync" --include="*.cs" .
grep -rn "AsyncBatchAnnotateFilesAsync\|PollUntilCompleted" --include="*.cs" .
# Find Protobuf symbol concatenation
grep -rn "\.Symbols\." --include="*.cs" .
grep -rn "SelectMany.*Symbols" --include="*.cs" .
# Find BoundingPoly vertex calculations
grep -rn "BoundingPoly\|BoundingBox\.Vertices" --include="*.cs" .
# Find rate limit retry handlers
grep -rn "ResourceExhausted\|StatusCode\." --include="*.cs" .
# Find environment variable references
grep -rn "GOOGLE_APPLICATION_CREDENTIALS" .
Anotações de inventário a serem preenchidas antes de começar:
- Liste todos os buckets do GCS criados para entrada/saída de OCR — agende a limpeza após a migração.
- Documente o endereço de e-mail da conta de serviço para que ela possa ser desativada no Console do GCP após a migração.
- Identifique todos os ambientes onde
GOOGLE_APPLICATION_CREDENTIALSestá configurado Observe qualquer código que leia IDs de projetos ou nomes de buckets do GCP a partir da configuração — remova-o após a migração.
Migração de código
- Remova o pacote NuGet
Google.Cloud.Vision.V1de todos os projetos - Remova o pacote NuGet
Google.Cloud.Storage.V1de todos os projetos - Instale o pacote NuGet
IronOcrem todos os projetos que realizam OCR - Adicione a inicialização
IronOcr.License.LicenseKeyna inicialização do aplicativo - Substitua todas as importações
using Google.Cloud.Vision.V1porusing IronOcr - Substitua todas as importações
using Google.Cloud.Storage.V1eusing Grpc.Core - Substitua
ImageAnnotatorClient.Create()pornew IronTesseract() - Apague todos os métodos de pipeline GCS (
StorageClient,UploadObjectAsync, anotação assíncrona, polling, download, exclusão) - Substitua
input.LoadPdf()para todos os caminhos de processamento de PDF (removendo a orquestração GCS assíncrona) - Substitua todos os loops de concatenação de símbolos Protobuf por acesso direto à propriedade
.Textem objetosOcrResult - Substitua cálculos de índice
BoundingPoly.Verticesporword.X,word.Y,word.Width,word.Height - Remova todos os blocos de captura
RpcExceptionparaResourceExhausted,PermissionDenied,Unavailable,DeadlineExceededeUnauthenticated - Substitua o loop por quadro de TIFF por
input.LoadImageFrames() - Converta loops de lote sequenciais para
Parallel.ForEachcom instânciasIronTesseractpor thread - Remova
GOOGLE_APPLICATION_CREDENTIALSde todas as configurações de ambiente, pipelines CI/CD, arquivos do Docker Compose e segredos do Kubernetes
Pós-migração
- Verifique se não há tipos
RpcExceptionouGoogleApiExceptionreferenciados no código de manipulação de erros - Confirme que
GOOGLE_APPLICATION_CREDENTIALSestá ausente de todas as configurações de ambiente de implantação - Execute o pipeline de OCR no mesmo conjunto de documentos de amostra usado em produção e compare a qualidade do texto gerado.
- Testar o processamento de PDF em documentos previamente processados pelo pipeline assíncrono do GCS e confirmar a identidade do texto de saída.
- Teste PDFs protegidos por senha com
input.LoadPdf(path, Password: "...")— anteriormente não suportados - Teste o processamento de TIFF multi-página usando
input.LoadImageFrames()e verifique se todos os quadros são processados Execute o processador em lote em uma amostra representativa e confirme se a qualidade da saída corresponde aos resultados anteriores. - Confirme que os valores
result.Confidenceestão dentro da faixa aceitável para o seu corpus de documentos - Verifique a saída de PDF pesquisável usando
result.SaveAsSearchablePdf()para documentos que anteriormente exigiam uma biblioteca PDF separada Execute o aplicativo em um ambiente sem conectividade de internet de saída e confirme se o OCR funciona corretamente.
Principais benefícios da migração para o IronOCR
A Superfície de Credenciais Reduzida a Zero Arquivos. Após a migração, não há arquivos de chave JSON, nenhuma configuração de bucket GCS, nenhum papel IAM, nenhuma conta de serviço, e nenhuma variável de ambiente GOOGLE_APPLICATION_CREDENTIALS em sua infraestrutura. Toda a superfície de credenciais é uma única variável de ambiente que contém uma string de chave de licença. A rotação de chaves, que era uma operação periódica obrigatória no Google Cloud Vision, não é mais um conceito aplicável. Para equipes que operam em várias regiões ou provedores de nuvem, a redução na complexidade da configuração de implantação é imediata.
Processamento de PDF e TIFF sem dependências externas. O pipeline assíncrono do GCS e o loop de quadros TIFF do System.Drawing são completamente removidos. input.LoadPdf() e input.LoadImageFrames() são as substituições — ambas síncronas, ambas locais, ambas três linhas de chamada a resultado. Os PDFs protegidos por senha, que eram impossíveis com o Google Cloud Vision, agora funcionam com um único parâmetro adicional. O guia de OCR em PDF e o guia de entrada em TIFF abrangem toda a API de entrada.
Processamento em lote na velocidade da CPU. A remoção da cota de 1.800 solicitações por minuto e da espera obrigatória de 60 segundos para novas tentativas significa que os trabalhos em lote que antes tinham sua taxa de processamento limitada agora são executados na velocidade dos núcleos de processador disponíveis. Uma máquina com 16 núcleos processa 16 documentos simultaneamente sem necessidade de aprovação externa. O padrão Parallel.ForEach com instâncias IronTesseract por thread é a substituição direta para o loop sequencial limitado. O guia de otimização de velocidade cobre opções de configuração do motor que ajustam a taxa de processamento para tipos específicos de documentos.
Dados Estruturados Sem Protobuf. Cada OcrResult expõe Text, Confidence, Pages, Paragraphs, Lines, Words e Characters como propriedades .NET tipadas, sem dependência de namespace Protobuf, sem concatenação de símbolos e sem aritmética de vértices para caixas delimitadoras. Código que anteriormente requeria loops aninhados de 20 linhas para extrair texto de parágrafo reduz para result.Paragraphs.Select(p => p.Text). Para casos de uso que precisam de posicionamento em nível de palavra para análise de layout de documentos, word.X, word.Y, word.Width e word.Height estão disponíveis diretamente. A página de resultados do OCR apresenta documentos que descrevem todas as propriedades do modelo de resultados.
Saída de PDF pesquisável integrada. OGoogle Cloud Visionretorna apenas texto — gerar um PDF pesquisável exigia uma biblioteca de geração de PDF separada, adicionando outra dependência do NuGet , outra API para aprender e licenciamento adicional para avaliar. O result.SaveAsSearchablePdf(outputPath) do IronOCR produz um PDF totalmente pesquisável a partir de qualquer resultado de OCR em uma linha. Para fluxos de trabalho de arquivamento de documentos e pipelines de descoberta legal, isso elimina uma dependência inteira. O exemplo de PDF pesquisável demonstra o padrão de ponta a ponta.
Soberania de dados para setores regulamentados. Os documentos processados pelo IronOCR nunca saem do servidor. Para registros de saúde cobertos pela HIPAA, dados técnicos controlados pela ITAR, materiais de contratados de defesa abrangidos pela CMMC, documentos legais protegidos pelo sigilo advogado-cliente e registros financeiros abrangidos pela PCI-DSS, a arquitetura local remove completamente a categoria de processador de dados de terceiros do escopo de conformidade. Não há Acordo de Parceiro Comercial para negociar, nenhum DPA para assinar e nenhuma política de retenção de dados do Google para revisar. O centro de documentação do IronOCR abrange configurações de implantação para ambientes Docker, Linux, Azure e AWS onde se aplicam requisitos de residência de dados.
Perguntas frequentes
Por que devo migrar da API Google Cloud Vision para o IronOCR?
Entre os principais motivos, incluem-se a eliminação da complexidade da interoperabilidade COM, a substituição do gerenciamento de licenças baseado em arquivos, a eliminação da cobrança por página, a viabilização da implantação em Docker/contêineres e a adoção de um fluxo de trabalho nativo do NuGet que se integra às ferramentas padrão do .NET.
Quais são as principais alterações de código ao migrar da API Google Cloud Vision para o IronOCR?
Substitua as sequências de inicialização do Google Cloud Vision pela instanciação do IronTesseract, remova o gerenciamento do ciclo de vida do COM (padrões explícitos de Criação/Carregamento/Fechamento) e atualize os nomes das propriedades de resultado. O resultado é uma redução significativa no número de linhas de código repetitivo.
Como faço para instalar o IronOCR para iniciar a migração?
Execute 'Install-Package IronOcr' no Console do Gerenciador de Pacotes ou 'dotnet add package IronOcr' na CLI. Os pacotes de idiomas são pacotes separados: 'dotnet add package IronOcr.Languages.French' para francês, por exemplo.
O IronOCR atinge a mesma precisão de OCR que a API Google Cloud Vision para documentos comerciais padrão?
O IronOCR alcança alta precisão para conteúdo comercial padrão, incluindo faturas, contratos, recibos e formulários digitados. Filtros de pré-processamento de imagem (correção de distorção, remoção de ruído, aprimoramento de contraste) melhoram ainda mais o reconhecimento em entradas de baixa qualidade.
Como o IronOCR lida com os dados de idioma que a API Google Cloud Vision instala separadamente?
Os dados de idioma no IronOCR são distribuídos como pacotes NuGet. O comando 'dotnet add package IronOcr.Languages.German' instala o suporte ao alemão. Não é necessário inserir arquivos manualmente nem configurar caminhos de diretório.
A migração da API Google Cloud Vision para o IronOCR exige alterações na infraestrutura de implantação?
O IronOCR requer menos alterações de infraestrutura do que a API Google Cloud Vision. Não há caminhos binários do SDK, necessidade de inserir arquivos de licença ou configurar servidores de licença. O pacote NuGet contém o mecanismo OCR completo e a chave de licença é uma string definida no código do aplicativo.
Como configuro o licenciamento do IronOCR após a migração?
Atribua `IronOcr.License.LicenseKey = "YOUR-KEY"` no código de inicialização do aplicativo. No Docker ou Kubernetes, armazene a chave como uma variável de ambiente e leia-a na inicialização. Use `License.IsValidLicense` para validar a licença antes de aceitar o tráfego.
O IronOCR consegue processar PDFs da mesma forma que o Google Cloud Vision?
Sim. O IronOCR lê PDFs nativos e digitalizados. Instancie o IronTesseract, chame ocr.Read(input) onde input é um caminho para um PDF ou OcrPdfInput, e itere pelas páginas do OcrResult. Não é necessário um pipeline de renderização de PDF separado.
Como o IronOCR lida com multithreading em processamento de alto volume?
O IronTesseract pode ser instanciado com segurança por thread. Crie uma instância por thread em um Parallel.ForEach ou pool de Tasks, execute o OCR simultaneamente e descarte cada instância ao terminar. Não é necessário nenhum estado global ou bloqueio.
Quais formatos de saída o IronOCR suporta após a extração de texto?
O IronOCR retorna resultados estruturados, incluindo texto, coordenadas de palavras, níveis de confiança e estrutura da página. As opções de exportação incluem texto simples, PDF pesquisável e objetos de resultados estruturados para processamento posterior.
O preço do IronOCR é mais previsível do que o da API Google Cloud Vision para dimensionamento de cargas de trabalho?
O IronOCR utiliza licenciamento perpétuo com preço fixo, sem cobranças por página ou volume. Independentemente de você processar 10.000 ou 10 milhões de páginas, o custo da licença permanece constante. As opções de licenciamento por volume e para equipes estão disponíveis na página de preços do IronOCR.
O que acontece com meus testes existentes após a migração da API Google Cloud Vision para o IronOCR?
Os testes que verificam o conteúdo de texto extraído devem continuar a ser aprovados após a migração. Os testes que validam padrões de chamadas de API ou o ciclo de vida de objetos COM precisarão ser atualizados para refletir o modelo de inicialização e resultados mais simples do IronOCR.

