Saltar al pie de página
VIDEOS

Migración de ABBYY FineReader a IronOCR

Esta guía acompaña a los desarrolladores .NET paso a paso en el proceso de sustitución del SDK de Motor ABBYY FineReader por IronOCR . Cubre los pasos mecánicos para eliminar las dependencias COM y los artefactos del instalador del SDK, establece un mapeo de la API de ABBYY con sus equivalentes de IronOCR y proporciona ejemplos de código antes y después para los patrones que se encuentran con mayor frecuencia en las integraciones de ABBYY en producción. La migración se dirige a equipos que han decidido que el costo empresarial y la complejidad de despliegue de ABBYY ya no se alinean con sus requisitos de proyecto.

¿Por qué migrar desde ABBYY FineReader?

ABBYY FineReader Engine es una plataforma OCR competente, pero su arquitectura fue diseñada para entornos Enterprise con Windows y equipos de infraestructura dedicados. Cuando la carga de trabajo real de un equipo .NET consiste en el procesamiento de facturas, la digitalización de contratos o la extracción de formularios escaneados, esa arquitectura se convierte en una desventaja en lugar de una ventaja.

La deuda de interoperabilidad COM se acumula con el tiempo. Cada integración de ABBYY en .NET pasa por una capa de interoperabilidad COM. Los objetos COM requieren una gestión explícita del ciclo de vida: crear, inicializar, procesar y luego cerrar en un bloque finally o el proceso tendrá fugas de memoria. Cada ruta de código que interactúa con ABBYY sigue este patrón. A lo largo de dos o tres años de incorporaciones de nuevas funcionalidades, ese ritual del ciclo de vida se propaga a través de las clases de servicio, los procesos en segundo plano y los gestores de solicitudes. El resultado es un 30-50% de código boilerplate en cada clase relacionada con OCR que desaparece por completo cuando cambias a IronTesseract.

El instalador del SDK bloquea los patrones de implementación modernos. ABBYY implementa mediante un instalador del SDK de Windows que coloca los binarios, los datos de lenguaje, los archivos de tiempo de ejecución y los archivos de licencia en rutas codificadas. Contener un servicio que usa ABBYY requiere o bien crear una imagen base personalizada de 300+ MB a partir de la salida de ese instalador o montar volúmenes con archivos de licencia al inicio. Ningún enfoque se ajusta a un pipeline estándar de Kubernetes o cloud-native.IronOCR es un paquete NuGet: el mismo dotnet restore que descarga todas las demás dependencias descarga el motor OCR completo.

El licenciamiento por página convierte el volumen en un centro de costos. Los modelos de licenciamiento por volumen de ABBYY cobran por cada página procesada que supere los umbrales incluidos. Una aplicación que procesa 50.000 documentos al mes en su lanzamiento y alcanza los 500.000 dos años después, ve cómo sus costes de OCR crecen en proporción directa a su éxito.IronOCR cobra una tarifa fija por la licencia: un equipo que procesa dos millones de páginas al mes paga exactamente el mismo coste de licencia que uno que procesa dos mil.

Los datos de idioma requieren coordinación manual para su implementación. Los paquetes de idioma de ABBYY se encuentran como archivos en el directorio de ejecución del SDK. Agregar un idioma implica identificar los archivos de datos correctos, copiarlos a la ruta adecuada en cada destino de despliegue y actualizar los scripts de CI/CD para incluirlos. En IronOCR, agregar francés es dotnet add package IronOcr.Languages.French — el administrador de paquetes maneja el resto.

Los fallos de archivos de licencia golpean la producción sin aviso. Las licencias de ABBYY viven como archivos .lic y .key que deben estar presentes en rutas específicas del disco cuando loader.GetEngineObject() se ejecuta. Si esos archivos faltan en un nuevo servidor de producción (debido a un script de implementación incorrecto, un error al copiar archivos o un problema de permisos), la llamada falla al iniciar. Una licencia caducada falla de la misma manera. La licencia de IronOCR es una clave de cadena asignada en el código de inicio, almacenable en cualquier gestor de secretos, con validación verificada por IronOcr.License.IsValidLicense antes de que la aplicación acepte tráfico.

La seguridad de los threads requiere una instancia única de motor compartido. El motor de ABBYY no es thread-safe trivialmente para llamadas CreateFRDocument concurrentes desde múltiples threads. Las implementaciones en producción utilizan estrategias de bloqueo o grupos de procesadores. El IronTesseract de IronOCR es sin estado: levanta una instancia por thread, ejecuta el reconocimiento concurrentemente sin locks, dispone cuando termine.

El problema fundamental

// ABBYY: COM loader + license path validation + profile load — before a single pixel of OCR runs
var loader = new EngineLoader();
var engine = loader.GetEngineObject(
    @"C:\Program Files\ABBYY SDK\FineReader Engine\Bin",  // Breaks on every new machine
    @"C:\Program Files\ABBYY SDK\License"                 // Fails if .lic file is missing
);
engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
var langParams = engine.CreateLanguageParams();
langParams.Languages.Add("English");
// ABBYY: COM loader + license path validation + profile load — before a single pixel of OCR runs
var loader = new EngineLoader();
var engine = loader.GetEngineObject(
    @"C:\Program Files\ABBYY SDK\FineReader Engine\Bin",  // Breaks on every new machine
    @"C:\Program Files\ABBYY SDK\License"                 // Fails if .lic file is missing
);
engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
var langParams = engine.CreateLanguageParams();
langParams.Languages.Add("English");
' ABBYY: COM loader + license path validation + profile load — before a single pixel of OCR runs
Dim loader As New EngineLoader()
Dim engine = loader.GetEngineObject(
    "C:\Program Files\ABBYY SDK\FineReader Engine\Bin",  ' Breaks on every new machine
    "C:\Program Files\ABBYY SDK\License"                 ' Fails if .lic file is missing
)
engine.LoadPredefinedProfile("DocumentConversion_Accuracy")
Dim langParams = engine.CreateLanguageParams()
langParams.Languages.Add("English")
$vbLabelText   $csharpLabel
// IronOCR: the entire initialization
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var ocr = new IronTesseract();
// IronOCR: the entire initialization
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var ocr = new IronTesseract();
Imports IronOcr

' IronOCR: the entire initialization
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim ocr As New IronTesseract()
$vbLabelText   $csharpLabel

IronOCR vs ABBYY FineReader: Comparación de características

La siguiente tabla resume las capacidades relevantes para los equipos que evalúan esta migración.

Característica Motor ABBYY FineReader IronOCR
Instalación Instalador del SDK (Windows) dotnet add package IronOcr
Adquisición Contacte con el departamento de ventas (4-12 semanas) NuGet de autoservicio
Modelo de licencia Enterprise, por servidor o por página Perpetua, $999-$2,999 de una sola vez
Gestión de licencias .lic + .key archivos en disco Clave de cadena en el código o variable de entorno
Integración .NET Interoperabilidad COM .NET nativo
Dependencia COM No
Seguridad de hilos Requiere estrategia de bloqueo Completo (uno IronTesseract por thread)
Idiomas compatibles Más de 190 125+
Instalación de idioma Archivos de datos de tiempo de ejecución en la ruta del SDK Paquetes de lenguaje NuGet
Entrada de PDF Sí (vía CreatePDFFile) Sí (nativo, input.LoadPdf())
Salida en PDF con capacidad de búsqueda Sí (canal de exportación) Sí (result.SaveAsSearchablePdf())
Preprocesamiento automático Basado en perfiles Funciones integradas (Desinclinación, Reducción de ruido, Contraste, Binarización, Nitidez)
OCR basado en regiones Objetos de zona (CreateZone, SetBounds) Parámetro CropRectangle
Lectura de códigos de barras Sí (ocr.Configuration.ReadBarCodes = true)
Traducción multiplataforma Windows, Linux, macOS Windows, Linux, macOS, Docker, Azure, AWS
Despliegue de Docker Se requiere una imagen base personalizada. Imagen base estándar de .NET + libgdiplus
Puntuación de confianza Sí (result.Confidence)
Tiempo hasta el primer resultado de OCR 4-12 semanas (contratación) El mismo día

