Saltar al pie de página
VIDEOS

Cómo instalar Tesseract OCR en Windows en c#

Esta guía acompaña a los desarrolladores .NET en el proceso de reemplazar Visión de Google Cloudcon IronOCR como motor OCR local. Cubre la eliminación de credenciales, el reemplazo del análisis de anotaciones Protobuf, la simplificación de anotaciones por lotes y el procesamiento de documentos de varias páginas: los cuatro cambios estructurales que representan la mayor parte del trabajo de migración.

¿Por qué migrar desde Visión de Google CloudOCR?

La decisión de migrar casi siempre comienza con una de dos constataciones: una auditoría de cumplimiento bloquea la transmisión de documentos en la nube, o la complejidad operativa de administrar las credenciales de GCP, los buckets de GCS, el sondeo asíncrono y la facturación por imagen se vuelve más costosa que el propio OCR.

Ciclo de vida de la clave JSON de la cuenta de servicio. Cada despliegue de su aplicación (máquina de desarrollo, canalización de CI/CD, servidor de pruebas, servidor de producción, contenedor Docker, pod de Kubernetes) requiere el mismo archivo de clave JSON de la cuenta de servicio. Este archivo contiene una clave privada RSA. Nunca debe entrar en el control de versiones, debe rotarse según un cronograma, debe estar protegido con permisos del sistema de archivos y debe actualizarse en todos los entornos simultáneamente cuando se produzca la rotación. Una clave comprometida otorga acceso a la API hasta que se revoca manualmente en la consola de GCP.IronOCR reemplaza toda esta superficie operativa con una única cadena de clave de licencia que se configura una sola vez al iniciar la aplicación.

Facturación por solicitud a gran escala. Con un coste de 1,50 $ por cada 1000 imágenes y 0,0015 $ por página PDF, los costes son imperceptibles durante el desarrollo y muy costosos en la producción. Un sistema de procesamiento de documentos que gestiona 200.000 páginas al mes cuesta 300 dólares al mes solo en tarifas de API, sin contar los cargos de almacenamiento de GCS ni los costes de salida de datos. Esos 300 dólares se repiten cada mes indefinidamente. La licencia perpetua de IronOCR convierte el OCR, que antes era un gasto operativo medido, en una partida de capital fijo cuyo funcionamiento no tiene coste alguno durante el segundo o tercer año.

Canalización asíncrona de GCS para archivos PDF. Visión de Google Cloudno acepta archivos PDF como entrada directa a la API. El pipeline completo requiere un segundo paquete de NuGet (Google.Cloud.Storage.V1), un bucket de GCS aprovisionado, una carga asíncrona, una llamada AsyncBatchAnnotateFilesAsync, un bucle de sondeo, análisis de salida JSON desde GCS, y un paso de limpieza. Ese pipeline abarca más de 50 líneas de código antes de que se haya extraído ningún texto.IronOCR lee un PDF en tres líneas, de forma síncrona y sin dependencias externas.

Concatenación de símbolos Protobuf. La respuesta DOCUMENT_TEXT_DETECTION almacena texto a nivel de símbolo dentro de una jerarquía de Protobuf de Páginas, Bloques, Párrafos, Palabras y Símbolos. Leer texto de párrafos requiere iterar cinco bucles anidados y llamar a .SelectMany(w => w.Symbols).Select(s => s.Text).IronOCR devuelve el texto de los párrafos como paragraph.Text — una propiedad de cadena tipada.

1,800 Solicitudes por Minuto Cuota Predeterminada. Las cargas de trabajo por lotes que superan la cuota predeterminada reciben respuestas StatusCode.ResourceExhausted que detienen el pipeline durante 60 segundos por cada exceso. Para aumentar la cuota, se requiere una solicitud a través de la consola de GCP y la aprobación de Google.IronOCR se procesa localmente a la velocidad de los núcleos de CPU disponibles; no hay cuotas que gestionar, ni aprobación que solicitar, ni lógica de reintento necesaria para limitar la velocidad.

No ofrece soporte sin conexión ni en entornos aislados. Visión de Google Cloudrequiere conexión a internet para acceder a los dispositivos de Google. Las redes aisladas, los centros de datos clasificados y los sistemas de control industrial no pueden utilizarlo en ningún nivel de complejidad arquitectónica.IronOCR funciona sin conectividad de red saliente después de la validación inicial de la licencia.

El problema fundamental

