Saltar al pie de página
VIDEOS

Cómo realizar OCR en matrículas en C#

Esta guía está dirigida a desarrolladores .NET que estén migrando cargas de trabajo de OCR del SDK Kofax OmniPageCapture (ahora comercializado como Tungsten Automation) a IronOCR . Abarca la ruta de migración completa: eliminar la dependencia del instalador del SDK, eliminar el protocolo del ciclo de vida del motor, reemplazar los patrones de reconocimiento basados ​​en zonas y modernizar el manejo de errores. No es necesario haber leído previamente el artículo comparativo.

¿Por qué migrar desde Kofax OmniPage(Tungsten)?

El SDK OmniPage Capture fue diseñado para la infraestructura de gestión de documentos Enterprise en una época anterior a NuGet, Docker y las canalizaciones de CI/CD. Cada una de sus decisiones arquitectónicas que tenían sentido en 2005 genera fricciones en 2026.

El instalador del SDK no tiene cabida en el entorno NuGet . Cada máquina de desarrollo, agente de compilación y servidor de producción que ejecuta código OmniPage requiere que el instalador del SDK de Tungsten se ejecute antes de la compilación. No hay referencia de paquete .csproj para restaurar. Actualizar la versión del SDK implica volver a ejecutar el instalador en toda la flota. Un nuevo desarrollador no puede ejecutar el proyecto sin ponerse en contacto con la persona que gestiona la licencia del SDK y esperar a obtener acceso al instalador.

El archivo de licencia es una responsabilidad operativa. Un archivo .lic debe estar presente en una ruta específica en cada máquina que llame a engine.Initialize(). Implementa el sistema en un nuevo servidor y olvida el archivo, y cada llamada de OCR fallará con una excepción de licencia, no con un error de OCR. Usar licencias flotantes y una partición de red entre su servidor de aplicaciones y el servidor de licencias significa que cada llamada Initialize() falla hasta que se restablezca la conectividad, independientemente de si esa máquina validó con éxito su licencia ayer.

La gestión del ciclo de vida del motor pierde recursos en rutas de error. Se debe llamar a OmniPageEngine.Shutdown() en cada posible ruta de código, incluidos los manejadores de excepciones, retornos anticipados y timeouts, o un asiento de licencia flotante permanece bloqueado hasta que el tiempo de espera del servidor de licencias expire. Escribir de manera defensiva en torno a esta restricción significa envolver cada punto de integración en patrones IDisposable que existen exclusivamente para proteger contra la omisión del apagado del motor.

La identificación del hardware entra en conflicto con las implementaciones modernas. La activación de OmniPage está vinculada al hardware. Los reinicios de contenedores, las migraciones de máquinas virtuales y el escalado automático en la nube implican cambios en la identidad del hardware que desencadenan requisitos de reactivación. Un modelo de despliegue incompatible con el autoescalado es un modelo de despliegue incompatible con la infraestructura en la nube.

El precio por página es impredecible a gran escala. Un pico en el procesamiento de documentos —un nuevo cliente incorporado, un lote de trabajo pendiente enviado— genera una factura por exceso de consumo que no se ha presupuestado.IronOCR es perpetuo e ilimitado dentro del nivel de licencia: sin contador por página, sin cuota, sin facturas por exceso de uso.

El proceso de adquisición bloquea el envío. El SDK de OmniPage está restringido a ventas. El acceso a la evaluación, los precios y las condiciones del contrato requieren un proceso de venta que dura entre 4 y 12 semanas. Los equipos que desarrollan funciones de OCR con un plazo de entrega ajustado no pueden esperar al ciclo de adquisición de la Enterprise . dotnet add package IronOcr se resuelve en 30 segundos. Consulte la página de licencias de IronOCR para precios publicados de autoservicio — $999 Lite hasta $2,999 Enterprise, todo perpetuo.

El problema fundamental

OmniPage requiere una ceremonia de inicialización completa antes de que se lea el primer byte, y una ceremonia de apagado después del último; cada sitio de llamada debe gestionar ambas:

// OmniPage: Ceremony required on EVERY entry point — init, process, shutdown
using var engine = new OmniPageEngine();
engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic"); // File must exist here
engine.Initialize();   // Network call to license server — can fail for license reasons, not OCR reasons

var document = engine.CreateDocument();
document.AddPage("invoice.jpg");
document.Recognize(new RecognitionSettings { Language = "English" });
string text = document.GetText();
document.Dispose();    // Must not be skipped
engine.Shutdown();     // Must not be skipped — omitting this locks a floating seat
// OmniPage: Ceremony required on EVERY entry point — init, process, shutdown
using var engine = new OmniPageEngine();
engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic"); // File must exist here
engine.Initialize();   // Network call to license server — can fail for license reasons, not OCR reasons

var document = engine.CreateDocument();
document.AddPage("invoice.jpg");
document.Recognize(new RecognitionSettings { Language = "English" });
string text = document.GetText();
document.Dispose();    // Must not be skipped
engine.Shutdown();     // Must not be skipped — omitting this locks a floating seat
Imports OmniPage

' OmniPage: Ceremony required on EVERY entry point — init, process, shutdown
Using engine As New OmniPageEngine()
    engine.SetLicenseFile("C:\Program Files\OmniPage\license.lic") ' File must exist here
    engine.Initialize()   ' Network call to license server — can fail for license reasons, not OCR reasons

    Dim document = engine.CreateDocument()
    document.AddPage("invoice.jpg")
    document.Recognize(New RecognitionSettings With {.Language = "English"})
    Dim text As String = document.GetText()
    document.Dispose()    ' Must not be skipped
    engine.Shutdown()     ' Must not be skipped — omitting this locks a floating seat
End Using
$vbLabelText   $csharpLabel
// IronOCR: One NuGet package, one line at startup, one call to read
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; // At app startup — once, ever
var text = new IronTesseract().Read("invoice.jpg").Text;
// IronOCR: One NuGet package, one line at startup, one call to read
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; // At app startup — once, ever
var text = new IronTesseract().Read("invoice.jpg").Text;
Imports IronOcr

' IronOCR: One NuGet package, one line at startup, one call to read
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY" ' At app startup — once, ever
Dim text = New IronTesseract().Read("invoice.jpg").Text
$vbLabelText   $csharpLabel

La versión OmniPage tiene ocho pasos distintos, dos llamadas de limpieza obligatorias, una dependencia de red y una dependencia del sistema de archivos. La versión IronOCR tiene dos.

IronOCR vs Kofax OmniPage(Tungsteno): Comparación de características

La tabla que aparece a continuación resume las funcionalidades más relevantes para los equipos que evalúan esta migración.

Característica SDK de Kofax OmniPage IronOCR
Método de instalación Instalador de SDK personalizado (sin NuGet) dotnet add package IronOcr
Hora de la primera llamada de OCR 4–12 semanas (adquisición) + horas (configuración) 30 segundos
Mecanismo de licencia archivo .lic en disco + servidor de licencias opcional Clave de cadena al iniciar la aplicación
Identificación de hardware Sí (reactivación en la migración de la máquina virtual) No
Se requiere un servidor de licencias Sí (para licencias flotantes) No
Facturación por tiempo de ejecución por página Disponible (variable) No
Precios publicados No (contactar con ventas) Sí ($999 / $1,499 / $2,999 perpetuo)
Cuotas de mantenimiento anuales Entre el 18% y el 25% del costo de la licencia. Opcional
Gestión del ciclo de vida del motor Requerido (Initialize / Shutdown) No es necesario
Windows x64
Linux Sí (añadido en enero de 2026) Sí (todas las versiones)
macOS No
Docker / Kubernetes Difícil (instalador + archivo de licencia en la imagen) Sencillo (solo paquete NuGet )
Azure / AWS Lambda Complejo (accesibilidad del servidor de licencias) Sencillo
Formatos de entrada de imagen JPG, PNG, BMP, TIFF, GIF y más
Entrada de PDF nativa Sí (puede requerir licencia de módulo) Sí (integrado, sin licencia adicional)
Archivo TIFF de varias páginas
Salida en PDF con capacidad de búsqueda Sí (result.SaveAsSearchablePdf())
preprocesamiento automático Configuración del objeto de configuración API de canalización integrada y explícita
Idiomas compatibles Más de 120 Más de 125 (paquetes NuGet separados)
Salida de datos estructurados Sí (coordenadas de la palabra) Páginas, párrafos, líneas, palabras, caracteres con coordenadas
Puntuación de confianza Sí (result.Confidence, por palabra)
Lectura de BarCodes Módulo adicional (licencia aparte) Integrado (ocr.Configuration.ReadBarCodes = true)
Seguridad de los hilos Complejo (restricciones de compartición de motor) Completo (uno IronTesseract por hilo)
Soporte para canalizaciones de CI/CD Requiere instalador en cada agente. dotnet restore estándar