Guía de inicio rápido: Migración de ABBYY FineReader a IronOCR

Paso 1: Sustituir el paquete NuGet

ABBYY FineReader Engine no tiene paquete NuGet . Elimínelo desinstalando el SDK y eliminando la referencia de ensamblaje manual del archivo de su proyecto:


<Reference Include="FREngine">
  <HintPath>C:\Program Files\ABBYY SDK\FineReader Engine\Bin\FREngine.dll</HintPath>
</Reference>

<Reference Include="FREngine">
  <HintPath>C:\Program Files\ABBYY SDK\FineReader Engine\Bin\FREngine.dll</HintPath>
</Reference>
XML

Luego retire la referencia de interoperabilidad COM FREngine.dll del nodo Referencias de Visual Studio, o elimine la entrada correspondiente directamente de su archivo de proyecto. Instala IronOCR desde NuGet :

dotnet add package IronOcr

Paso 2: Actualizar los espacios de nombres

// Before (ABBYY)
using FREngine;
using ABBYY.FineReader;

// After (IronOCR)
using IronOcr;
// Before (ABBYY)
using FREngine;
using ABBYY.FineReader;

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

Paso 3: Inicializar licencia

Agregue esto una sola vez al iniciar la aplicación, antes de cualquier llamada de OCR:

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 o en un gestor de secretos para implementaciones en producción:

IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY");
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY");
Imports System

IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
$vbLabelText   $csharpLabel

Ejemplos de migración de código

Ciclo de vida del motor en un servicio de Windows frente a IronTesseract sin estado.

La ceremonia de inicialización del motor de ABBYY pertenece a un contenedor de servicio porque los objetos EngineLoader y IEngine son costosos de crear. La mayoría de las integraciones de producción encapsulan el motor en un servicio singleton con métodos explícitos de inicio y finalización.

Enfoque de ABBYY FineReader:

using FREngine;

public class DocumentOcrService : IHostedService, IDisposable
{
    private IEngine _engine;

    public Task StartAsync(CancellationToken cancellationToken)
    {
        // Step 1: Create loader — requires Interoperabilidad COM registration
        var loader = new EngineLoader();

        // Step 2: Load engine from SDK path — throws if license files are missing
        _engine = loader.GetEngineObject(
            @"C:\Program Files\ABBYY SDK\FineReader Engine\Bin",
            @"C:\Program Files\ABBYY SDK\License"
        );

        // Step 3: Load profile before any recognition work
        _engine.LoadPredefinedProfile("DocumentConversion_Accuracy");

        return Task.CompletedTask;
    }

    public string ProcessDocument(string imagePath)
    {
        // Document must be created and destroyed per call
        var document = _engine.CreateFRDocument();
        try
        {
            document.AddImageFile(imagePath, null, null);
            document.Process(null);
            return document.PlainText.Text;
        }
        finally
        {
            document.Close(); // Memory leaks if omitted
        }
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _engine = null; // COM cleanup
        return Task.CompletedTask;
    }

    public void Dispose() => _engine = null;
}
using FREngine;

public class DocumentOcrService : IHostedService, IDisposable
{
    private IEngine _engine;

    public Task StartAsync(CancellationToken cancellationToken)
    {
        // Step 1: Create loader — requires Interoperabilidad COM registration
        var loader = new EngineLoader();

        // Step 2: Load engine from SDK path — throws if license files are missing
        _engine = loader.GetEngineObject(
            @"C:\Program Files\ABBYY SDK\FineReader Engine\Bin",
            @"C:\Program Files\ABBYY SDK\License"
        );

        // Step 3: Load profile before any recognition work
        _engine.LoadPredefinedProfile("DocumentConversion_Accuracy");

        return Task.CompletedTask;
    }

    public string ProcessDocument(string imagePath)
    {
        // Document must be created and destroyed per call
        var document = _engine.CreateFRDocument();
        try
        {
            document.AddImageFile(imagePath, null, null);
            document.Process(null);
            return document.PlainText.Text;
        }
        finally
        {
            document.Close(); // Memory leaks if omitted
        }
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _engine = null; // COM cleanup
        return Task.CompletedTask;
    }

    public void Dispose() => _engine = null;
}
Imports FREngine
Imports System.Threading
Imports System.Threading.Tasks

Public Class DocumentOcrService
    Implements IHostedService, IDisposable

    Private _engine As IEngine

    Public Function StartAsync(cancellationToken As CancellationToken) As Task Implements IHostedService.StartAsync
        ' Step 1: Create loader — requires Interoperabilidad COM registration
        Dim loader As New EngineLoader()

        ' Step 2: Load engine from SDK path — throws if license files are missing
        _engine = loader.GetEngineObject(
            "C:\Program Files\ABBYY SDK\FineReader Engine\Bin",
            "C:\Program Files\ABBYY SDK\License"
        )

        ' Step 3: Load profile before any recognition work
        _engine.LoadPredefinedProfile("DocumentConversion_Accuracy")

        Return Task.CompletedTask
    End Function

    Public Function ProcessDocument(imagePath As String) As String
        ' Document must be created and destroyed per call
        Dim document = _engine.CreateFRDocument()
        Try
            document.AddImageFile(imagePath, Nothing, Nothing)
            document.Process(Nothing)
            Return document.PlainText.Text
        Finally
            document.Close() ' Memory leaks if omitted
        End Try
    End Function

    Public Function StopAsync(cancellationToken As CancellationToken) As Task Implements IHostedService.StopAsync
        _engine = Nothing ' COM cleanup
        Return Task.CompletedTask
    End Function

    Public Sub Dispose() Implements IDisposable.Dispose
        _engine = Nothing
    End Sub
End Class
$vbLabelText   $csharpLabel

Enfoque IronOCR:

using IronOcr;

public class DocumentOcrService
{
    // No startup, no shutdown, no COM lifecycle
    // IronTesseract is stateless — create per call or reuse per thread

    public string ProcessDocument(string imagePath)
    {
        return new IronTesseract().Read(imagePath).Text;
    }
}
using IronOcr;

public class DocumentOcrService
{
    // No startup, no shutdown, no COM lifecycle
    // IronTesseract is stateless — create per call or reuse per thread

    public string ProcessDocument(string imagePath)
    {
        return new IronTesseract().Read(imagePath).Text;
    }
}
Imports IronOcr

Public Class DocumentOcrService
    ' No startup, no shutdown, no COM lifecycle
    ' IronTesseract is stateless — create per call or reuse per thread

    Public Function ProcessDocument(imagePath As String) As String
        Return New IronTesseract().Read(imagePath).Text
    End Function
End Class
$vbLabelText   $csharpLabel