Google Cloud Vision requiere un archivo de clave JSON en el disco antes de que se ejecute la primera línea 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

IronOCR comienza con una sola cadena de texto y se ejecuta completamente en la 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 vs Visión de Google CloudOCR: Comparación de características

La tabla que aparece a continuación muestra directamente las características que pueden utilizar los equipos para elaborar el caso de negocio para la migración.

Característica OCR de Visión de Google Cloud IronOCR
Ubicación de procesamiento Google Cloud (remoto) En el establecimiento (local)
Autenticación Clave JSON de cuenta de servicio + variable de entorno Cadena de clave de licencia
Entrada en PDF Carga a GCS + API asíncrona input.LoadPdf() direct
PDF protegido con contraseña No soportado LoadPdf(path, Password: "...")
Entrada TIFF de varias páginas Limitado input.LoadImageFrames()
Salida en PDF con capacidad de búsqueda No disponible result.SaveAsSearchablePdf()
Acceso a datos estructurados Protobuf: Páginas > Bloques > Párrafos > Palabras > Símbolos result.Paragraphs, result.Lines, result.Words (objetos tipados de .NET)
Propiedad del texto del párrafo No — requiere concatenación de símbolos paragraph.Text direct property
Puntuaciones de confianza Por símbolo (requiere bucle) result.Confidence, word.Confidence
preprocesamiento automático de imágenes Ninguno (el aprendizaje automático lo gestiona) Enderezar, Reducir ruido, Contraste, Binarizar, Enfocar
OCR basado en la región No hay cultivos autóctonos CropRectangle on OcrInput
Lectura de códigos de barras Función de API independiente ocr.Configuration.ReadBarCodes = true
límites de velocidad 1800 solicitudes/minuto por defecto Ninguno (limitado por la CPU)
Sin conexión / aislado de la red No
Coste por documento 1,50 dólares por cada 1.000 imágenes; $0.0015/página PDF Ninguna (licencia perpetua)
Idiomas compatibles ~50 125+
Autorización FedRAMP No autorizado No aplicable (en las instalaciones)
Ruta de cumplimiento de HIPAA Se requiere un Acuerdo de Asociado Comercial No se realiza ningún manejo de datos por parte de terceros.
Compatibilidad con .NET Framework .NET Standard 2.0 o superior .NET Framework 4.6.2+ y .NET 5/6/7/8/9
Se requieren paquetes NuGet Google.Cloud.Vision.V1 + Google.Cloud.Storage.V1 IronOcr only
Modelo de precios Facturación por consumo según solicitud Perpetual ($999 Lite / $1,499 Plus / $2,999 Professional / $5,999 Unlimited)

Inicio rápido: Migración de OCR de Visión de Google Clouda IronOCR

Paso 1: Sustituir el paquete NuGet

Elimine los paquetes de 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 IronOCR desde la página del paquete NuGet :

dotnet add package IronOcr

Paso 2: Actualizar los espacios de nombres

Reemplace los espacios de nombres de Google Cloud con el espacio de nombres 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

Paso 3: Inicializar licencia

Agregue la inicialización de la licencia una vez al iniciar la aplicación, antes de que se cree cualquier instancia de IronTesseract:

IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
$vbLabelText   $csharpLabel

En producción, lea la clave desde una variable de entorno o un gestor de secretos:

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

Ejemplos de migración de código

Eliminación de la configuración de credenciales de la cuenta de servicio

La inicialización del cliente de Visión de Google Cloudparece una sola línea, pero requiere una amplia infraestructura previa. Cada desarrollador que se une al proyecto, cada entorno de despliegue y cada canalización de CI/CD necesita que la configuración completa de credenciales esté lista antes de que el constructor finalice sin generar errores.

Enfoque de 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

Enfoque IronOCR:

using IronOcr;

// Prerequisites: set the license key once at app startup
// No JSON files, no environment variables beyond the key, no GCP Console configuration
// No 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
// No JSON files, no environment variables beyond the key, no GCP Console configuration
// No 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
' No JSON files, no environment variables beyond the key, no GCP Console configuration
' No 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

La diferencia operativa es concreta: Visión de Google Cloudgenera cinco categorías de RpcException en tiempo de ejecución — PermissionDenied, ResourceExhausted, Unavailable, DeadlineExceeded, y Unauthenticated — cada uno representando un modo de fallo de infraestructura diferente. Los modos de fallo de IronOCR son IOException (archivo no encontrado o bloqueado) y OcrException (fallo de procesamiento). Consulte la guía de configuración de IronTesseract para conocer las opciones de configuración y la página del producto IronOCR para obtener detalles sobre la licencia.