Inicio rápido: Migración de Kofax OmniPagea IronOCR

Paso 1: Reemplace el SDK con un paquete NuGet.

OmniPage no tiene ningún paquete NuGet para eliminar. Elimine las referencias DLL manuales del archivo .csproj e instale el SDK de las máquinas de desarrollo cuando se complete la migración. Luego instala IronOCR:

dotnet add package IronOcr

El paquete NuGet IronOCR incluye el motor OCR, los filtros de preprocesamiento y los componentes de tiempo de ejecución. Sin instalador independiente, sin gestión nativa de DLL, sin registro de componentes.

Paso 2: Actualizar los espacios de nombres

// Before (Kofax OmniPage)
using Kofax.OmniPage.CSDK;
using Kofax.OmniPageCSDK;
using CSDK;

// After (IronOCR)
using IronOcr;
// Before (Kofax OmniPage)
using Kofax.OmniPage.CSDK;
using Kofax.OmniPageCSDK;
using CSDK;

// After (IronOCR)
using IronOcr;
Imports IronOcr
$vbLabelText   $csharpLabel

Paso 3: Inicializar licencia

Agregue una línea al inicio de la aplicación. Esto reemplaza la ruta de archivo .lic, la configuración del servidor de licencias y la llamada a engine.Initialize():

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

Almacene la clave en una variable de entorno (IRONOCR_LICENSE_KEY) o appsettings.json en lugar de en el código fuente. La clave se lee sin conexión; no se produce ninguna llamada de red en tiempo de ejecución.

Ejemplos de migración de código

Reemplazo de ruta de error de inicialización del motor

El aspecto más peligroso del modelo de ciclo de vida de OmniPage no es el camino hacia el éxito, sino el camino hacia el error. Cualquier excepción lanzada entre engine.Initialize() y engine.Shutdown() puede dejar un asiento de licencia flotante bloqueado si no se alcanza Shutdown(). El código de producción requiere bloques try/finally alrededor de cada llamada de procesamiento de documentos:

Enfoque OmniPage de Kofax:

// OmniPage: try/finally required everywhere to protect license seat release
public string ProcessInvoice(string imagePath)
{
    var engine = new OmniPageEngine();
    engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic");

    try
    {
        engine.Initialize(); // Contacts license server — failure here locks nothing
        var document = engine.CreateDocument();

        try
        {
            document.AddPage(imagePath);
            document.Recognize(new RecognitionSettings { Language = "English" });
            return document.GetText();
        }
        catch (RecognitionException ex)
        {
            // Log, rethrow — but Shutdown must still be called
            throw new OcrProcessingException("Recognition failed", ex);
        }
        finally
        {
            document.Dispose(); // Inner finally: release document
        }
    }
    catch (LicenseValidationException ex)
    {
        throw new OcrProcessingException("License validation failed", ex);
    }
    finally
    {
        engine.Shutdown(); // Outer finally: release license seat — MUST execute
    }
}
// OmniPage: try/finally required everywhere to protect license seat release
public string ProcessInvoice(string imagePath)
{
    var engine = new OmniPageEngine();
    engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic");

    try
    {
        engine.Initialize(); // Contacts license server — failure here locks nothing
        var document = engine.CreateDocument();

        try
        {
            document.AddPage(imagePath);
            document.Recognize(new RecognitionSettings { Language = "English" });
            return document.GetText();
        }
        catch (RecognitionException ex)
        {
            // Log, rethrow — but Shutdown must still be called
            throw new OcrProcessingException("Recognition failed", ex);
        }
        finally
        {
            document.Dispose(); // Inner finally: release document
        }
    }
    catch (LicenseValidationException ex)
    {
        throw new OcrProcessingException("License validation failed", ex);
    }
    finally
    {
        engine.Shutdown(); // Outer finally: release license seat — MUST execute
    }
}
Imports System

' OmniPage: try/finally required everywhere to protect license seat release
Public Function ProcessInvoice(imagePath As String) As String
    Dim engine As New OmniPageEngine()
    engine.SetLicenseFile("C:\Program Files\OmniPage\license.lic")

    Try
        engine.Initialize() ' Contacts license server — failure here locks nothing
        Dim document = engine.CreateDocument()

        Try
            document.AddPage(imagePath)
            document.Recognize(New RecognitionSettings With {.Language = "English"})
            Return document.GetText()
        Catch ex As RecognitionException
            ' Log, rethrow — but Shutdown must still be called
            Throw New OcrProcessingException("Recognition failed", ex)
        Finally
            document.Dispose() ' Inner finally: release document
        End Try
    Catch ex As LicenseValidationException
        Throw New OcrProcessingException("License validation failed", ex)
    Finally
        engine.Shutdown() ' Outer finally: release license seat — MUST execute
    End Try
End Function
$vbLabelText   $csharpLabel

Enfoque IronOCR:

// IronOCR: No license seat to release, no engine to shut down
public string ProcessInvoice(string imagePath)
{
    using var input = new OcrInput();
    input.LoadImage(imagePath);

    var ocr = new IronTesseract();
    var result = ocr.Read(input);
    return result.Text;
    // OcrInput disposal is automatic — no license implications on any code path
}
// IronOCR: No license seat to release, no engine to shut down
public string ProcessInvoice(string imagePath)
{
    using var input = new OcrInput();
    input.LoadImage(imagePath);

    var ocr = new IronTesseract();
    var result = ocr.Read(input);
    return result.Text;
    // OcrInput disposal is automatic — no license implications on any code path
}
Imports IronOcr

Public Function ProcessInvoice(imagePath As String) As String
    Using input As New OcrInput()
        input.LoadImage(imagePath)

        Dim ocr As New IronTesseract()
        Dim result = ocr.Read(input)
        Return result.Text
    End Using
    ' OcrInput disposal is automatic — no license implications on any code path
End Function
$vbLabelText   $csharpLabel

El bloque using en OcrInput maneja la limpieza de memoria para los datos de imagen cargados. No existe try/finally para proteger un asiento de licencia. Una excepción en cualquier parte de este método no tiene efectos secundarios fuera del ámbito del propio método. Consulte la guía de configuración de IronTesseract para obtener información sobre las opciones de configuración, incluidas las instancias locales de subprocesos.

Migración de reconocimiento basada en zonas

El principal mecanismo de extracción estructurada de OmniPage es la definición de zonas: las regiones del documento se declaran con límites de coordenadas y tipo de reconocimiento por zona (OCR, ICR, OMR, código de barras). Los desarrolladores definen objetos FormZone por plantilla de documento y los pasan al motor de reconocimiento.IronOCR utiliza CropRectangle para lograr la misma extracción dirigida sin gestión de plantillas o declaraciones de tipo de zona:

Enfoque OmniPage de Kofax:

// OmniPage: Zone-based form extraction with template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    // Define zones per document template
    var zones = new List<FormZone>
    {
        new FormZone { Name = "InvoiceNumber", Type = "OCR",
            X = 520, Y = 80, Width = 200, Height = 30 },
        new FormZone { Name = "InvoiceDate",   Type = "OCR",
            X = 520, Y = 120, Width = 200, Height = 30 },
        new FormZone { Name = "TotalAmount",   Type = "OCR",
            X = 520, Y = 580, Width = 200, Height = 30 },
        new FormZone { Name = "VendorName",    Type = "OCR",
            X = 50,  Y = 80,  Width = 300, Height = 40 }
    };

    var template = new FormTemplate { Name = "StandardInvoice", Zones = zones };
    var settings = new RecognitionSettings { Language = "English" };

    var document = engine.CreateDocument();
    document.AddPage(invoicePath);
    document.ApplyTemplate(template);
    document.Recognize(settings);

    var fields = new Dictionary<string, string>();
    foreach (var zone in zones)
        fields[zone.Name] = document.GetZoneText(zone.Name);

    document.Dispose();
    engine.Shutdown();
    return fields;
}
// OmniPage: Zone-based form extraction with template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    // Define zones per document template
    var zones = new List<FormZone>
    {
        new FormZone { Name = "InvoiceNumber", Type = "OCR",
            X = 520, Y = 80, Width = 200, Height = 30 },
        new FormZone { Name = "InvoiceDate",   Type = "OCR",
            X = 520, Y = 120, Width = 200, Height = 30 },
        new FormZone { Name = "TotalAmount",   Type = "OCR",
            X = 520, Y = 580, Width = 200, Height = 30 },
        new FormZone { Name = "VendorName",    Type = "OCR",
            X = 50,  Y = 80,  Width = 300, Height = 40 }
    };

    var template = new FormTemplate { Name = "StandardInvoice", Zones = zones };
    var settings = new RecognitionSettings { Language = "English" };

    var document = engine.CreateDocument();
    document.AddPage(invoicePath);
    document.ApplyTemplate(template);
    document.Recognize(settings);

    var fields = new Dictionary<string, string>();
    foreach (var zone in zones)
        fields[zone.Name] = document.GetZoneText(zone.Name);

    document.Dispose();
    engine.Shutdown();
    return fields;
}
Imports System
Imports System.Collections.Generic

' OmniPage: Zone-based form extraction with template management
Public Function ExtractInvoiceFields(invoicePath As String) As Dictionary(Of String, String)
    Using engine As New OmniPageEngine()
        engine.SetLicenseFile(licensePath)
        engine.Initialize()

        ' Define zones per document template
        Dim zones As New List(Of FormZone) From {
            New FormZone With {.Name = "InvoiceNumber", .Type = "OCR", .X = 520, .Y = 80, .Width = 200, .Height = 30},
            New FormZone With {.Name = "InvoiceDate", .Type = "OCR", .X = 520, .Y = 120, .Width = 200, .Height = 30},
            New FormZone With {.Name = "TotalAmount", .Type = "OCR", .X = 520, .Y = 580, .Width = 200, .Height = 30},
            New FormZone With {.Name = "VendorName", .Type = "OCR", .X = 50, .Y = 80, .Width = 300, .Height = 40}
        }

        Dim template As New FormTemplate With {.Name = "StandardInvoice", .Zones = zones}
        Dim settings As New RecognitionSettings With {.Language = "English"}

        Dim document = engine.CreateDocument()
        document.AddPage(invoicePath)
        document.ApplyTemplate(template)
        document.Recognize(settings)

        Dim fields As New Dictionary(Of String, String)()
        For Each zone In zones
            fields(zone.Name) = document.GetZoneText(zone.Name)
        Next

        document.Dispose()
        engine.Shutdown()
        Return fields
    End Using
End Function
$vbLabelText   $csharpLabel

Enfoque IronOCR:

// IronOCR: CropRectangle targets specific regions — no template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
    var fields = new Dictionary<string, string>();
    var ocr = new IronTesseract();

    // Extract each field by reading only the target region
    var fieldRegions = new Dictionary<string, CropRectangle>
    {
        ["InvoiceNumber"] = new CropRectangle(520, 80,  200, 30),
        ["InvoiceDate"]   = new CropRectangle(520, 120, 200, 30),
        ["TotalAmount"]   = new CropRectangle(520, 580, 200, 30),
        ["VendorName"]    = new CropRectangle(50,  80,  300, 40)
    };

    foreach (var (fieldName, region) in fieldRegions)
    {
        using var input = new OcrInput();
        input.LoadImage(invoicePath, region);
        fields[fieldName] = ocr.Read(input).Text.Trim();
    }

    return fields;
}
// IronOCR: CropRectangle targets specific regions — no template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
    var fields = new Dictionary<string, string>();
    var ocr = new IronTesseract();

    // Extract each field by reading only the target region
    var fieldRegions = new Dictionary<string, CropRectangle>
    {
        ["InvoiceNumber"] = new CropRectangle(520, 80,  200, 30),
        ["InvoiceDate"]   = new CropRectangle(520, 120, 200, 30),
        ["TotalAmount"]   = new CropRectangle(520, 580, 200, 30),
        ["VendorName"]    = new CropRectangle(50,  80,  300, 40)
    };

    foreach (var (fieldName, region) in fieldRegions)
    {
        using var input = new OcrInput();
        input.LoadImage(invoicePath, region);
        fields[fieldName] = ocr.Read(input).Text.Trim();
    }

    return fields;
}
Imports System.Collections.Generic

' IronOCR: CropRectangle targets specific regions — no template management
Public Function ExtractInvoiceFields(invoicePath As String) As Dictionary(Of String, String)
    Dim fields As New Dictionary(Of String, String)()
    Dim ocr As New IronTesseract()

    ' Extract each field by reading only the target region
    Dim fieldRegions As New Dictionary(Of String, CropRectangle) From {
        {"InvoiceNumber", New CropRectangle(520, 80, 200, 30)},
        {"InvoiceDate", New CropRectangle(520, 120, 200, 30)},
        {"TotalAmount", New CropRectangle(520, 580, 200, 30)},
        {"VendorName", New CropRectangle(50, 80, 300, 40)}
    }

    For Each kvp In fieldRegions
        Dim fieldName = kvp.Key
        Dim region = kvp.Value
        Using input As New OcrInput()
            input.LoadImage(invoicePath, region)
            fields(fieldName) = ocr.Read(input).Text.Trim()
        End Using
    Next

    Return fields
End Function
$vbLabelText   $csharpLabel

Cada CropRectangle especifica (x, y, width, height) en píxeles. El motor OCR procesa únicamente la región seleccionada, no la página completa, lo que ofrece la misma ventaja de eficiencia que el procesamiento por zonas de OmniPage. La guía OCR basada en regiones cubre patrones de selección de coordenadas, incluido cómo extraer múltiples regiones de una sola carga de imagen usando la API de múltiples regiones de OcrInput.

Migración de salida de datos estructurados

OmniPage expone la estructura del documento a través de un sistema de exportación propio: se aplican los ajustes de formato a un documento reconocido y la salida se escribe en un archivo en un formato específico (RTF, XML, CSV, PDF con capacidad de búsqueda). Para acceder a las coordenadas a nivel de palabra, es necesario iterar un iterador de resultados con consultas de posición explícitas.IronOCR expone los mismos datos estructurados directamente en el objeto resultante sin un paso de exportación secundario:

Enfoque OmniPage de Kofax:

// OmniPage: Structured output requires format configuration and file export
public void ExtractStructuredContent(string imagePath, string outputDir)
{
    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    var settings = new RecognitionSettings { Language = "English" };
    var document = engine.CreateDocument();
    document.AddPage(imagePath);
    document.Recognize(settings);

    // Word-level coordinates through result iterator
    var iterator = document.GetResultIterator(ResultIteratorLevel.Word);
    while (iterator.MoveNext())
    {
        string word = iterator.GetText();
        var bounds = iterator.GetBoundingBox();
        double confidence = iterator.GetConfidence();
        Console.WriteLine($"Word: {word} at ({bounds.X},{bounds.Y}) conf:{confidence:F1}%");
    }

    // Structured export requires separate output format configuration
    var outputSettings = new OutputSettings
    {
        Format = OutputFormat.XML,
        IncludeCoordinates = true,
        IncludeConfidence = true
    };
    document.SaveAs(Path.Combine(outputDir, "output.xml"), outputSettings);

    document.Dispose();
    engine.Shutdown();
}
// OmniPage: Structured output requires format configuration and file export
public void ExtractStructuredContent(string imagePath, string outputDir)
{
    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    var settings = new RecognitionSettings { Language = "English" };
    var document = engine.CreateDocument();
    document.AddPage(imagePath);
    document.Recognize(settings);

    // Word-level coordinates through result iterator
    var iterator = document.GetResultIterator(ResultIteratorLevel.Word);
    while (iterator.MoveNext())
    {
        string word = iterator.GetText();
        var bounds = iterator.GetBoundingBox();
        double confidence = iterator.GetConfidence();
        Console.WriteLine($"Word: {word} at ({bounds.X},{bounds.Y}) conf:{confidence:F1}%");
    }

    // Structured export requires separate output format configuration
    var outputSettings = new OutputSettings
    {
        Format = OutputFormat.XML,
        IncludeCoordinates = true,
        IncludeConfidence = true
    };
    document.SaveAs(Path.Combine(outputDir, "output.xml"), outputSettings);

    document.Dispose();
    engine.Shutdown();
}
Imports System
Imports System.IO