IronTesseract no tiene ciclo de vida del motor. Se inicializa internamente la primera vez que se usa y no requiere un apagado explícito. El contenedor de servicio alojado, el campo IEngine, y los métodos StopAsync desaparecen. Si la aplicación procesa documentos de manera concurrente, cada thread crea su propia instancia IronTesseract — no se requiere bloqueo. La guía de configuración de IronTesseract cubre opciones de configuración, incluyendo propiedades TesseractVersion y Configuration.

Configuración del idioma de reconocimiento

La configuración de idioma de ABBYY implica crear un objeto LanguageParams, agregar cadenas de nombres de idioma que deben coincidir con los archivos de datos instalados, y asociar esos parámetros con el motor antes de que se procese cualquier documento. Cada idioma adicional requiere que los archivos de datos correspondientes se implementen en la ruta de ejecución.

Enfoque de ABBYY FineReader:

using FREngine;

// Engine must already be initialized with sdkPath and licensePath
private void ConfigureLanguages(IEngine engine, string[] languageCodes)
{
    // Create language parameters object
    var langParams = engine.CreateLanguageParams();

    // Add each language — string names must match installed data file names
    // Missing data file causes runtime failure
    foreach (var lang in languageCodes)
    {
        langParams.Languages.Add(lang);  // e.g., "English", "French", "German"
    }

    // Language params are associated at the profile level, not per-document
    // Changing languages requires reloading profile or reinitializing engine
    engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
}

public string RecognizeFrenchDocument(IEngine engine, string imagePath)
{
    var langParams = engine.CreateLanguageParams();
    langParams.Languages.Add("French");  // Requires FrenchLanguage data files at runtime path

    var document = engine.CreateFRDocument();
    try
    {
        document.AddImageFile(imagePath, null, null);
        document.Process(null);
        return document.PlainText.Text;
    }
    finally
    {
        document.Close();
    }
}
using FREngine;

// Engine must already be initialized with sdkPath and licensePath
private void ConfigureLanguages(IEngine engine, string[] languageCodes)
{
    // Create language parameters object
    var langParams = engine.CreateLanguageParams();

    // Add each language — string names must match installed data file names
    // Missing data file causes runtime failure
    foreach (var lang in languageCodes)
    {
        langParams.Languages.Add(lang);  // e.g., "English", "French", "German"
    }

    // Language params are associated at the profile level, not per-document
    // Changing languages requires reloading profile or reinitializing engine
    engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
}

public string RecognizeFrenchDocument(IEngine engine, string imagePath)
{
    var langParams = engine.CreateLanguageParams();
    langParams.Languages.Add("French");  // Requires FrenchLanguage data files at runtime path

    var document = engine.CreateFRDocument();
    try
    {
        document.AddImageFile(imagePath, null, null);
        document.Process(null);
        return document.PlainText.Text;
    }
    finally
    {
        document.Close();
    }
}
Imports FREngine

' Engine must already be initialized with sdkPath and licensePath
Private Sub ConfigureLanguages(engine As IEngine, languageCodes As String())
    ' Create language parameters object
    Dim langParams = engine.CreateLanguageParams()

    ' Add each language — string names must match installed data file names
    ' Missing data file causes runtime failure
    For Each lang In languageCodes
        langParams.Languages.Add(lang)  ' e.g., "English", "French", "German"
    Next

    ' Language params are associated at the profile level, not per-document
    ' Changing languages requires reloading profile or reinitializing engine
    engine.LoadPredefinedProfile("DocumentConversion_Accuracy")
End Sub

Public Function RecognizeFrenchDocument(engine As IEngine, imagePath As String) As String
    Dim langParams = engine.CreateLanguageParams()
    langParams.Languages.Add("French")  ' Requires FrenchLanguage data files at runtime path

    Dim document = engine.CreateFRDocument()
    Try
        document.AddImageFile(imagePath, Nothing, Nothing)
        document.Process(Nothing)
        Return document.PlainText.Text
    Finally
        document.Close()
    End Try
End Function
$vbLabelText   $csharpLabel

Enfoque IronOCR:

using IronOcr;

// Single language — install IronOcr.Languages.French via NuGet first
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.French;
var result = ocr.Read("french-document.jpg");
Console.WriteLine(result.Text);

// Multiple simultaneous languages — operator overload, no data file management
var multiOcr = new IronTesseract();
multiOcr.Language = OcrLanguage.French + OcrLanguage.German + OcrLanguage.English;
var multiResult = multiOcr.Read("multilingual-contract.jpg");
Console.WriteLine(multiResult.Text);
using IronOcr;

// Single language — install IronOcr.Languages.French via NuGet first
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.French;
var result = ocr.Read("french-document.jpg");
Console.WriteLine(result.Text);

// Multiple simultaneous languages — operator overload, no data file management
var multiOcr = new IronTesseract();
multiOcr.Language = OcrLanguage.French + OcrLanguage.German + OcrLanguage.English;
var multiResult = multiOcr.Read("multilingual-contract.jpg");
Console.WriteLine(multiResult.Text);
Imports IronOcr

' Single language — install IronOcr.Languages.French via NuGet first
Dim ocr As New IronTesseract()
ocr.Language = OcrLanguage.French
Dim result = ocr.Read("french-document.jpg")
Console.WriteLine(result.Text)

' Multiple simultaneous languages — operator overload, no data file management
Dim multiOcr As New IronTesseract()
multiOcr.Language = OcrLanguage.French + OcrLanguage.German + OcrLanguage.English
Dim multiResult = multiOcr.Read("multilingual-contract.jpg")
Console.WriteLine(multiResult.Text)
$vbLabelText   $csharpLabel

Los paquetes de idioma se instalan como paquetes NuGet estándar (dotnet add package IronOcr.Languages.French). No es necesario desplegar manualmente ningún archivo de datos, ni configurar rutas, ni reinicializar el motor al cambiar de idioma. La guía multilingüe explica cómo combinar idiomas y el índice de idiomas enumera los más de 125 paquetes disponibles.

Procesamiento de archivos TIFF multifotograma

ABBYY procesa archivos TIFF de varias páginas iterando sobre los fotogramas y añadiendo cada fotograma como una página de documento independiente. El número de fotogramas debe obtenerse del objeto TIFF, y luego cada fotograma se añade individualmente al contenedor del documento.

Enfoque de ABBYY FineReader:

using FREngine;

public string ProcessMultiFrameTiff(IEngine engine, string tiffPath)
{
    var document = engine.CreateFRDocument();

    try
    {
        // Must add each frame individually — no automatic multi-frame handling
        // Page count requires reading the TIFF metadata before processing
        var imageInfo = engine.CreateImageInfo();
        imageInfo.LoadImageFile(tiffPath);
        int frameCount = imageInfo.FrameCount;

        for (int i = 0; i < frameCount; i++)
        {
            // Each frame added with its frame index via image processing params
            var imgParams = engine.CreateImageProcessingParams();
            imgParams.FrameIndex = i;
            document.AddImageFile(tiffPath, imgParams, null);
        }

        document.Process(null);
        return document.PlainText.Text;
    }
    finally
    {
        document.Close();
    }
}
using FREngine;

public string ProcessMultiFrameTiff(IEngine engine, string tiffPath)
{
    var document = engine.CreateFRDocument();

    try
    {
        // Must add each frame individually — no automatic multi-frame handling
        // Page count requires reading the TIFF metadata before processing
        var imageInfo = engine.CreateImageInfo();
        imageInfo.LoadImageFile(tiffPath);
        int frameCount = imageInfo.FrameCount;

        for (int i = 0; i < frameCount; i++)
        {
            // Each frame added with its frame index via image processing params
            var imgParams = engine.CreateImageProcessingParams();
            imgParams.FrameIndex = i;
            document.AddImageFile(tiffPath, imgParams, null);
        }

        document.Process(null);
        return document.PlainText.Text;
    }
    finally
    {
        document.Close();
    }
}
Imports FREngine