Sustitución de las solicitudes de anotación por lotes con múltiples tipos de características

Google Cloud Vision admite agrupar múltiples imágenes en un solo BatchAnnotateImagesRequest, cada imagen configurada con una lista de tipos Feature. Este patrón se utiliza cuando una sola llamada necesita recoger tanto los resultados de TEXT_DETECTION como DOCUMENT_TEXT_DETECTION, o al enviar muchas imágenes para minimizar la sobrecarga de ida y vuelta. La respuesta de Protobuf requiere hacer coincidir cada AnnotateImageResponse con su solicitud original por índice.

Enfoque de 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 Característica { Type = Feature.Types.Type.TextDetection },
                new Característica { 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 Característica { Type = Feature.Types.Type.TextDetection },
                new Característica { 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

Enfoque 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

IronOCR ejecuta el procesamiento por lotes en paralelo en todos los núcleos de la CPU sin sobrecarga de red. No hay límite BatchSize, no hay coincidencia de índice de respuesta, y no hay manejo de errores por artículo para fallos de red o credenciales. Para las cargas de trabajo que combinan múltiples imágenes en un solo documento lógico — por ejemplo, formularios de varias páginas escaneados enviados como JPEG individuales — BatchAsDocument carga todas las imágenes en un OcrInput y devuelve un OcrResult unificado con result.Pages indexado a cada imagen de entrada. El ejemplo de multihilo muestra pruebas de rendimiento para el procesamiento paralelo.

Migración de la extracción de anotaciones a nivel de palabra de Protobuf

Los datos de confianza y de cuadro delimitador a nivel de palabra de Visión de Google Cloudrequieren navegar por toda la jerarquía de Protobuf: Páginas, luego Bloques, luego Párrafos, luego Palabras y luego Símbolos. Extraer texto de palabras requiere concatenar Símbolos de cada Palabra — el objeto Word no tiene una propiedad .Text directa. Las coordenadas de los cuadros delimitadores se almacenan como un BoundingPoly con una lista de Vertices en lugar de como campos discretos de X, Y, Ancho, Altura.

Enfoque de 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

Enfoque 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

El bucle de concatenación de símbolos en la versión de Visión de Google Cloudno es una decisión de diseño, sino un requisito del esquema Protobuf. Word.Text no es una propiedad en el objeto de respuesta. Cada equipo que usa la API a nivel de palabra escribe un bucle equivalente. La colección OcrResult.Words de IronOCR expone Text, X, Y, Width, Height, y Confidence como propiedades de primera clase. La guía de resultados de lectura documenta el conjunto completo de propiedades disponibles en cada nivel de granularidad.

Procesamiento de archivos TIFF de varias páginas para generar un archivo PDF con capacidad de búsqueda.

Google Cloud Vision trata los archivos TIFF de varias páginas como una serie secuencial de imágenes, cada una de las cuales requiere una llamada a la API independiente. No existe una única llamada a la API que acepte un archivo TIFF y devuelva una salida estructurada para todos los fotogramas. Para generar un PDF con capacidad de búsqueda a partir de los resultados de Google Cloud Vision, se necesita una biblioteca de generación de PDF independiente; la API solo devuelve texto.

Enfoque de 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)
        // Visión de Google Cloudhas 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)
        // Visión de Google Cloudhas 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)
        ' Visión de Google Cloudhas no PDF output capability
        Return pageTexts
    End Function
End Class
$vbLabelText   $csharpLabel

Enfoque 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);

        // Visión de Google Cloudhas 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);

        // Visión de Google Cloudhas 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)

            ' Visión de Google Cloudhas 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

La versión de Visión de Google Cloudrequiere System.Drawing para la extracción de fotogramas, archivos JPEG temporales que se escriben en el disco, una llamada a la API por fotograma (consumiendo una cuota proporcional al número de fotogramas) y una biblioteca PDF independiente para producir cualquier salida que no sea texto plano.IronOCR maneja TIFF de múltiples marcos de manera nativa a través de LoadImageFrames, procesa todos los marcos en una sola llamada Read, y produce una salida de PDF buscable a través de SaveAsSearchablePdf. Para documentos TIFF de archivo escaneado, el pipeline de preprocesamiento en ProcessLowQualityTiff aborda los problemas de calidad más comunes en una sola pasada. Consulte la documentación sobre entrada TIFF y GIF y salida PDF con capacidad de búsqueda para obtener la API completa.