Public Sub ExtractStructuredContent(imagePath As String, outputDir As String)
    Using engine As New OmniPageEngine()
        engine.SetLicenseFile(licensePath)
        engine.Initialize()

        Dim settings As New RecognitionSettings With {.Language = "English"}
        Dim document = engine.CreateDocument()
        document.AddPage(imagePath)
        document.Recognize(settings)

        ' Word-level coordinates through result iterator
        Dim iterator = document.GetResultIterator(ResultIteratorLevel.Word)
        While iterator.MoveNext()
            Dim word As String = iterator.GetText()
            Dim bounds = iterator.GetBoundingBox()
            Dim confidence As Double = iterator.GetConfidence()
            Console.WriteLine($"Word: {word} at ({bounds.X},{bounds.Y}) conf:{confidence:F1}%")
        End While

        ' Structured export requires separate output format configuration
        Dim outputSettings As New OutputSettings With {
            .Format = OutputFormat.XML,
            .IncludeCoordinates = True,
            .IncludeConfidence = True
        }
        document.SaveAs(Path.Combine(outputDir, "output.xml"), outputSettings)

        document.Dispose()
        engine.Shutdown()
    End Using
End Sub
$vbLabelText   $csharpLabel

Enfoque IronOCR:

// IronOCR: Structured data directly on OcrResult — no export step
public void ExtractStructuredContent(string imagePath)
{
    var result = new IronTesseract().Read(imagePath);

    Console.WriteLine($"Confidence: {result.Confidence:F1}%");
    Console.WriteLine($"Pages: {result.Pages.Length}");

    foreach (var page in result.Pages)
    {
        foreach (var paragraph in page.Paragraphs)
        {
            Console.WriteLine($"[Paragraph at ({paragraph.X},{paragraph.Y})]");
            Console.WriteLine(paragraph.Text);

            foreach (var word in paragraph.Words)
            {
                // Per-word confidence and coordinates — no iterator needed
                Console.WriteLine(
                    $"  Word: '{word.Text}' " +
                    $"at ({word.X},{word.Y}) " +
                    $"conf: {word.Confidence:F1}%");
            }
        }
    }
}
// IronOCR: Structured data directly on OcrResult — no export step
public void ExtractStructuredContent(string imagePath)
{
    var result = new IronTesseract().Read(imagePath);

    Console.WriteLine($"Confidence: {result.Confidence:F1}%");
    Console.WriteLine($"Pages: {result.Pages.Length}");

    foreach (var page in result.Pages)
    {
        foreach (var paragraph in page.Paragraphs)
        {
            Console.WriteLine($"[Paragraph at ({paragraph.X},{paragraph.Y})]");
            Console.WriteLine(paragraph.Text);

            foreach (var word in paragraph.Words)
            {
                // Per-word confidence and coordinates — no iterator needed
                Console.WriteLine(
                    $"  Word: '{word.Text}' " +
                    $"at ({word.X},{word.Y}) " +
                    $"conf: {word.Confidence:F1}%");
            }
        }
    }
}
Public Sub ExtractStructuredContent(imagePath As String)
    Dim result = New IronTesseract().Read(imagePath)

    Console.WriteLine($"Confidence: {result.Confidence:F1}%")
    Console.WriteLine($"Pages: {result.Pages.Length}")

    For Each page In result.Pages
        For Each paragraph In page.Paragraphs
            Console.WriteLine($"[Paragraph at ({paragraph.X},{paragraph.Y})]")
            Console.WriteLine(paragraph.Text)

            For Each word In paragraph.Words
                ' Per-word confidence and coordinates — no iterator needed
                Console.WriteLine(
                    $"  Word: '{word.Text}' " &
                    $"at ({word.X},{word.Y}) " &
                    $"conf: {word.Confidence:F1}%")
            Next
        Next
    Next
End Sub
$vbLabelText   $csharpLabel

La jerarquía del objeto OcrResult: Páginas, Párrafos, Líneas, Palabras, Caracteres, proporciona los mismos datos posicionales que el iterador de resultados de OmniPage expone, pero como un gráfico de objetos navegable en lugar de un cursor secuencial. Los índices de confianza están disponibles a nivel de resultado, página, párrafo y palabra sin necesidad de configuración adicional. La guía de resultados de lectura abarca todas las propiedades de los datos estructurados, y la guía de puntuaciones de confianza abarca los patrones de validación palabra por palabra.

Migración de procesamiento TIFF de varias páginas

El flujo de trabajo TIFF multipágina de OmniPage requiere la extracción página por página del contenedor TIFF, con una llamada de reconocimiento y una eliminación de documento independientes para cada página. Esto crea tanto código repetitivo como una superficie de gestión de recursos proporcional al número de páginas.IronOCR carga archivos TIFF multifotograma en una sola llamada:

Enfoque OmniPage de Kofax:

// OmniPage: Page-by-page TIFF extraction with per-page lifecycle
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
    var pageTexts = new List<string>();

    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    // Open TIFF and iterate frames
    var tiffContainer = engine.OpenTiff(tiffPath);
    int pageCount = tiffContainer.GetPageCount();

    var settings = new RecognitionSettings { Language = "English" };

    for (int i = 0; i < pageCount; i++)
    {
        var page = tiffContainer.GetPage(i); // Extract frame
        var document = engine.CreateDocument();

        try
        {
            document.AddPage(page);
            document.Recognize(settings);
            pageTexts.Add(document.GetText());
        }
        finally
        {
            document.Dispose(); // Dispose per page to manage memory
        }
    }

    tiffContainer.Dispose();
    engine.Shutdown();
    return pageTexts;
}
// OmniPage: Page-by-page TIFF extraction with per-page lifecycle
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
    var pageTexts = new List<string>();

    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    // Open TIFF and iterate frames
    var tiffContainer = engine.OpenTiff(tiffPath);
    int pageCount = tiffContainer.GetPageCount();

    var settings = new RecognitionSettings { Language = "English" };

    for (int i = 0; i < pageCount; i++)
    {
        var page = tiffContainer.GetPage(i); // Extract frame
        var document = engine.CreateDocument();

        try
        {
            document.AddPage(page);
            document.Recognize(settings);
            pageTexts.Add(document.GetText());
        }
        finally
        {
            document.Dispose(); // Dispose per page to manage memory
        }
    }

    tiffContainer.Dispose();
    engine.Shutdown();
    return pageTexts;
}
Imports System.Collections.Generic

' OmniPage: Page-by-page TIFF extraction with per-page lifecycle
Public Function ExtractFromMultiPageTiff(tiffPath As String) As List(Of String)
    Dim pageTexts As New List(Of String)()

    Using engine As New OmniPageEngine()
        engine.SetLicenseFile(licensePath)
        engine.Initialize()

        ' Open TIFF and iterate frames
        Dim tiffContainer = engine.OpenTiff(tiffPath)
        Dim pageCount As Integer = tiffContainer.GetPageCount()

        Dim settings As New RecognitionSettings With {.Language = "English"}

        For i As Integer = 0 To pageCount - 1
            Dim page = tiffContainer.GetPage(i) ' Extract frame
            Dim document = engine.CreateDocument()

            Try
                document.AddPage(page)
                document.Recognize(settings)
                pageTexts.Add(document.GetText())
            Finally
                document.Dispose() ' Dispose per page to manage memory
            End Try
        Next

        tiffContainer.Dispose()
        engine.Shutdown()
    End Using

    Return pageTexts
End Function
$vbLabelText   $csharpLabel

Enfoque IronOCR:

// IronOCR: Multi-frame TIFF loaded in one call — all pages in one result
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
    using var input = new OcrInput();
    input.LoadImageFrames(tiffPath); // All frames loaded automatically

    var result = new IronTesseract().Read(input);

    // Each TIFF frame becomes a page in the result
    return result.Pages.Select(page => page.Text).ToList();
}
// IronOCR: Multi-frame TIFF loaded in one call — all pages in one result
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
    using var input = new OcrInput();
    input.LoadImageFrames(tiffPath); // All frames loaded automatically

    var result = new IronTesseract().Read(input);

    // Each TIFF frame becomes a page in the result
    return result.Pages.Select(page => page.Text).ToList();
}
Imports System.Collections.Generic
Imports IronOcr