Public Function ProcessMultiFrameTiff(engine As IEngine, tiffPath As String) As String
    Dim document = engine.CreateFRDocument()

    Try
        ' Must add each frame individually — no automatic multi-frame handling
        ' Page count requires reading the TIFF metadata before processing
        Dim imageInfo = engine.CreateImageInfo()
        imageInfo.LoadImageFile(tiffPath)
        Dim frameCount As Integer = imageInfo.FrameCount

        For i As Integer = 0 To frameCount - 1
            ' Each frame added with its frame index via image processing params
            Dim imgParams = engine.CreateImageProcessingParams()
            imgParams.FrameIndex = i
            document.AddImageFile(tiffPath, imgParams, Nothing)
        Next

        document.Process(Nothing)
        Return document.PlainText.Text
    Finally
        document.Close()
    End Try
End Function
$vbLabelText   $csharpLabel

Enfoque IronOCR:

using IronOcr;

// LoadImageFrames handles multi-frame TIFF automatically
using var input = new OcrInput();
input.LoadImageFrames("scanned-batch.tiff");

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

// Per-page results accessible directly
foreach (var page in result.Pages)
{
    Console.WriteLine($"Frame {page.PageNumber}: {page.Text.Length} characters");
    Console.WriteLine(page.Text);
}
using IronOcr;

// LoadImageFrames handles multi-frame TIFF automatically
using var input = new OcrInput();
input.LoadImageFrames("scanned-batch.tiff");

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

// Per-page results accessible directly
foreach (var page in result.Pages)
{
    Console.WriteLine($"Frame {page.PageNumber}: {page.Text.Length} characters");
    Console.WriteLine(page.Text);
}
Imports IronOcr

' LoadImageFrames handles multi-frame TIFF automatically
Using input As New OcrInput()
    input.LoadImageFrames("scanned-batch.tiff")

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

    ' Per-page results accessible directly
    For Each page In result.Pages
        Console.WriteLine($"Frame {page.PageNumber}: {page.Text.Length} characters")
        Console.WriteLine(page.Text)
    Next
End Using
$vbLabelText   $csharpLabel

OcrInput.LoadImageFrames lee cada cuadro en un TIFF multipágina sin iteración manual. El resultado proporciona acceso por página a través de result.Pages, incluyendo texto, datos de coordenadas y confianza por cuadro. La guía de entrada TIFF abarca el manejo tanto de archivos TIFF multifotograma como de GIF animados.

Procesamiento por lotes en paralelo

El motor basado en COM de ABBYY no es seguro para llamar CreateFRDocument concurrentemente desde múltiples threads sin una estrategia de sincronización. Los procesadores de lotes de producción suelen mantener un grupo de instancias del motor o serializar el acceso mediante un bloqueo. Cualquiera de los dos enfoques añade infraestructura que IronOCR elimina.

Enfoque de ABBYY FineReader:

using FREngine;
using System.Collections.Concurrent;
using System.Threading;

public class AbbyyBatchProcessor
{
    // Pool required because engine is not safely concurrent
    private readonly SemaphoreSlim _engineLock = new SemaphoreSlim(1, 1);
    private IEngine _engine;

    public async Task<Dictionary<string, string>> ProcessBatchAsync(string[] imagePaths)
    {
        var results = new ConcurrentDictionary<string, string>();

        // Must serialize — one document at a time through single engine
        foreach (var imagePath in imagePaths)
        {
            await _engineLock.WaitAsync();
            try
            {
                var document = _engine.CreateFRDocument();
                try
                {
                    document.AddImageFile(imagePath, null, null);
                    document.Process(null);
                    results[imagePath] = document.PlainText.Text;
                }
                finally
                {
                    document.Close();
                }
            }
            finally
            {
                _engineLock.Release();
            }
        }

        return new Dictionary<string, string>(results);
    }
}
using FREngine;
using System.Collections.Concurrent;
using System.Threading;

public class AbbyyBatchProcessor
{
    // Pool required because engine is not safely concurrent
    private readonly SemaphoreSlim _engineLock = new SemaphoreSlim(1, 1);
    private IEngine _engine;

    public async Task<Dictionary<string, string>> ProcessBatchAsync(string[] imagePaths)
    {
        var results = new ConcurrentDictionary<string, string>();

        // Must serialize — one document at a time through single engine
        foreach (var imagePath in imagePaths)
        {
            await _engineLock.WaitAsync();
            try
            {
                var document = _engine.CreateFRDocument();
                try
                {
                    document.AddImageFile(imagePath, null, null);
                    document.Process(null);
                    results[imagePath] = document.PlainText.Text;
                }
                finally
                {
                    document.Close();
                }
            }
            finally
            {
                _engineLock.Release();
            }
        }

        return new Dictionary<string, string>(results);
    }
}
Imports FREngine
Imports System.Collections.Concurrent
Imports System.Threading

Public Class AbbyyBatchProcessor
    ' Pool required because engine is not safely concurrent
    Private ReadOnly _engineLock As New SemaphoreSlim(1, 1)
    Private _engine As IEngine

    Public Async Function ProcessBatchAsync(imagePaths As String()) As Task(Of Dictionary(Of String, String))
        Dim results As New ConcurrentDictionary(Of String, String)()

        ' Must serialize — one document at a time through single engine
        For Each imagePath In imagePaths
            Await _engineLock.WaitAsync()
            Try
                Dim document = _engine.CreateFRDocument()
                Try
                    document.AddImageFile(imagePath, Nothing, Nothing)
                    document.Process(Nothing)
                    results(imagePath) = document.PlainText.Text
                Finally
                    document.Close()
                End Try
            Finally
                _engineLock.Release()
            End Try
        Next

        Return New Dictionary(Of String, String)(results)
    End Function
End Class
$vbLabelText   $csharpLabel

Enfoque IronOCR:

using IronOcr;
using System.Collections.Concurrent;
using System.Threading.Tasks;

public class OcrBatchProcessor
{
    public Dictionary<string, string> ProcessBatch(string[] imagePaths)
    {
        var results = new ConcurrentDictionary<string, string>();

        // IronTesseract is thread-safe — one instance per thread, fully parallel
        Parallel.ForEach(imagePaths, imagePath =>
        {
            var ocr = new IronTesseract();  // Each thread owns its instance
            var result = ocr.Read(imagePath);
            results[imagePath] = result.Text;
        });

        return new Dictionary<string, string>(results);
    }
}
using IronOcr;
using System.Collections.Concurrent;
using System.Threading.Tasks;

public class OcrBatchProcessor
{
    public Dictionary<string, string> ProcessBatch(string[] imagePaths)
    {
        var results = new ConcurrentDictionary<string, string>();

        // IronTesseract is thread-safe — one instance per thread, fully parallel
        Parallel.ForEach(imagePaths, imagePath =>
        {
            var ocr = new IronTesseract();  // Each thread owns its instance
            var result = ocr.Read(imagePath);
            results[imagePath] = result.Text;
        });

        return new Dictionary<string, string>(results);
    }
}
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Threading.Tasks