Migración por lotes con límite de velocidad y seguimiento del progreso

A escala de producción, la cuota predeterminada de 1800 solicitudes por minuto de Visión de Google Cloudrequiere una lógica de limitación o de reintentos con retroceso exponencial. Procesar 5.000 documentos en una sola operación nocturna superará la cuota varias veces. Cada vez que se supera la cuota, el sistema se bloquea durante una espera obligatoria de 60 segundos.IronOCR no tiene límite de velocidad; la canalización solo está limitada por los núcleos de la CPU y los hilos disponibles.

Enfoque de 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

Enfoque 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;

        // Sin límites de tarifa — 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;

        // Sin límites de tarifa — 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

        ' Sin límites de tarifa — 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

La versión de Visión de Google Cloudes secuencial porque las solicitudes paralelas multiplicarían la exposición al límite de velocidad. Cada excepción ResourceExhausted agrega una pausa completa de 60 segundos. Un lote de 5.000 documentos que alcanza la cuota 10 veces añade 10 minutos de espera inactiva. La versión de IronOCR realiza la ejecución en paralelo en todos los núcleos disponibles sin tiempos de espera. Para lotes de larga duración, la API de seguimiento de progreso proporciona callbacks de progreso integrados sin requerir cableado Interlocked.Increment manual. Para problemas de calidad de imagen en escaneos por lotes, la guía de corrección de calidad de imagen cubre el pipeline de preprocesamiento que se puede agregar antes de cada llamada Read.

Referencia de mapeo de la API OCR de Visión de Google Clouda IronOCR

Visión de Google Cloud IronOCR Notas
ImageAnnotatorClient.Create() new IronTesseract() Inicialización del cliente; no se necesita archivo de credenciales
Image.FromFile(path) _ocr.Read(path) or input.LoadImage(path) Lectura de ruta directa disponible en IronTesseract
_client.DetectText(image) _ocr.Read(path).Text TEXT_DETECTION equivalent
_client.DetectDocumentText(image) _ocr.Read(path) DOCUMENT_TEXT_DETECTION equivalent; el modo es automático
response[0].Description result.Text Texto completo del documento
TextAnnotation OcrResult Contenedor de resultados de nivel superior
annotation.Text result.Text Cadena de texto completa
annotation.Pages[i] result.Pages[i] Acceso por página
page.Blocks[i].Paragraphs[j] result.Paragraphs[i] IronOCR expone los párrafos como una colección plana
paragraph.Words.SelectMany(w => w.Symbols).Select(s => s.Text) paragraph.Text Propiedad de cadena directa; sin iteración de símbolos
word.BoundingBox.Vertices word.X, word.Y, word.Width, word.Height Propiedades de enteros discretos en lugar de una lista de vértices.
word.Confidence word.Confidence Puntuación de confianza por palabra
page.Confidence result.Confidence Confianza en el resultado general
Feature.Types.Type.DocumentTextDetection Automático IronOCR selecciona automáticamente el modo de procesamiento.
BatchAnnotateImagesRequest Parallel.ForEach + new IronTesseract() por hilo Procesamiento local en paralelo; sin límite de tamaño de lote
_client.BatchAnnotateImages(requests) new IronTesseract().Read(input) con OcrInput de múltiples imágenes Llamada única para entrada de múltiples imágenes
AsyncBatchAnnotateFilesAsync() input.LoadPdf(); _ocr.Read(input) El procesamiento de archivos PDF es síncrono; No se requiere GCS
StorageClient.Create() No es necesario Sin dependencia de GCS
storageClient.UploadObjectAsync() No es necesario Los archivos PDF se cargan directamente desde una ruta local o un flujo de datos.
operation.PollUntilCompletedAsync() No es necesario El procesamiento es síncrono.
RpcException (StatusCode.ResourceExhausted) No procede Sin límites de tarifa
RpcException (StatusCode.PermissionDenied) No procede Sin autenticación en tiempo de ejecución
GOOGLE_APPLICATION_CREDENTIALS variable de entorno IronOcr.License.LicenseKey Asignación de cadena, no ruta de archivo

Problemas comunes de migración y soluciones

Problema 1: El constructor lanza una excepción sin GOOGLE_APPLICATION_CREDENTIALS