' IronOCR: Multi-frame TIFF loaded in one call — all pages in one result
Public Function ExtractFromMultiPageTiff(tiffPath As String) As List(Of String)
    Using input As New OcrInput()
        input.LoadImageFrames(tiffPath) ' All frames loaded automatically

        Dim result = New IronTesseract().Read(input)

        ' Each TIFF frame becomes a page in the result
        Return result.Pages.Select(Function(page) page.Text).ToList()
    End Using
End Function
$vbLabelText   $csharpLabel

LoadImageFrames lee cada cuadro del contenedor TIFF en una sola llamada. El resultado expone un OcrResult.Page por cuadro, conservando la estructura página por página sin requerir un bucle de iteración manual o limpieza por página. Para flujos de trabajo de producción batch TIFF, consulte la guía de entrada TIFF y GIF.

Migración de procesamiento paralelo seguro para subprocesos

El motor de OmniPage es un recurso compartido y con estado. Compartir una instancia de OmniPageEngine entre hilos requiere sincronización externa porque múltiples hilos llamando a CreateDocument() simultáneamente pueden producir un estado entrelazado. El patrón seguro — una instancia de motor por hilo — entra en conflicto con el alto costo de inicialización del motor. Las instancias de IronOCR no tienen estado compartido: cree una IronTesseract por hilo sin sobrecarga de coordinación:

Enfoque OmniPage de Kofax:

// OmniPage: Shared engine with locking to serialize document operations
public ConcurrentDictionary<string, string> ProcessBatchWithEngine(
    string[] imagePaths, string licensePath)
{
    var results = new ConcurrentDictionary<string, string>();
    var engineLock = new object();

    // One engine — document operations must be serialized
    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    var settings = new RecognitionSettings { Language = "English" };

    Parallel.ForEach(imagePaths, imagePath =>
    {
        lock (engineLock) // Serialize: one recognition at a time
        {
            var document = engine.CreateDocument();
            try
            {
                document.AddPage(imagePath);
                document.Recognize(settings);
                results[imagePath] = document.GetText();
            }
            finally
            {
                document.Dispose();
            }
        }
    });

    engine.Shutdown();
    return results;
}
// OmniPage: Shared engine with locking to serialize document operations
public ConcurrentDictionary<string, string> ProcessBatchWithEngine(
    string[] imagePaths, string licensePath)
{
    var results = new ConcurrentDictionary<string, string>();
    var engineLock = new object();

    // One engine — document operations must be serialized
    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    var settings = new RecognitionSettings { Language = "English" };

    Parallel.ForEach(imagePaths, imagePath =>
    {
        lock (engineLock) // Serialize: one recognition at a time
        {
            var document = engine.CreateDocument();
            try
            {
                document.AddPage(imagePath);
                document.Recognize(settings);
                results[imagePath] = document.GetText();
            }
            finally
            {
                document.Dispose();
            }
        }
    });

    engine.Shutdown();
    return results;
}
Imports System.Collections.Concurrent
Imports System.Threading.Tasks

' OmniPage: Shared engine with locking to serialize document operations
Public Function ProcessBatchWithEngine(imagePaths As String(), licensePath As String) As ConcurrentDictionary(Of String, String)
    Dim results As New ConcurrentDictionary(Of String, String)()
    Dim engineLock As New Object()

    ' One engine — document operations must be serialized
    Using engine As New OmniPageEngine()
        engine.SetLicenseFile(licensePath)
        engine.Initialize()

        Dim settings As New RecognitionSettings With {.Language = "English"}

        Parallel.ForEach(imagePaths, Sub(imagePath)
                                        SyncLock engineLock ' Serialize: one recognition at a time
                                            Dim document = engine.CreateDocument()
                                            Try
                                                document.AddPage(imagePath)
                                                document.Recognize(settings)
                                                results(imagePath) = document.GetText()
                                            Finally
                                                document.Dispose()
                                            End Try
                                        End SyncLock
                                    End Sub)
        engine.Shutdown()
    End Using

    Return results
End Function
$vbLabelText   $csharpLabel

Enfoque IronOCR:

// IronOCR: One IronTesseract per thread — no locking, genuine parallelism
public ConcurrentDictionary<string, string> ProcessBatchInParallel(string[] imagePaths)
{
    var results = new ConcurrentDictionary<string, string>();

    Parallel.ForEach(imagePaths, imagePath =>
    {
        // Each thread creates its own instance — no shared state, no locks
        var ocr = new IronTesseract();
        using var input = new OcrInput();
        input.LoadImage(imagePath);

        var result = ocr.Read(input);
        results[imagePath] = result.Text;
    });

    return results;
}
// IronOCR: One IronTesseract per thread — no locking, genuine parallelism
public ConcurrentDictionary<string, string> ProcessBatchInParallel(string[] imagePaths)
{
    var results = new ConcurrentDictionary<string, string>();

    Parallel.ForEach(imagePaths, imagePath =>
    {
        // Each thread creates its own instance — no shared state, no locks
        var ocr = new IronTesseract();
        using var input = new OcrInput();
        input.LoadImage(imagePath);

        var result = ocr.Read(input);
        results[imagePath] = result.Text;
    });

    return results;
}
Imports System.Collections.Concurrent
Imports System.Threading.Tasks
Imports IronOcr

' IronOCR: One IronTesseract per thread — no locking, genuine parallelism
Public Function ProcessBatchInParallel(imagePaths As String()) As ConcurrentDictionary(Of String, String)
    Dim results As New ConcurrentDictionary(Of String, String)()

    Parallel.ForEach(imagePaths, Sub(imagePath)
        ' Each thread creates its own instance — no shared state, no locks
        Dim ocr As New IronTesseract()
        Using input As New OcrInput()
            input.LoadImage(imagePath)

            Dim result = ocr.Read(input)
            results(imagePath) = result.Text
        End Using
    End Sub)

    Return results
End Function
$vbLabelText   $csharpLabel

La versión OmniPage serializa todo el reconocimiento a través de un bloqueo, lo que anula el paralelismo. La versión IronOCR procesa los documentos simultáneamente sin ninguna coordinación. Para cargas de trabajo por lotes de alto rendimiento, consulte el ejemplo de multihilo y la guía de optimización de velocidad .

Generación de PDF con capacidad de búsqueda a partir de archivos escaneados

La salida de PDF searchable de OmniPage requiere ensamblar un pipeline de reconocimiento con configuración explícita de OutputSettings y OutputFormat antes de guardar.IronOCR genera archivos PDF con capacidad de búsqueda a partir de una única llamada a un método en el objeto resultante:

Enfoque OmniPage de Kofax:

// OmniPage: Searchable PDF requires OutputSettings configuration before save
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    var recognitionSettings = new RecognitionSettings
    {
        Language = "English",
        AccuracyMode = "Maximum",
        PreserveLayout = true
    };

    var outputSettings = new OutputSettings
    {
        Format = OutputFormat.SearchablePDF,
        Compression = PDFCompression.Standard,
        ImageQuality = 85,
        EmbedFonts = true
    };

    foreach (var pdfPath in scannedPdfPaths)
    {
        var document = engine.OpenPDF(pdfPath);
        document.RecognizeAll(recognitionSettings);

        string outputPath = Path.Combine(
            outputDirectory,
            Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");

        document.SaveAs(outputPath, outputSettings);
        document.Dispose();
    }

    engine.Shutdown();
}
// OmniPage: Searchable PDF requires OutputSettings configuration before save
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    var recognitionSettings = new RecognitionSettings
    {
        Language = "English",
        AccuracyMode = "Maximum",
        PreserveLayout = true
    };

    var outputSettings = new OutputSettings
    {
        Format = OutputFormat.SearchablePDF,
        Compression = PDFCompression.Standard,
        ImageQuality = 85,
        EmbedFonts = true
    };

    foreach (var pdfPath in scannedPdfPaths)
    {
        var document = engine.OpenPDF(pdfPath);
        document.RecognizeAll(recognitionSettings);

        string outputPath = Path.Combine(
            outputDirectory,
            Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");

        document.SaveAs(outputPath, outputSettings);
        document.Dispose();
    }

    engine.Shutdown();
}
Imports System.IO