Public Class OcrBatchProcessor
    Public Function ProcessBatch(imagePaths As String()) As Dictionary(Of String, String)
        Dim results = New ConcurrentDictionary(Of String, String)()

        ' IronTesseract is thread-safe — one instance per thread, fully parallel
        Parallel.ForEach(imagePaths, Sub(imagePath)
                                         Dim ocr = New IronTesseract() ' Each thread owns its instance
                                         Dim result = ocr.Read(imagePath)
                                         results(imagePath) = result.Text
                                     End Sub)

        Return New Dictionary(Of String, String)(results)
    End Function
End Class
$vbLabelText   $csharpLabel

Cada instancia IronTesseract es independiente. Parallel.ForEach satura los núcleos de CPU disponibles sin ningún estado compartido, locks, o serialización. La versión de ABBYY procesa los documentos secuencialmente a pesar del envoltorio asíncrono; La versión IronOCR los procesa realmente en paralelo. El ejemplo de multihilo demuestra este patrón mediante comparaciones de tiempos. Para un control de rendimiento de nivel superior, consulte la guía de optimización de velocidad .

Proceso de exportación de documentos

ABBYY soporta múltiples formatos de exportación a través de su método Export con valores FileExportFormatEnum. Exportar a DOCX, RTF, o texto plano requiere crear objetos de parámetros de exportación específicos de formato, luego llamar a document.Export con el valor enum apropiado y el objeto de parámetros.

Enfoque de ABBYY FineReader:

using FREngine;

public class AbbyyExporter
{
    private IEngine _engine;

    public void ExportToMultipleFormats(string imagePath, string outputDir)
    {
        var document = _engine.CreateFRDocument();

        try
        {
            document.AddImageFile(imagePath, null, null);
            document.Process(null);

            string baseName = Path.GetFileNameWithoutExtension(imagePath);

            // Export as plain text
            document.Export(
                Path.Combine(outputDir, baseName + ".txt"),
                FileExportFormatEnum.FEF_TextUnicodeDefaults,
                null
            );

            // Export as searchable PDF (requires PDF export params)
            var pdfParams = _engine.CreatePDFExportParams();
            pdfParams.Scenario = PDFExportScenarioEnum.PDES_Balanced;
            pdfParams.UseOriginalPaperSize = true;
            document.Export(
                Path.Combine(outputDir, baseName + ".pdf"),
                FileExportFormatEnum.FEF_PDF,
                pdfParams
            );

            // Export as DOCX
            var docxParams = _engine.CreateDOCXExportParams();
            document.Export(
                Path.Combine(outputDir, baseName + ".docx"),
                FileExportFormatEnum.FEF_DOCX,
                docxParams
            );
        }
        finally
        {
            document.Close();
        }
    }
}
using FREngine;

public class AbbyyExporter
{
    private IEngine _engine;

    public void ExportToMultipleFormats(string imagePath, string outputDir)
    {
        var document = _engine.CreateFRDocument();

        try
        {
            document.AddImageFile(imagePath, null, null);
            document.Process(null);

            string baseName = Path.GetFileNameWithoutExtension(imagePath);

            // Export as plain text
            document.Export(
                Path.Combine(outputDir, baseName + ".txt"),
                FileExportFormatEnum.FEF_TextUnicodeDefaults,
                null
            );

            // Export as searchable PDF (requires PDF export params)
            var pdfParams = _engine.CreatePDFExportParams();
            pdfParams.Scenario = PDFExportScenarioEnum.PDES_Balanced;
            pdfParams.UseOriginalPaperSize = true;
            document.Export(
                Path.Combine(outputDir, baseName + ".pdf"),
                FileExportFormatEnum.FEF_PDF,
                pdfParams
            );

            // Export as DOCX
            var docxParams = _engine.CreateDOCXExportParams();
            document.Export(
                Path.Combine(outputDir, baseName + ".docx"),
                FileExportFormatEnum.FEF_DOCX,
                docxParams
            );
        }
        finally
        {
            document.Close();
        }
    }
}
Imports FREngine

Public Class AbbyyExporter
    Private _engine As IEngine

    Public Sub ExportToMultipleFormats(imagePath As String, outputDir As String)
        Dim document = _engine.CreateFRDocument()

        Try
            document.AddImageFile(imagePath, Nothing, Nothing)
            document.Process(Nothing)

            Dim baseName As String = Path.GetFileNameWithoutExtension(imagePath)

            ' Export as plain text
            document.Export(
                Path.Combine(outputDir, baseName & ".txt"),
                FileExportFormatEnum.FEF_TextUnicodeDefaults,
                Nothing
            )

            ' Export as searchable PDF (requires PDF export params)
            Dim pdfParams = _engine.CreatePDFExportParams()
            pdfParams.Scenario = PDFExportScenarioEnum.PDES_Balanced
            pdfParams.UseOriginalPaperSize = True
            document.Export(
                Path.Combine(outputDir, baseName & ".pdf"),
                FileExportFormatEnum.FEF_PDF,
                pdfParams
            )

            ' Export as DOCX
            Dim docxParams = _engine.CreateDOCXExportParams()
            document.Export(
                Path.Combine(outputDir, baseName & ".docx"),
                FileExportFormatEnum.FEF_DOCX,
                docxParams
            )
        Finally
            document.Close()
        End Try
    End Sub
End Class
$vbLabelText   $csharpLabel

Enfoque IronOCR:

using IronOcr;

public class OcrExporter
{
    public void ExportToMultipleFormats(string imagePath, string outputDir)
    {
        var ocr = new IronTesseract();
        var result = ocr.Read(imagePath);
        string baseName = Path.GetFileNameWithoutExtension(imagePath);

        // Plain text — direct property access
        File.WriteAllText(
            Path.Combine(outputDir, baseName + ".txt"),
            result.Text
        );

        // Searchable PDF — one method call, no parameter objects
        result.SaveAsSearchablePdf(
            Path.Combine(outputDir, baseName + ".pdf")
        );

        // hOCR format — for document management systems
        result.SaveAsHocrFile(
            Path.Combine(outputDir, baseName + ".hocr")
        );
    }
}
using IronOcr;

public class OcrExporter
{
    public void ExportToMultipleFormats(string imagePath, string outputDir)
    {
        var ocr = new IronTesseract();
        var result = ocr.Read(imagePath);
        string baseName = Path.GetFileNameWithoutExtension(imagePath);

        // Plain text — direct property access
        File.WriteAllText(
            Path.Combine(outputDir, baseName + ".txt"),
            result.Text
        );

        // Searchable PDF — one method call, no parameter objects
        result.SaveAsSearchablePdf(
            Path.Combine(outputDir, baseName + ".pdf")
        );

        // hOCR format — for document management systems
        result.SaveAsHocrFile(
            Path.Combine(outputDir, baseName + ".hocr")
        );
    }
}
Imports IronOcr
Imports System.IO

Public Class OcrExporter
    Public Sub ExportToMultipleFormats(imagePath As String, outputDir As String)
        Dim ocr As New IronTesseract()
        Dim result = ocr.Read(imagePath)
        Dim baseName As String = Path.GetFileNameWithoutExtension(imagePath)

        ' Plain text — direct property access
        File.WriteAllText(
            Path.Combine(outputDir, baseName & ".txt"),
            result.Text
        )

        ' Searchable PDF — one method call, no parameter objects
        result.SaveAsSearchablePdf(
            Path.Combine(outputDir, baseName & ".pdf")
        )

        ' hOCR format — for document management systems
        result.SaveAsHocrFile(
            Path.Combine(outputDir, baseName & ".hocr")
        )
    End Sub