Google Cloud Vision: ImageAnnotatorClient.Create() lanza RpcException con StatusCode.Unauthenticated o StatusCode.PermissionDenied si la variable de entorno no está configurada o apunta a un archivo no válido. Este fallo ocurre al inicio, no en la primera llamada a la API, lo que significa que toda la aplicación no se inicializa si faltan credenciales en cualquier entorno.

Solución: Después de eliminar los paquetes de Google Cloud Vision, elimine todas las referencias a GOOGLE_APPLICATION_CREDENTIALS de las configuraciones de entorno, secretos del pipeline CI/CD, secretos de Kubernetes, y archivos de Docker Compose. Reemplace con una sola variable de entorno 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: El código de concatenación de símbolos de Protobuf deja de funcionar tras la eliminación del espacio de nombres.

Google Cloud Vision: Cada ubicación en su código donde se extraía texto de párrafos o palabras utilizando .SelectMany(w => w.Symbols).Select(s => s.Text) producirá errores de compilación después de que se elimine el namespace Google.Cloud.Vision.V1. Estas llamadas se distribuyen entre todas las clases de ayudantes o servicios que hayan consumido la respuesta de la API.

Solución: Busque todos los patrones SelectMany y w.Symbols en su código y reemplácelos con acceso directo de propiedad en objetos de resultados de IronOCR. La guía cómo leer resultados cubre cada propiedad disponible en OcrResult, OcrResult.Page, OcrResult.Paragraph, OcrResult.Line, y 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

Reemplazar cada aparición:

// 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: El código de procesamiento de PDF no compila después de eliminar Storage.V1.

Google Cloud Vision: Después de eliminar Google.Cloud.Storage.V1, todo el código que haga referencia a StorageClient, UploadObjectAsync, DeleteObjectAsync, AsyncAnnotateFileRequest, GcsSource, GcsDestination, y PollUntilCompletedAsync fallará al compilar. Este código puede abarcar múltiples clases de servicio y típicamente representa el bloque singular más grande de cambios.

Solución: Elimine toda la canalización de GCS. Reemplace el método asíncrono de más de 50 líneas con su equivalente de tres líneas en IronOCR. Para el código que mantenía una firma async para compatibilidad del llamante, envuélvalo con 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 código nuevo, utilice el soporte nativo de OCR asíncrono en lugar del envoltorio Task.Run. La guía de entrada de PDF abarca la selección del rango de páginas y la carga de archivos PDF protegidos con contraseña.

Problema 4: Ya no es necesaria la lógica de reintento con límite de velocidad.

Google Cloud Vision: Cualquier código que capture RpcException con StatusCode.ResourceExhausted e implemente un patrón de espera y reintento fue escrito para manejar la cuota de 1,800 solicitudes por minuto. Esta lógica de reintento puede estar integrada en middleware, pasos de la canalización o bucles de procesamiento por lotes.

Solución: Eliminar toda la lógica de reintento asociada a los errores de cuota.IronOCR realiza los procesos localmente sin necesidad de cuotas externas. El contrato del controlador de errores cambia de cinco casos RpcException a dos:

// 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: Los archivos TIFF de varias páginas requieren un bucle de extracción de fotogramas.

Google Cloud Vision: El código de procesamiento TIFF existente probablemente extrae marcos usando System.Drawing.Image, guarda cada marco como un JPEG en un directorio temporal, envía cada JPEG como una llamada API separada y elimina los archivos temporales después. Este patrón consume una unidad de cuota por fotograma y puede dejar archivos temporales huérfanos en caso de fallo.

Solución: Reemplace el bucle de extracción de marcos y la gestión de archivos temporales con input.LoadImageFrames(). Se elimina todo el bucle del marco System.Drawing:

// 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 la guía de entrada TIFF y GIF para obtener información sobre las opciones de procesamiento de múltiples fotogramas, incluida la selección del rango de fotogramas.

Problema 6: Interrupción en los cálculos de vértices de BoundingPoly

Google Cloud Vision: El código que leía las coordenadas del cuadro delimitador de annotation.BoundingPoly.Vertices calculaba X, Y, Anchura y Altura a partir de la aritmética de índice de vértice: vertices[0].X para X, vertices[1].X - vertices[0].X para Anchura, vertices[2].Y - vertices[0].Y para Altura. Después de la migración, estas expresiones no tienen equivalente en IronOCR porque Vertices no existe.

Solución: Reemplazar la aritmética de vértices con propiedades enteras directas. No es necesario realizar ningún 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 verificación para la migración de OCR de Google Cloud Vision

