Ir para o conteúdo do rodapé
VíDEOS

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
SHELL

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
$vbLabelText   $csharpLabel

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"
$vbLabelText   $csharpLabel

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."))
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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."))
$vbLabelText   $csharpLabel

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" .
SHELL

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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)
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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" .
SHELL

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_CREDENTIALS está 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

  1. Remova o pacote NuGet Google.Cloud.Vision.V1 de todos os projetos
  2. Remova o pacote NuGet Google.Cloud.Storage.V1 de todos os projetos
  3. Instale o pacote NuGet IronOcr em todos os projetos que realizam OCR
  4. Adicione a inicialização IronOcr.License.LicenseKey na inicialização do aplicativo
  5. Substitua todas as importações using Google.Cloud.Vision.V1 por using IronOcr
  6. Substitua todas as importações using Google.Cloud.Storage.V1 e using Grpc.Core
  7. Substitua ImageAnnotatorClient.Create() por new IronTesseract()
  8. Apague todos os métodos de pipeline GCS (StorageClient, UploadObjectAsync, anotação assíncrona, polling, download, exclusão)
  9. Substitua input.LoadPdf() para todos os caminhos de processamento de PDF (removendo a orquestração GCS assíncrona)
  10. Substitua todos os loops de concatenação de símbolos Protobuf por acesso direto à propriedade .Text em objetos OcrResult
  11. Substitua cálculos de índice BoundingPoly.Vertices por word.X, word.Y, word.Width, word.Height
  12. Remova todos os blocos de captura RpcException para ResourceExhausted, PermissionDenied, Unavailable, DeadlineExceeded e Unauthenticated
  13. Substitua o loop por quadro de TIFF por input.LoadImageFrames()
  14. Converta loops de lote sequenciais para Parallel.ForEach com instâncias IronTesseract por thread
  15. Remova GOOGLE_APPLICATION_CREDENTIALS de 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 RpcException ou GoogleApiException referenciados no código de manipulação de erros
  • Confirme que GOOGLE_APPLICATION_CREDENTIALS está 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.Confidence estã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.

ObserveGoogle Cloud Vision, Tesseract, e iText são marcas registradas de seus respectivos proprietários. Este site não está afiliado, endossado ou patrocinado pelo Google ou iText Group. Todos os nomes de produtos, logotipos e marcas são propriedade de seus respectivos proprietários. As comparações são apenas para fins informativos e refletem informações disponíveis publicamente no momento da redação.

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.

Kannaopat Udonpant
Engenheiro de Software
Antes de se tornar Engenheiro de Software, Kannapat concluiu um doutorado em Recursos Ambientais pela Universidade de Hokkaido, no Japão. Durante o doutorado, Kannapat também integrou o Laboratório de Robótica Veicular, que faz parte do Departamento de Engenharia de Bioprodução. Em 2022, ele utilizou suas habilidades ...
Leia mais

Equipe de Suporte Iron

Estamos online 24 horas por dia, 5 dias por semana.
Bater papo
E-mail
Liga para mim