' OmniPage: Searchable PDF requires OutputSettings configuration before save
Public Sub ConvertArchiveToSearchable(scannedPdfPaths As String(), outputDirectory As String)
    Using engine As New OmniPageEngine()
        engine.SetLicenseFile(licensePath)
        engine.Initialize()

        Dim recognitionSettings As New RecognitionSettings With {
            .Language = "English",
            .AccuracyMode = "Maximum",
            .PreserveLayout = True
        }

        Dim outputSettings As New OutputSettings With {
            .Format = OutputFormat.SearchablePDF,
            .Compression = PDFCompression.Standard,
            .ImageQuality = 85,
            .EmbedFonts = True
        }

        For Each pdfPath As String In scannedPdfPaths
            Dim document = engine.OpenPDF(pdfPath)
            document.RecognizeAll(recognitionSettings)

            Dim outputPath As String = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(pdfPath) & "_searchable.pdf")

            document.SaveAs(outputPath, outputSettings)
            document.Dispose()
        Next
    End Using

    engine.Shutdown()
End Sub
$vbLabelText   $csharpLabel

Enfoque IronOCR:

// IronOCR: Searchable PDF output is one method call on the result
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
    var ocr = new IronTesseract();

    foreach (var pdfPath in scannedPdfPaths)
    {
        using var input = new OcrInput();
        input.LoadPdf(pdfPath); // All pages loaded automatically

        var result = ocr.Read(input);

        string outputPath = Path.Combine(
            outputDirectory,
            Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");

        result.SaveAsSearchablePdf(outputPath);
        Console.WriteLine($"Processed: {Path.GetFileName(pdfPath)} ({result.Pages.Length} pages)");
    }
}
// IronOCR: Searchable PDF output is one method call on the result
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
    var ocr = new IronTesseract();

    foreach (var pdfPath in scannedPdfPaths)
    {
        using var input = new OcrInput();
        input.LoadPdf(pdfPath); // All pages loaded automatically

        var result = ocr.Read(input);

        string outputPath = Path.Combine(
            outputDirectory,
            Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");

        result.SaveAsSearchablePdf(outputPath);
        Console.WriteLine($"Processed: {Path.GetFileName(pdfPath)} ({result.Pages.Length} pages)");
    }
}
Imports System.IO
Imports IronOcr

Public Sub ConvertArchiveToSearchable(scannedPdfPaths As String(), outputDirectory As String)
    Dim ocr As New IronTesseract()

    For Each pdfPath As String In scannedPdfPaths
        Using input As New OcrInput()
            input.LoadPdf(pdfPath) ' All pages loaded automatically

            Dim result = ocr.Read(input)

            Dim outputPath As String = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(pdfPath) & "_searchable.pdf")

            result.SaveAsSearchablePdf(outputPath)
            Console.WriteLine($"Processed: {Path.GetFileName(pdfPath)} ({result.Pages.Length} pages)")
        End Using
    Next
End Sub
$vbLabelText   $csharpLabel

SaveAsSearchablePdf incrusta una capa de texto en el PDF, haciéndolo indexable en sistemas de gestión de documentos sin alterar la apariencia visual del escaneo original. La guía en formato PDF con función de búsqueda abarca las opciones de capas de texto y el ejemplo de OCR en PDF demuestra el procesamiento integral de archivos PDF escaneados.

Referencia de mapeo de la API de Kofax OmniPagea IronOCR

Kofax OmniPage Equivalente a IronOCR
using Kofax.OmniPage.CSDK; using IronOcr;
using Kofax.OmniPageCSDK; using IronOcr;
using CSDK; using IronOcr;
new OmniPageEngine() No es necesario: no hay objeto de motor
engine.SetLicenseFile(path) IronOcr.License.LicenseKey = "key";
engine.Initialize() No es necesario
engine.Shutdown() No es necesario
engine.CreateDocument() new OcrInput()
document.AddPage(imagePath) input.LoadImage(imagePath)
engine.OpenPDF(pdfPath) input.LoadPdf(pdfPath)
engine.OpenTiff(tiffPath) input.LoadImageFrames(tiffPath)
document.Recognize(settings) new IronTesseract().Read(input)
document.RecognizeAll(settings) new IronTesseract().Read(input) (todas las páginas a la vez)
document.GetText() result.Text
document.GetZoneText(zoneName) input.LoadImage(path, cropRectangle) + result.Text
document.Dispose() using var input = new OcrInput() (automático)
iterator.GetText() result.Pages[n].Words[m].Text
iterator.GetBoundingBox() result.Pages[n].Words[m].X / Y / Width / Height
iterator.GetConfidence() result.Pages[n].Words[m].Confidence
engine.LoadLanguageDictionary("German") dotnet add package IronOcr.Languages.German
settings.PrimaryLanguage = "English" ocr.Language = OcrLanguage.English;
settings.SecondaryLanguages = new[] {"German"} ocr.Language = OcrLanguage.English + OcrLanguage.German;
settings.DeskewImage = true input.Deskew();
settings.DespeckleLevel = 2 input.DeNoise();
settings.ContrastEnhancement = true input.Contrast();
settings.AutoRotate = true input.Deskew(); (incluye corrección de rotación)
document.SaveAs(path, outputSettings) result.SaveAsSearchablePdf(path)
OutputFormat.SearchablePDF result.SaveAsSearchablePdf(path)
OutputFormat.XML (con coordenadas) result.Pages / .Paragraphs / gráfico de objetos .Words
FormZone con límites coordinados new CropRectangle(x, y, width, height)
Conteo de licencias por página No procede
despliegue de archivos .lic No procede
Configuración del servidor de licencias No procede

Problemas comunes de migración y soluciones

Problema 1: Excepciones de archivo de licencia no encontrado en producción

Kofax OmniPage: Cada despliegue requiere que el archivo .lic exista en una ruta específica accesible para el proceso de la aplicación. Un pipeline de despliegue que no copie explícitamente el archivo de licencia al servidor de destino causa FileNotFoundException o LicenseException en la inicialización del motor. Los equipos de seguridad a menudo señalan como problemático incrustar archivos .lic en imágenes de contenedores o comprometerlas con el control de código fuente.

Solución:IronOCR lee su licencia a partir de una cadena de texto. Almacena la clave como una variable de entorno y léela al iniciar el sistema; no hay archivos que implementar ni rutas que configurar.

// At application startup — reads IRONOCR_LICENSE_KEY from environment
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
    ?? throw new InvalidOperationException("IronOCR license key not configured.");
// At application startup — reads IRONOCR_LICENSE_KEY from environment
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
    ?? throw new InvalidOperationException("IronOCR license key not configured.");
Imports System