Pre-Migración

Antes de realizar cualquier cambio, revise el código fuente para identificar todas las dependencias de Google Cloud Vision:

# Find all Visión de Google Cloudnamespace 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 all Visión de Google Cloudnamespace 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

Notas de inventario que deben completarse antes de comenzar:

  • Enumerar todos los depósitos GCS creados para la entrada/salida de OCR; programar la limpieza después de la migración.
  • Documenta el correo electrónico de la cuenta de servicio para que pueda desactivarse en la consola de GCP después de la migración.
  • Identificar todos los entornos donde GOOGLE_APPLICATION_CREDENTIALS está configurado
  • Tenga en cuenta cualquier código que lea los ID de proyecto de GCP o los nombres de los buckets desde la configuración; elimínelo después de la migración.

Migración de código

  1. Eliminar el paquete de NuGet Google.Cloud.Vision.V1 de todos los proyectos
  2. Eliminar el paquete de NuGet Google.Cloud.Storage.V1 de todos los proyectos
  3. Instalar el paquete de NuGet IronOcr en todos los proyectos que realicen OCR
  4. Añadir IronOcr.License.LicenseKey de inicialización al inicio de la aplicación
  5. Reemplazar todas las importaciones using Google.Cloud.Vision.V1 con using IronOcr
  6. Reemplazar todas las importaciones using Google.Cloud.Storage.V1 y using Grpc.Core
  7. Reemplazar ImageAnnotatorClient.Create() con new IronTesseract()
  8. Eliminar todos los métodos del pipeline GCS (StorageClient, UploadObjectAsync, anotación async, sondeo, descarga, eliminación)
  9. Reemplazar input.LoadPdf() para todos los caminos de procesamiento de PDF (eliminando la orquestación async de GCS)
  10. Reemplazar todos los bucles de concatenación de símbolos de Protobuf con acceso directo de propiedad .Text en objetos OcrResult
  11. Reemplazar cálculos de índice BoundingPoly.Vertices con word.X, word.Y, word.Width, word.Height
  12. Remover todos los bloques de captura RpcException para ResourceExhausted, PermissionDenied, Unavailable, DeadlineExceeded, y Unauthenticated
  13. Reemplazar bucle TIFF por marco con input.LoadImageFrames()
  14. Convertir bucles de lotes secuenciales a Parallel.ForEach con instancias IronTesseract por hilo
  15. Remover GOOGLE_APPLICATION_CREDENTIALS de todas las configuraciones de entorno, pipelines CI/CD, archivos de Docker Compose, y secretos de Kubernetes

Posmigración

  • Verificar que no queden tipos RpcException o GoogleApiException referenciados en el código de manejo de errores
  • Confirmar que GOOGLE_APPLICATION_CREDENTIALS no está presente en todas las configuraciones de entorno de implementación
  • Ejecute el proceso de OCR en el mismo conjunto de documentos de muestra utilizados en producción y compare la calidad del texto resultante.
  • Pruebe el procesamiento de PDF en documentos previamente procesados ​​por la canalización asíncrona de GCS y confirme que la salida de texto es idéntica.
  • Probar archivos PDF protegidos por contraseña con input.LoadPdf(path, Password: "...") — anteriormente no soportados
  • Probar procesamiento de TIFF de múltiples páginas usando input.LoadImageFrames() y verificar que todos los marcos son procesados
  • Ejecute el procesador por lotes en una muestra representativa y confirme que la calidad de la salida coincide con los resultados anteriores.
  • Confirmar que los valores de result.Confidence están dentro del rango aceptable para su corpus de documentos
  • Verificar salida de PDF buscable utilizando result.SaveAsSearchablePdf() para documentos que anteriormente requerían una biblioteca PDF separada
  • Ejecute la aplicación en un entorno sin conexión a internet saliente y confirme que el OCR funciona correctamente.

Principales ventajas de migrar a IronOCR

Superficie de credenciales reducida a cero archivos. Después de la migración, no hay archivos de clave JSON, ni configuraciones de bucket de GCS, ni roles de IAM, ni cuentas de servicio, ni variables de entorno GOOGLE_APPLICATION_CREDENTIALS en su infraestructura. Toda la información de credenciales se reduce a una variable de entorno que contiene una cadena de clave de licencia. La rotación de claves, que era una operación periódica obligatoria con Google Cloud Vision, ya no es un concepto aplicable. Para los equipos que operan en varias regiones o proveedores de nube, la reducción en la complejidad de la configuración de la implementación es inmediata.