End Class
$vbLabelText   $csharpLabel

El OcrResult de IronOCR expone .Text directamente y proporciona métodos de salida sin objetos de parámetros o enums de formato. La llamada SaveAsSearchablePdf maneja la exportación PDF en una línea frente a la secuencia de tres pasos de parámetros/exportación de ABBYY. La guía en formato PDF, que permite realizar búsquedas, abarca las opciones de rango de páginas y la configuración de compresión. La guía de exportación de hOCR abarca el formato HOCR para sistemas que utilizan resultados de OCR con reconocimiento de posición.

Referencia de mapeo de la API de ABBYY FineReader a IronOCR

Motor ABBYY FineReader Equivalente a IronOCR
new EngineLoader() No es necesario
loader.GetEngineObject(sdkPath, licensePath) new IronTesseract()
engine.LoadPredefinedProfile("...") No es necesario (se gestiona internamente)
engine.CreateLanguageParams() No es necesario
langParams.Languages.Add("French") ocr.Language = OcrLanguage.French
langParams.Languages.Add("English") + langParams.Languages.Add("German") ocr.Language = OcrLanguage.English + OcrLanguage.German
engine.CreateFRDocument() new OcrInput()
engine.CreateFRDocumentFromImage(path, null) ocr.Read(path)
document.AddImageFile(path, null, null) input.LoadImage(path)
imageInfo.LoadImageFile(tiff) + bucle frameCount input.LoadImageFrames(tiff)
engine.CreatePDFFile() luego pdfFile.Open(path, null, null) input.LoadPdf(path)
document.Process(null) ocr.Read(input)
document.PlainText.Text result.Text
frDocument.Pages[i].PlainText.Text result.Pages[i].Text
page.Layout.Blocks + verificación BlockTypeEnum.BT_Table result.Pages + datos de coordenadas de palabras
block.GetAsTableBlock() result.Pages[i].Lines (con coordenadas)
engine.CreatePDFExportParams() No es necesario
document.Export(path, FEF_PDF, params) result.SaveAsSearchablePdf(path)
document.Export(path, FEF_TextUnicodeDefaults, null) File.WriteAllText(path, result.Text)
engine.CreateDOCXExportParams() + Exportar No cuenta con soporte directo.
document.Close() Manejado por using en OcrInput
_engine.GetLicenseInfo().ExpirationDate IronOcr.License.IsValidLicense
Archivos de licencia (ABBYY.lic, ABBYY.key) IronOcr.License.LicenseKey = "key"
engine.CreateZone() + zone.SetBounds(x, y, w, h) new CropRectangle(x, y, width, height)

Problemas comunes de migración y soluciones

Problema 1: Errores de registro COM después de eliminar el SDK

ABBYY: Después de eliminar FREngine.dll de las referencias del proyecto, la construcción puede aún fallar con Could not load type 'FREngine.EngineLoader' o errores de interoperabilidad COM de clases que retuvieron el espacio de nombres antiguo.

Solución: Busque todos los usos de FREngine y ABBYY.FineReader antes de eliminar la referencia. Cualquier clase que implemente IDisposable específicamente para anular un campo IEngine necesita reemplazar su lógica de eliminación con bloques using en OcrInput:

// Before: explicit Close in finally
var document = _engine.CreateFRDocument();
try { document.Process(null); }
finally { document.Close(); }

// After: using pattern on OcrInput
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = new IronTesseract().Read(input);
// Before: explicit Close in finally
var document = _engine.CreateFRDocument();
try { document.Process(null); }
finally { document.Close(); }

// After: using pattern on OcrInput
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = new IronTesseract().Read(input);
Option Strict On



' Before: explicit Close in finally
Dim document = _engine.CreateFRDocument()
Try
    document.Process(Nothing)
Finally
    document.Close()
End Try

' After: using pattern on OcrInput
Using input As New OcrInput()
    input.LoadImage(imagePath)
    Dim result = New IronTesseract().Read(input)
End Using
$vbLabelText   $csharpLabel

Problema 2: El perfil de reconocimiento no tiene equivalente

ABBYY: El código que llama engine.LoadPredefinedProfile("DocumentConversion_Speed") o engine.LoadPredefinedProfile("FieldLevelRecognition") utiliza perfiles específicos de ABBYY para equilibrar precisión contra rendimiento. No hay una propiedad equivalente en IronOCR llamada Profile.

Solución:IronOCR expone los mismos compromisos a través de IronTesseract.Configuration. Para la optimización de velocidad, establezca ocr.Configuration.TesseractVersion = TesseractVersion.Tesseract5 (por defecto) y reduzca los filtros de preprocesamiento. Para obtener la máxima precisión, añada el proceso de preprocesamiento completo:

// Speed-optimized
var ocr = new IronTesseract();
// No preprocessing — fastest path
var result = ocr.Read("clean-document.jpg");

// Accuracy-optimized for difficult inputs
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("degraded-scan.jpg");
input.Deskew();
input.DeNoise();
input.Contrast();
input.Binarize();
var result = ocr.Read(input);
// Speed-optimized
var ocr = new IronTesseract();
// No preprocessing — fastest path
var result = ocr.Read("clean-document.jpg");

// Accuracy-optimized for difficult inputs
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("degraded-scan.jpg");
input.Deskew();
input.DeNoise();
input.Contrast();
input.Binarize();
var result = ocr.Read(input);
Imports IronTesseract

' Speed-optimized
Dim ocr As New IronTesseract()
' No preprocessing — fastest path
Dim result = ocr.Read("clean-document.jpg")

' Accuracy-optimized for difficult inputs
Dim ocr As New IronTesseract()
Using input As New OcrInput()
    input.LoadImage("degraded-scan.jpg")
    input.Deskew()
    input.DeNoise()
    input.Contrast()
    input.Binarize()
    Dim result = ocr.Read(input)
End Using
$vbLabelText   $csharpLabel

La guía de corrección de la calidad de imagen explica qué filtros solucionan qué problemas de calidad de entrada. La guía de optimización de velocidad abarca las propiedades de configuración que reducen el tiempo de procesamiento en documentos limpios.

Problema 3: El paso de implementación del archivo de licencia permanece en CI/CD.

ABBYY: Los pipelines de construcción típicamente contienen un paso que copia ABBYY.lic y ABBYY.key desde un almacén seguro al objetivo de despliegue. Tras la migración, a veces los equipos olvidan eliminar este paso, dejando código de implementación obsoleto que hace referencia a rutas que ya no existen.

Solución: Eliminar por completo el paso de copia del archivo de licencia. Sustitúyalo por un paso de inyección de variable de entorno:

# Remove these CI/CD steps after migration:
# - name: Copy ABBYY license files
#   run: |
#     cp $SECRETS_PATH/ABBYY.lic $DEPLOY_PATH/License/
#     cp $SECRETS_PATH/ABBYY.key $DEPLOY_PATH/License/

# Add this instead (environment variable injection):
# - name: Set IronOCR license
#   env:
#     IRONOCR_LICENSE_KEY: ${{secrets.IRONOCR_LICENSE}}
# Remove these CI/CD steps after migration:
# - name: Copy ABBYY license files
#   run: |
#     cp $SECRETS_PATH/ABBYY.lic $DEPLOY_PATH/License/
#     cp $SECRETS_PATH/ABBYY.key $DEPLOY_PATH/License/