' At application startup — reads IRONOCR_LICENSE_KEY from environment
IronOcr.License.LicenseKey = If(Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY"), Throw New InvalidOperationException("IronOCR license key not configured."))
$vbLabelText   $csharpLabel

En Docker, pase --env IRONOCR_LICENSE_KEY=YOUR-KEY. En Azure App Service, configúrelo como una configuración de la aplicación. En Kubernetes, inyéctelo a través de un Secret. Sin montaje de archivos, sin configuración de rutas, sin revisión de seguridad para archivos de licencia integrados.

Problema 2: Las licencias flotantes se bloquean tras un fallo del proceso.

Kofax OmniPage: Cuando un proceso de aplicación se bloquea, es terminado por un watchdog o sale a través de Environment.FailFast, engine.Shutdown() no se ejecuta. Las licencias flotantes permanecen asignadas hasta que expira el tiempo de espera del servidor de licencias, que suele ser de 30 a 60 minutos. En un entorno de contenedores donde los pods se reinician con frecuencia, este comportamiento agota el conjunto de licencias.

Solución:IronOCR no tiene el concepto de asiento de licencia prestado. Un fallo en un proceso no libera recursos ni bloquea ningún sistema externo. El siguiente proceso comienza de inmediato, sin necesidad de esperar a que haya asientos disponibles:

// IronOCR: Process crash has no license implications whatsoever
// Start processing immediately after any failure
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("document.jpg"); // No seat to check out
// IronOCR: Process crash has no license implications whatsoever
// Start processing immediately after any failure
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("document.jpg"); // No seat to check out
' IronOCR: Process crash has no license implications whatsoever
' Start processing immediately after any failure
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim result = New IronTesseract().Read("document.jpg") ' No seat to check out
$vbLabelText   $csharpLabel

Para servicios ASP.NET Core resilientes, consulte la guía de OCR asíncrono para obtener patrones que se integren perfectamente con los servicios alojados y el cierre controlado.

Problema 3: Fallo en la compilación de la imagen de Docker: el instalador no puede ejecutarse de forma no interactiva.

Kofax OmniPage: El instalador SDK de OmniPage es una interfaz gráfica o instalador seminteractivo que no se ejecuta limpiamente en un contexto docker build. Los equipos que intentan integrar el SDK en una imagen de contenedor se encuentran con fallos en el acuerdo de licencia del instalador o en los pasos de selección de componentes. Las soluciones alternativas (opciones de instalación silenciosa, copias de DLL preextraídas) no están documentadas y dependen de la versión.

Solución: IronOCR's Dockerfile son tres líneas:

FROM mcr.microsoft.com/dotnet/aspnet:8.0
RUN apt-get update && apt-get install -y libgdiplus  # Single Linux dependency
COPY --from=build /app/publish /app
WORKDIR /app
ENTRYPOINT ["dotnet", "YourApp.dll"]

libgdiplus es la única dependencia del sistema. El paquete NuGet de IronOCR se restaura mediante dotnet restore dentro de la etapa de construcción como cualquier otra referencia de paquete. La guía de implementación de Docker abarca las imágenes base de Debian y Alpine, y la guía de implementación de Linux abarca los nombres de los paquetes específicos de cada distribución.

Problema 4: Se requiere reactivación después de la migración de máquinas virtuales o el escalado automático en la nube.

Kofax OmniPage: La activación de OmniPage está vinculada a la identidad del hardware. Los entornos en la nube que migran máquinas virtuales entre hosts físicos, se autoescalan a nuevas instancias o reemplazan contenedores con imágenes nuevas provocan cambios en la identidad del hardware que requieren reactivación. Contactar con el soporte de Tungsten para la reactivación en medio de un incidente aumenta el riesgo operativo.

Solución: La clave de licencia de IronOCR es independiente del hardware. La misma clave funciona en cualquier máquina, cualquier contenedor, cualquier región de la nube y cualquier número de instancias con escalado automático dentro del nivel de licencia. Sin reactivación, sin contacto con el servicio de asistencia, sin tiempo de inactividad:

// Identical startup code on any hardware topology
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
bool valid = IronOcr.License.IsLicensed; // Verify without a network call
// Identical startup code on any hardware topology
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
bool valid = IronOcr.License.IsLicensed; // Verify without a network call
' Identical startup code on any hardware topology
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim valid As Boolean = IronOcr.License.IsLicensed ' Verify without a network call
$vbLabelText   $csharpLabel

Problema 5: El objeto RecognitionSettings no tiene un equivalente en IronOCR.

Kofax OmniPage: OmniPage utiliza un objeto RecognitionSettings (o PreprocessingSettings dependiendo de la versión del SDK) para configurar el comportamiento del motor por documento. Los equipos que migran esperan un objeto de configuración paralelo en IronOCR.

Solución:IronOCR divide la configuración en dos superficies: operaciones de preprocesamiento en OcrInput y configuraciones del motor en IronTesseract. No hay ningún objeto de configuración para instanciar:

// OmniPage settings object →IronOCR method calls on OcrInput
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.English;              // RecognitionSettings.Language
// ocr.Configuration.TesseractVersion = ...     // Engine version already optimized

using var input = new OcrInput();
input.LoadImage(imagePath);
input.Deskew();                                  // settings.DeskewImage = true
input.DeNoise();                                 // settings.DespeckleLevel = 2
input.Contrast();                                // settings.ContrastEnhancement = true

var result = ocr.Read(input);
// OmniPage settings object →IronOCR method calls on OcrInput
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.English;              // RecognitionSettings.Language
// ocr.Configuration.TesseractVersion = ...     // Engine version already optimized

using var input = new OcrInput();
input.LoadImage(imagePath);
input.Deskew();                                  // settings.DeskewImage = true
input.DeNoise();                                 // settings.DespeckleLevel = 2
input.Contrast();                                // settings.ContrastEnhancement = true

var result = ocr.Read(input);
Imports IronOcr

' OmniPage settings object → IronOCR method calls on OcrInput
Dim ocr As New IronTesseract()
ocr.Language = OcrLanguage.English ' RecognitionSettings.Language
' ocr.Configuration.TesseractVersion = ... ' Engine version already optimized

Using input As New OcrInput()
    input.LoadImage(imagePath)
    input.Deskew() ' settings.DeskewImage = true
    input.DeNoise() ' settings.DespeckleLevel = 2
    input.Contrast() ' settings.ContrastEnhancement = true

    Dim result = ocr.Read(input)
End Using
$vbLabelText   $csharpLabel

La guía de corrección de la calidad de imagen enumera todos los métodos de preprocesamiento disponibles, con indicaciones sobre qué filtros se aplican a qué perfiles de calidad de documento.

Problema 6: Los archivos de plantilla de zona no se pueden portar directamente.

Kofax OmniPage: Las plantillas de formulario OmniPage almacenan definiciones de zona en archivos de plantilla propietarios .fdt o .fpf que definen posiciones de campo, tipos de reconocimiento y reglas de validación por clase de documento. Estos archivos no pueden ser importados por IronOCR.

Solución: Extraiga los datos de coordenadas de los archivos de plantilla (están basados en XML) y convierta cada zona en un CropRectangle. Los nombres de los campos se convierten en claves del diccionario; las coordenadas de la zona se asignan directamente a los parámetros (x, y, width, height):

// Convert OmniPage zone definition to IronOCR CropRectangle
// OmniPage zone: Name="TotalDue" X="490" Y="612" Width="180" Height="28"
//IronOCR equivalent:
var totalDueRegion = new CropRectangle(490, 612, 180, 28);

using var input = new OcrInput();
input.LoadImage(invoicePath, totalDueRegion);
string totalDue = new IronTesseract().Read(input).Text.Trim();
// Convert OmniPage zone definition to IronOCR CropRectangle
// OmniPage zone: Name="TotalDue" X="490" Y="612" Width="180" Height="28"
//IronOCR equivalent:
var totalDueRegion = new CropRectangle(490, 612, 180, 28);

using var input = new OcrInput();
input.LoadImage(invoicePath, totalDueRegion);
string totalDue = new IronTesseract().Read(input).Text.Trim();
Imports IronOcr

' Convert OmniPage zone definition to IronOCR CropRectangle
' OmniPage zone: Name="TotalDue" X="490" Y="612" Width="180" Height="28"
' IronOCR equivalent:
Dim totalDueRegion As New CropRectangle(490, 612, 180, 28)

Using input As New OcrInput()
    input.LoadImage(invoicePath, totalDueRegion)
    Dim totalDue As String = New IronTesseract().Read(input).Text.Trim()
End Using
$vbLabelText   $csharpLabel

Para documentos donde las zonas varían por página, cargue la misma imagen con diferentes valores CropRectangle por región en lugar de volver a cargar el archivo. El ejemplo de recorte de región demuestra múltiples lecturas de región de una sola fuente de imagen.

Lista de verificación para la migración de Kofax OmniPage

Pre-Migración

Identifique todos los puntos de integración de OmniPage en el código fuente:

# Find all OmniPage namespace references
grep -r "Kofax\|OmniPage\|CSDK" --include="*.cs" --include="*.csproj" .

# Find engine lifecycle calls
grep -r "Initialize\|Shutdown\|SetLicenseFile" --include="*.cs" .

# Find document and zone creation patterns
grep -r "CreateDocument\|AddPage\|FormZone\|RecognitionSettings\|PreprocessingSettings" --include="*.cs" .

# Find output format configuration
grep -r "OutputSettings\|OutputFormat\|SaveAs\|GetText" --include="*.cs" .

# Find license file path references
grep -r "\.lic\|license\.lic\|licensePath" --include="*.cs" --include="*.config" --include="*.json" .
# Find all OmniPage namespace references
grep -r "Kofax\|OmniPage\|CSDK" --include="*.cs" --include="*.csproj" .

# Find engine lifecycle calls
grep -r "Initialize\|Shutdown\|SetLicenseFile" --include="*.cs" .

# Find document and zone creation patterns
grep -r "CreateDocument\|AddPage\|FormZone\|RecognitionSettings\|PreprocessingSettings" --include="*.cs" .

# Find output format configuration
grep -r "OutputSettings\|OutputFormat\|SaveAs\|GetText" --include="*.cs" .

# Find license file path references
grep -r "\.lic\|license\.lic\|licensePath" --include="*.cs" --include="*.config" --include="*.json" .
SHELL

Inventario antes de comenzar:

  • Contar archivos con importaciones del espacio de nombres OmniPage
  • Enumere todas las definiciones FormTemplate y FormZone y extraiga sus datos de coordenadas
  • Identifique qué diccionarios de idioma están cargados y asigne a los paquetes NuGet IronOcr.Languages.*
  • Tenga en cuenta qué formatos de salida se utilizan (PDF con capacidad de búsqueda, texto plano, XML) y sus equivalentes en IronOCR.
  • Localice todas las referencias de archivo .lic en scripts de despliegue y configuración

Migración de código

  1. Elimine las referencias DLL de OmniPage de todos los archivos .csproj
  2. Ejecute dotnet add package IronOcr en cada proyecto que realice OCR
  3. Agregue paquetes de idioma para cada idioma en uso: dotnet add package IronOcr.Languages.[Language]
  4. Agregue IronOcr.License.LicenseKey = ...; en cada punto de entrada de la aplicación (Program.cs, Startup.cs o servicio host)
  5. Reemplace todos using Kofax.OmniPage.CSDK;, using CSDK; e importaciones de namespace relacionadas con using IronOcr;
  6. Elimine toda la construcción OmniPageEngine, SetLicenseFile y llamadas Initialize
  7. Elimine todas las llamadas engine.Shutdown() y las implementaciones IDisposable que existen únicamente para garantizar el apagado
  8. Reemplace engine.CreateDocument() + document.AddPage() con new OcrInput() + input.LoadImage()
  9. Reemplace engine.OpenPDF() + iteración por página con input.LoadPdf() (todas las páginas en una llamada)
  10. Reemplace engine.OpenTiff() + bucles por cuadro con input.LoadImageFrames()
  11. Reemplace document.Recognize(settings) con new IronTesseract().Read(input)
  12. Convierta los datos de coordenadas FormZone a instancias CropRectangle(x, y, width, height)
  13. Reemplace document.GetZoneText() con input.LoadImage(path, cropRectangle) por región + result.Text
  14. Reemplace document.SaveAs(path, outputSettings) para PDF búsqueda con result.SaveAsSearchablePdf(path)
  15. Reemplace los patrones de iterador de resultado con travesía de propiedades result.Pages / .Paragraphs / .Words
  16. Elimine objetos PreprocessingSettings y reemplace las banderas booleanas con llamadas de método explícitas input.Deskew(), input.DeNoise(), input.Contrast()
  17. Elimine las rutas de los archivos de licencia de los scripts de implementación, los archivos de configuración y las plantillas de infraestructura como código.

Posmigración

  • Verifique la precisión del OCR en una muestra representativa de más de 100 documentos del corpus real; compárela con la salida de OmniPage línea por línea para facturas, contratos y formularios.
  • Confirme que la extracción de zona basada en CropRectangle devuelve texto que coincide con la salida GetZoneText() de OmniPage para cada campo mapeado
  • Prueba el procesamiento de PDF de varias páginas: verifica el número de páginas, el orden de las páginas y la continuidad del texto.
  • Prueba de procesamiento de TIFF multifotograma: confirmar que todos los fotogramas se procesan y que se conserva la secuencia de fotogramas.
  • Verificar que el archivo PDF con capacidad de búsqueda sea indexable en el sistema de gestión documental utilizado (SharePoint, OpenText, etc.).
  • Ejecutar una prueba de procesamiento paralelo con más de 50 documentos concurrentes y confirmar que no haya problemas de seguridad de subprocesos.
  • Pruebe la cobertura de paquetes de idioma: cargue cada diccionario de idioma utilizado anteriormente a través de IronOcr.Languages.* y verifique la calidad del reconocimiento
  • Confirme que los fallos del proceso y los reinicios del contenedor no requieren ninguna acción de recuperación de licencia.
  • Ejecute la construcción de Docker en un agente CI — verifique que dotnet restore resuelva IronOCR sin pasos de instalador
  • Validar que las puntuaciones de confianza estén disponibles por palabra y que coincidan con las expectativas de calidad de datos de cualquier flujo de trabajo de validación posterior.
  • Verifique que la aplicación se inicie limpiamente sin el archivo .lic presente en ninguna ruta

Principales ventajas de migrar a IronOCR

La complejidad del despliegue se reduce de semanas a minutos. Un proyecto que anteriormente requería acceso al instalador del SDK, despliegue del archivo .lic, configuración del firewall para el servidor de licencias y coordinación del equipo de infraestructura ahora se despliega vía dotnet restore. Un nuevo desarrollador clona el repositorio y ejecuta el proyecto. El pipeline de CI/CD aprovisiona un nuevo servidor de producción. Las imágenes de contenedor se crean sin pasos de instalación.

El riesgo de licencia desaparece por completo. No hay asientos flotantes para agotar, no hay tiempos de espera de checkout que esperar, no hay huellas de hardware para reactivar y no hay archivos .lic para proteger en pipelines de despliegue. Si una aplicación falla a las 3 de la mañana, no libera nada ni bloquea nada. El técnico de guardia reinicia el proceso; La OCR reanuda sus operaciones de inmediato. La página del producto IronOCR cubre todos los niveles de licencia.

La implementación multiplataforma se convierte en un objetivo estándar de NuGet . Las máquinas de desarrollo macOS funcionan sin excepción de plataforma. Los servidores de producción Linux no requieren una versión del SDK de enero de 2026. Los contenedores de Docker se construyen a partir de una imagen base mcr.microsoft.com/dotnet/aspnet estándar con una sola dependencia del sistema. La misma referencia de paquete IronOcr en .csproj produce una construcción funcional en Windows, Linux y macOS. Consulte la guía de implementación de Azure y la guía de implementación de AWS para obtener información sobre la configuración específica de la nube.

El rendimiento en paralelo se adapta al hardware. La arquitectura de motor compartido de OmniPage requiere un mecanismo de bloqueo que serializa el reconocimiento concurrente. Las instancias sin estado de IronOCRIronTesseract escalan linealmente con los núcleos de CPU disponibles. Duplicar el número de núcleos de un servidor de procesamiento por lotes duplica el rendimiento sin necesidad de cambios en la configuración ni licencias adicionales.

El acceso a datos estructurados es inmediato. OcrResult.Pages, Paragraphs, Lines, Words y Characters exponen la estructura completa del documento como un gráfico de objetos .NET navegable. La confianza y las coordenadas por palabra son propiedades de cada objeto de palabra. No se requiere ninguna canalización de exportación secundaria, ninguna configuración de formato ni ningún archivo de salida para acceder a los datos de posición.

El costo es fijo y predecible. La combinación de la licencia del SDK, el mantenimiento anual y las tarifas de ejecución por página de OmniPage genera un costo que aumenta con el uso y se repite anualmente.IronOCR a $999–$2,999 perpetuo es una compra única. Un flujo de trabajo de 500.000 páginas al año que generaba cargos por página recupera el coste de la licencia de IronOCR en cuestión de semanas. El centro de IronOCR y los tutoriales de IronOCR están disponibles sin necesidad de un contrato de soporte.

Por favor notaKofax OmniPage, OpenPDF y Tesseract son marcas registradas de sus respectivos propietarios. Este sitio no está afiliado, respaldado ni patrocinado por Google, Kofax o LibrePDF. Todos los nombres de producto, 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 Kofax OmniPage 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 de código al migrar de Kofax OmniPage a IronOCR?

Sustituya las secuencias de inicialización de Kofax OmniPage 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 iguala la precisión de OCR de Kofax OmniPage 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 maneja IronOCR los datos de idioma que Kofax OmniPage 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.

¿La migración de Kofax OmniPage a IronOCR requiere cambios en la infraestructura de implementación?

IronOCR requiere menos cambios de infraestructura que Kofax OmniPage. No hay rutas binarias del SDK, ubicaciones de archivos de licencia ni configuraciones del servidor de licencias. El paquete NuGet contiene el motor de 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 lo hace Kofax OmniPage?

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 Kofax OmniPage 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é sucede con mis pruebas existentes después de migrar de Kofax OmniPage 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