Procesamiento de PDF y TIFF sin dependencias externas. Se eliminan por completo la canalización asíncrona de GCS y el bucle de fotogramas TIFF de System.Drawing. input.LoadPdf() y input.LoadImageFrames() son los reemplazos — ambos sincrónicos, ambos locales, ambos tres líneas desde la llamada hasta el resultado. Los archivos PDF protegidos con contraseña, algo imposible con Google Cloud Vision, funcionan con un único parámetro adicional. La guía de OCR para PDF y la guía de entrada TIFF cubren la API de entrada completa.

Procesamiento por lotes a velocidad de CPU. Al eliminar la cuota de 1800 solicitudes por minuto y los tiempos de espera obligatorios de 60 segundos para reintentos, los trabajos por lotes que antes estaban limitados por la velocidad ahora se ejecutan a la velocidad de los núcleos de procesador disponibles. Una máquina con 16 núcleos procesa 16 documentos simultáneamente sin necesidad de aprobación externa. El patrón Parallel.ForEach con instancias IronTesseract por hilo es el reemplazo directo para el bucle secuencial restringido. La guía de optimización de velocidad cubre opciones de configuración del motor que ajustan el rendimiento para tipos específicos de documentos.

Datos estructurados sin Protobuf. Cada OcrResult expone Text, Confidence, Pages, Paragraphs, Lines, Words, y Characters como propiedades tipadas de .NET sin dependencia del namespace de Protobuf, sin concatenación de símbolos, ni aritmética de vértices para cuadros delimitadores. El código que previamente requería bucles anidados de 20 líneas para extraer texto de párrafos se reduce a result.Paragraphs.Select(p => p.Text). Para casos de uso que necesitan posicionamiento a nivel de palabra para análisis de diseño de documentos, word.X, word.Y, word.Width, y word.Height están disponibles directamente. Los resultados del OCR incluyen documentos de página para cada propiedad del modelo de resultados.

Salida de PDF con capacidad de búsqueda integrada. Visión de Google Cloudsolo devuelve texto; para generar un PDF con capacidad de búsqueda se requería una biblioteca de generación de PDF independiente, lo que añadía otra dependencia de NuGet , otra API que aprender y una licencia adicional que evaluar. La result.SaveAsSearchablePdf(outputPath) de IronOCR produce un PDF completamente buscable a partir de cualquier resultado de OCR en una línea. Para los flujos de trabajo de archivo de documentos y los pipelines de descubrimiento legal, esto elimina una dependencia completa. El ejemplo de PDF con función de búsqueda demuestra el patrón de principio a fin.

Soberanía de datos para industrias reguladas. Los documentos procesados ​​por IronOCR nunca salen del servidor. En el caso de los registros sanitarios cubiertos por la HIPAA, los datos técnicos controlados por la ITAR, los materiales de contratistas de defensa sujetos al ámbito de aplicación de la CMMC, los documentos legales con privilegio de confidencialidad entre abogado y cliente, y los registros financieros sujetos al ámbito de aplicación de la PCI-DSS, la arquitectura local elimina por completo la categoría de procesador de datos de terceros del ámbito de cumplimiento. No hay ningún acuerdo de asociación comercial que negociar, ningún acuerdo de protección de datos que firmar, ni ninguna política de retención de datos de Google que revisar. El centro de documentación de IronOCR abarca las configuraciones de implementación para entornos Docker, Linux, Azure y AWS donde se aplican requisitos de residencia de datos.

Por favor notaGoogle Cloud Vision, Tesseract, y iText son marcas registradas de sus respectivos propietarios. Este sitio no está afiliado, respaldado o patrocinado por Google o iText Group. Todos los nombres de productos, logotipos y marcas son propiedad de sus respectivos dueños. Las comparaciones son solo para fines informativos y reflejan información públicamente disponible en el momento de la redacción.

Preguntas Frecuentes

¿Por qué debería migrar de Google Cloud Vision API a IronOCR?

Los impulsores comunes incluyen la eliminación de la complejidad de la interoperabilidad COM, la sustitución de la gestión de licencias basada en archivos, la evitación de la facturación por página, la habilitación de la implementación de Docker/contenedores y la adopción de un flujo de trabajo nativo de NuGet que se integre con las herramientas .NET estándar.

¿Cuáles son los principales cambios en el código al migrar de Google Cloud Vision API a IronOCR?