# Add this instead (environment variable injection):
# - name: Set IronOCR license
#   env:
#     IRONOCR_LICENSE_KEY: ${{secrets.IRONOCR_LICENSE}}
YAML

Y al iniciar la aplicación:

IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
    ?? throw new InvalidOperationException("IRONOCR_LICENSE_KEY not set");
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
    ?? throw new InvalidOperationException("IRONOCR_LICENSE_KEY not set");
Imports System

IronOcr.License.LicenseKey = If(Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY"), Throw New InvalidOperationException("IRONOCR_LICENSE_KEY not set"))
$vbLabelText   $csharpLabel

Problema 4: El motor no es seguro para subprocesos: código de bloqueo existente.

ABBYY: Las aplicaciones que llaman a ABBYY desde múltiples threads típicamente contienen SemaphoreSlim, lock declaraciones, o instancias de motor locales al thread para evitar problemas de threading de COM. Este código de sincronización es específico del modelo de subprocesos de ABBYY.

Solución: Eliminar todo el código de sincronización que envuelve las llamadas a ABBYY. El IronTesseract de IronOCR es seguro para instanciar por thread:

// Remove all of this:
// private readonly SemaphoreSlim _engineLock = new SemaphoreSlim(1, 1);
// await _engineLock.WaitAsync();
// try { ... } finally { _engineLock.Release(); }

// Replace with:
Parallel.ForEach(documents, doc =>
{
    var ocr = new IronTesseract(); // One per thread — no lock needed
    results[doc.Id] = ocr.Read(doc.Path).Text;
});
// Remove all of this:
// private readonly SemaphoreSlim _engineLock = new SemaphoreSlim(1, 1);
// await _engineLock.WaitAsync();
// try { ... } finally { _engineLock.Release(); }

// Replace with:
Parallel.ForEach(documents, doc =>
{
    var ocr = new IronTesseract(); // One per thread — no lock needed
    results[doc.Id] = ocr.Read(doc.Path).Text;
});
Imports System.Threading.Tasks

Parallel.ForEach(documents, Sub(doc)
    Dim ocr = New IronTesseract() ' One per thread — no lock needed
    results(doc.Id) = ocr.Read(doc.Path).Text
End Sub)
$vbLabelText   $csharpLabel

Problema 5: Patrón CreateImageInfo / FrameCount para TIFF

ABBYY: El código que lee la cantidad de cuadros de archivos TIFF usando engine.CreateImageInfo() y imageInfo.LoadImageFile() antes de recorrer los cuadros no tiene equivalente directo en IronOCR porque OcrInput.LoadImageFrames maneja la enumeración de cuadros internamente.

Solución: Eliminar por completo el bucle de conteo de fotogramas:

// Remove:
// var imageInfo = engine.CreateImageInfo();
// imageInfo.LoadImageFile(tiffPath);
// for (int i = 0; i < imageInfo.FrameCount; i++) { document.AddImageFile(...) }

// Replace with:
using var input = new OcrInput();
input.LoadImageFrames("multi-page-scan.tiff");
var result = new IronTesseract().Read(input);
// result.Pages contains one entry per TIFF frame
// Remove:
// var imageInfo = engine.CreateImageInfo();
// imageInfo.LoadImageFile(tiffPath);
// for (int i = 0; i < imageInfo.FrameCount; i++) { document.AddImageFile(...) }

// Replace with:
using var input = new OcrInput();
input.LoadImageFrames("multi-page-scan.tiff");
var result = new IronTesseract().Read(input);
// result.Pages contains one entry per TIFF frame
Imports IronOcr

' Remove:
' Dim imageInfo = engine.CreateImageInfo()
' imageInfo.LoadImageFile(tiffPath)
' For i As Integer = 0 To imageInfo.FrameCount - 1
'     document.AddImageFile(...)
' Next

' Replace with:
Using input As New OcrInput()
    input.LoadImageFrames("multi-page-scan.tiff")
    Dim result = New IronTesseract().Read(input)
    ' result.Pages contains one entry per TIFF frame
End Using
$vbLabelText   $csharpLabel

Problema 6: La exportación a DOCX no tiene un equivalente directo.

ABBYY: document.Export(path, FileExportFormatEnum.FEF_DOCX, docxParams) produce un documento de Word.IronOCR no genera archivos DOCX directamente.

Solución:IronOCR genera archivos PDF con capacidad de búsqueda y datos de texto estructurados. Para los flujos de trabajo que requieren salida en formato DOCX, la ruta de migración práctica consiste en generar un PDF con capacidad de búsqueda y convertirlo posteriormente, o extraer texto estructurado y escribirlo en un DOCX utilizando una biblioteca como Open XML SDK:

//IronOCR to searchable PDF (closest equivalent)
var result = new IronTesseract().Read(inputPath);
result.SaveAsSearchablePdf(outputPath.Replace(".docx", ".pdf"));

// Or extract structured text for downstream DOCX generation
foreach (var paragraph in result.Paragraphs)
{
    Console.WriteLine(paragraph.Text);
    // Write to DOCX via Open XML SDK or similar
}
//IronOCR to searchable PDF (closest equivalent)
var result = new IronTesseract().Read(inputPath);
result.SaveAsSearchablePdf(outputPath.Replace(".docx", ".pdf"));

// Or extract structured text for downstream DOCX generation
foreach (var paragraph in result.Paragraphs)
{
    Console.WriteLine(paragraph.Text);
    // Write to DOCX via Open XML SDK or similar
}
Imports IronOcr

' IronOCR to searchable PDF (closest equivalent)
Dim result = New IronTesseract().Read(inputPath)
result.SaveAsSearchablePdf(outputPath.Replace(".docx", ".pdf"))

' Or extract structured text for downstream DOCX generation
For Each paragraph In result.Paragraphs
    Console.WriteLine(paragraph.Text)
    ' Write to DOCX via Open XML SDK or similar
Next
$vbLabelText   $csharpLabel

La guía de resultados de lectura abarca el acceso a párrafos, líneas, palabras y datos de coordenadas a nivel de caracteres para su posterior procesamiento.

Lista de verificación para la migración a ABBYY FineReader

Tareas previas a la migración

Revisa el código fuente antes de realizar cualquier cambio:

# Find all files using ABBYY namespaces
grep -r "using FREngine" --include="*.cs" .
grep -r "using ABBYY" --include="*.cs" .

# Find engine lifecycle patterns
grep -r "EngineLoader\|GetEngineObject\|LoadPredefinedProfile" --include="*.cs" .

# Find document lifecycle patterns
grep -r "CreateFRDocument\|document\.Close\|AddImageFile\|document\.Process" --include="*.cs" .

# Find language configuration
grep -r "CreateLanguageParams\|langParams\.Languages" --include="*.cs" .

# Find export calls
grep -r "FileExportFormatEnum\|CreatePDFExportParams\|document\.Export" --include="*.cs" .

# Find license file references in CI/CD and deployment scripts
grep -r "ABBYY\.lic\|ABBYY\.key" .
# Find all files using ABBYY namespaces
grep -r "using FREngine" --include="*.cs" .
grep -r "using ABBYY" --include="*.cs" .

# Find engine lifecycle patterns
grep -r "EngineLoader\|GetEngineObject\|LoadPredefinedProfile" --include="*.cs" .

# Find document lifecycle patterns
grep -r "CreateFRDocument\|document\.Close\|AddImageFile\|document\.Process" --include="*.cs" .

# Find language configuration
grep -r "CreateLanguageParams\|langParams\.Languages" --include="*.cs" .

# Find export calls
grep -r "FileExportFormatEnum\|CreatePDFExportParams\|document\.Export" --include="*.cs" .

# Find license file references in CI/CD and deployment scripts
grep -r "ABBYY\.lic\|ABBYY\.key" .
SHELL

Documente cada clase que tenga un campo IEngine o IFRDocument. Tenga en cuenta qué formatos de exportación se utilizan: la salida DOCX requiere un enfoque alternativo (consulte el punto 6 anterior).

Tareas de actualización de código

  1. Elimine la referencia FREngine.dll de todos los archivos .csproj
  2. Ejecute dotnet add package IronOcr en cada proyecto que usó ABBYY
  3. Agregue IronOcr.License.LicenseKey = ... al inicio de la aplicación (Program.cs o clase de inicio)
  4. Instale paquetes NuGet de lenguaje para cada idioma no inglés (dotnet add package IronOcr.Languages.French, etc.)
  5. Elimine todas las llamadas a EngineLoader, GetEngineObject y LoadPredefinedProfile
  6. Elimine todas las llamadas a CreateLanguageParams y langParams.Languages.Add
  7. Reemplace engine.CreateFRDocument() + document.AddImageFile() + document.Process() con new IronTesseract().Read(path)
  8. Reemplace los bucles de TIFF multiframe con input.LoadImageFrames(tiffPath)
  9. Reemplace document.PlainText.Text con result.Text
  10. Reemplace frDocument.Pages[i].PlainText.Text con result.Pages[i].Text
  11. Reemplace document.Export(..., FEF_PDF, pdfParams) con result.SaveAsSearchablePdf(path)
  12. Reemplace todas las llamadas a document.Close() con bloques using en OcrInput
  13. Elimine el código SemaphoreSlim y de bloqueo que serializaba el acceso al motor de ABBYY
  14. Reemplace engine.CreateZone() / zone.SetBounds() / page.Zones.Add() con new CropRectangle(x, y, width, height) pasado a input.LoadImage()
  15. Eliminar los pasos de copia de archivos de licencia de las canalizaciones de CI/CD.
  16. Actualice las imágenes de Docker — elimine la capa de instalación de SDK, agregue libgdiplus para objetivos de Linux

Pruebas posteriores a la migración

  • Verificar el resultado de la extracción de texto en una muestra representativa de cada tipo de documento (facturas, contratos, formularios escaneados).
  • Confirme que el procesamiento de TIFF de varias páginas devuelve el mismo número de páginas que los fotogramas producidos por ABBYY.
  • Pruebe los documentos multilingües con las mismas entradas utilizadas para la comparación de referencia de ABBYY.
  • Verificar que la salida PDF con capacidad de búsqueda permita realizar búsquedas de texto en Adobe Reader y en los visores de PDF del navegador.
  • Ejecute el procesador de lotes paralelo con el nivel de concurrencia de producción y confirme que no haya excepciones.
  • Verifique result.Confidence en documentos conocidos buenos para establecer un umbral de referencia para las puertas de calidad
  • Probar la inicialización de la clave de licencia desde una variable de entorno en el entorno de implementación de prueba.
  • Verificar que la imagen de Docker se compile y ejecute OCR sin el montaje de volumen del SDK de ABBYY
  • Confirmar que la canalización de CI/CD se complete sin el paso de copia del archivo de licencia.
  • Ejecute un perfilador de memoria en el procesador por lotes para confirmar que no hay objetos OcrInput con fugas (verifique la ubicación using)

Principales ventajas de migrar a IronOCR

La complejidad de la implementación se reduce drásticamente. Antes de que la aplicación se iniciara, cada implementación de ABBYY requería la instalación del SDK, la ubicación del archivo de licencia, la configuración de la ruta de ejecución y la validación de que los archivos estuvieran en las rutas correctas.IronOCR se implementa como una dependencia de NuGet . dotnet publish produce un artefacto autocontenido con el motor OCR incluido. La guía de implementación de Docker y la guía de configuración de Azure muestran la configuración completa; ambas caben en una sola página.

La interoperabilidad COM ha desaparecido. Eliminar la capa COM elimina una categoría completa de fallos en tiempo de ejecución: errores de registro COM en máquinas nuevas, desajustes de threading en el apartamento, errores de ciclo de vida RCW, y las 15-25 líneas de código boilerplate try/finally que cada llamada de procesamiento de documentos ABBYY requería. El código fuente se reduce. La superficie de error se reduce con ello.

El aumento del volumen de documentos ya no desencadena revisiones presupuestarias. La licencia perpetua de IronOCR cubre un volumen ilimitado de documentos. Una aplicación que procesa 10 000 documentos al mes durante el primer año y 2 000 000 al mes durante el tercer año tiene el mismo coste de licencia OCR. No hay contadores por página, ni facturas por exceso de consumo, ni renegociaciones de niveles de volumen. La página de licencias muestra todos los niveles: la licencia Professional , con un precio de 2999 dólares, cubre a diez desarrolladores que procesan cualquier volumen en cualquier número de destinos de implementación.

La implementación multiplataforma abre nuevas opciones de infraestructura. La capa COM de ABBYY requiere Windows. Los equipos que querían trasladar el procesamiento de documentos a contenedores Linux por motivos de coste o densidad se vieron bloqueados.IronOCR funciona de forma idéntica en Windows, Linux y macOS desde el mismo paquete NuGet . La migración desde ABBYY elimina la restricción de Windows en la capa de OCR de la pila de aplicaciones. La guía de implementación de Linux y la guía de implementación de AWS cubren la configuración completa para cada entorno.

El procesamiento paralelo está disponible sin necesidad de infraestructura adicional. Las estrategias de bloqueo que utilizaba el acceso serializado del motor ABBYY han desaparecido. Las instancias de IronTesseract son independientes: levante una por thread, ejecute Parallel.ForEach a través de un lote de documentos, obtenga resultados. El rendimiento se ajusta automáticamente según los núcleos de CPU disponibles, sin necesidad de código adicional. El ejemplo de multihilo demuestra mejoras en el rendimiento en hardware multinúcleo.

La configuración de idioma es una referencia de paquete. Agregar compatibilidad con OCR en alemán o japonés a una integración de ABBYY implicaba identificar los archivos de datos, implementarlos en las rutas de ejecución de cada máquina de destino y gestionar los fallos cuando faltaban archivos. Con IronOCR, dotnet add package IronOcr.Languages.German agrega el paquete de idioma como una dependencia NuGet versionada y reproducible. El gestor de paquetes garantiza que los datos estén presentes en cada compilación. La guía del paquete de idioma personalizado abarca la capacitación y la implementación de modelos de lenguaje personalizados para dominios especializados.

Por favor notaABBYY FineReader y Tesseract son marcas registradas de sus respectivos propietarios. Este sitio no está afiliado, respaldado ni patrocinado por ABBYY ni Google. 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 ABBYY FineReader Engine 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 ABBYY FineReader Engine a IronOCR?

Sustituya las secuencias de inicialización de ABBYY FineReader 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 ABBYY FineReader Engine 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 de idioma que ABBYY FineReader Engine 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 ABBYY FineReader Engine a IronOCR requiere cambios en la infraestructura de implementación?

IronOCR requiere menos cambios de infraestructura que ABBYY FineReader Engine. 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 ABBYY FineReader?

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 ABBYY FineReader Engine 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 ABBYY FineReader Engine 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