Sustituya las secuencias de inicialización de Google Cloud Vision por la instanciación de IronTesseract, elimine la gestión del ciclo de vida COM (patrones explícitos Create/Load/Close) y actualice los nombres de las propiedades de los resultados. El resultado es un número significativamente menor de líneas repetitivas.

¿Cómo instalo IronOCR para comenzar la migración?

Ejecute 'Install-Package IronOcr' en la consola del gestor de paquetes o 'dotnet add package IronOcr' en la CLI. Los paquetes de idiomas son paquetes independientes: 'dotnet add package IronOcr.Languages.French' para el francés, por ejemplo.

¿IronOCR ofrece la misma precisión de reconocimiento óptico de caracteres que Google Cloud Vision API para documentos comerciales estándar?

IronOCR consigue una gran precisión para contenido empresarial estándar, como facturas, contratos, recibos y formularios mecanografiados. Los filtros de preprocesamiento de imágenes (eliminación de distorsiones, eliminación de ruido, mejora del contraste) mejoran aún más el reconocimiento en entradas degradadas.

¿Cómo gestiona IronOCR los datos lingüísticos que la API Google Cloud Vision instala por separado?

Los datos de idiomas en IronOCR se distribuyen como paquetes NuGet. dotnet add package IronOcr.Languages.German' instala la compatibilidad con el alemán. No es necesario colocar manualmente los archivos ni las rutas de los directorios.

¿Requiere la migración de Google Cloud Vision API a IronOCR cambios en la infraestructura de implementación?

IronOCR requiere menos cambios de infraestructura que Google Cloud Vision API. No hay rutas binarias del SDK, ubicaciones de archivos de licencia ni configuraciones del servidor de licencias. El paquete NuGet contiene el motor OCR completo, y la clave de licencia es una cadena establecida en el código de la aplicación.

¿Cómo configuro las licencias de IronOCR después de la migración?

Asigne IronOcr.License.LicenseKey = "YOUR-KEY" en el código de inicio de la aplicación. En Docker o Kubernetes, almacene la clave como una variable de entorno y léala en el inicio. Utilice License.IsValidLicense para validar antes de aceptar tráfico.

¿Puede IronOCR procesar archivos PDF del mismo modo que Google Cloud Vision?

Sí, IronOCR lee PDF nativos y escaneados. Instancie IronTesseract, llame a ocr.Read(input) donde input es una ruta PDF u OcrPdfInput, e itere las páginas OcrResult. No es necesario un proceso de renderizado de PDF independiente.

¿Cómo gestiona IronOCR los hilos en el procesamiento de grandes volúmenes?

IronTesseract puede instanciarse de forma segura por subproceso. Gire una instancia por hilo en un Parallel.ForEach o Task pool, ejecute OCR concurrentemente, y disponga de cada instancia cuando termine. No se requiere estado global o bloqueo.

¿Qué formatos de salida admite IronOCR tras la extracción de texto?

IronOCR devuelve resultados estructurados que incluyen texto, coordenadas de palabras, puntuaciones de confianza y estructura de páginas. Las opciones de exportación incluyen texto sin formato, PDF con opción de búsqueda y objetos de resultados estructurados para su procesamiento posterior.

¿Es el precio de IronOCR más predecible que el de Google Cloud Vision API para escalar cargas de trabajo?

IronOCR utiliza licencias perpetuas de tarifa plana sin cargos por página o volumen. Tanto si procesa 10.000 como 10 millones de páginas, el coste de la licencia permanece constante. Las opciones de licencias por volumen y por equipo se encuentran en la página de precios de IronOCR.

¿Qué ocurre con mis pruebas existentes después de migrar de Google Cloud Vision API a IronOCR?

Las pruebas que validan el contenido de texto extraído deben seguir superándose tras la migración. Las pruebas que validan patrones de llamada a API o el ciclo de vida de objetos COM deberán actualizarse para reflejar el modelo de inicialización y resultados más sencillo de IronOCR.

Kannaopat Udonpant
Ingeniero de Software
Antes de convertirse en Ingeniero de Software, Kannapat completó un doctorado en Recursos Ambientales de la Universidad de Hokkaido en Japón. Mientras perseguía su grado, Kannapat también se convirtió en miembro del Laboratorio de Robótica de Vehículos, que es parte del Departamento de Ingeniería ...
Leer más

Equipo de soporte de Iron

Estamos disponibles online las 24 horas, 5 días a la semana.
Chat
Email
Llámame