Saltar al pie de página
VIDEOS

Cómo extraer texto árabe de imágenes utilizando herramientas OCR

Esta guía acompaña a los desarrolladores de .NET a través de una migración completa de GdPicture .NET OCR a IronOCR . Abarca el intercambio de paquetes, los cambios de espacio de nombres y los patrones prácticos de código antes y después para cada flujo de trabajo principal de OCR, con especial atención a la eliminación del ciclo de vida del ID de imagen entero que define el modelo de gestión de recursos de GdPicture. No es necesario haber leído previamente el artículo comparativo.

¿Por qué migrar desde GdPicture .NET?

GdPicture .NET es una plataforma de procesamiento de imágenes de documentos diseñada para equipos que necesitan integración de escáneres, compatibilidad con DICOM, edición de PDF, anotaciones y OCR de un único proveedor. Cuando el reconocimiento óptico de caracteres (OCR) es el único requisito, los precios de la plataforma y la arquitectura de la API generan una fricción que se acumula con el tiempo.

Costo del complemento para un flujo de trabajo OCR básico. La extracción de texto de archivos PDF escaneados y la generación de resultados con capacidad de búsqueda requieren tres licencias independientes: el SDK principal (aproximadamente $4,000), el complemento OCR (aproximadamente $2,000) y el complemento PDF (aproximadamente $2,000). Esto representa un costo inicial de $8,000, más un 20 % de mantenimiento anual. Los equipos que solo necesitan OCR asumen la estructura de precios completa de una plataforma de procesamiento de imágenes de documentos.IronOCR cubre el mismo flujo de trabajo — OCR de imagen, OCR de PDF, preprocesamiento, salida de PDF buscable — desde un solo paquete en $999 hasta $2,999 perpetuo, sin decisiones de licencia por función. Consulte la página de licencias de IronOCR para obtener un desglose completo de los niveles.

El ciclo de vida del ID de imagen entero. Cada imagen cargada a través de GdPicture devuelve un int. Pasas ese entero a las operaciones de OCR, luego llamas a ReleaseGdPictureImage con él cuando terminas. Este patrón precede a IDisposable. Funciona si se sigue correctamente; Pierde memoria cuando se omite. En los servicios de producción que procesan cientos de documentos diariamente, una sola llamada de liberación fallida en una rama de error produce un aumento de memoria que resulta realmente difícil de diagnosticar. El OcrInput de IronOCR implementa IDisposable — una declaración using elimina toda la carga de limpieza.

Espacio de nombres específico de la versión. GdPicture embebe el número de versión principal en su espacio de nombres: using GdPicture14. Actualizar a la siguiente versión principal requiere actualizar esa directiva using en cada archivo fuente que referencia clases de GdPicture. Una aplicación de gran tamaño con OCR distribuido en docenas de servicios convierte esa tarea de búsqueda y reemplazo en una labor de varias horas que no aporta ninguna mejora funcional. El espacio de nombres de IronOCR ha sido IronOcr en todas las versiones principales.

Requisito de carpeta de recursos externos. GdPicture OCR requiere una propiedad ResourceFolder que apunte a un directorio de archivos de idioma .traineddata en tiempo de ejecución. Esa ruta funciona en una máquina de desarrollo, luego falla en servidores Linux, contenedores Docker y despliegues de Azure App Service donde la estructura de directorios no existe.IronOCR incluye soporte de idioma inglés dentro del paquete NuGet; Los idiomas adicionales se instalan como paquetes NuGet que se incluyen en el resultado de la compilación.

Inicialización de tres componentes. Un flujo de trabajo de OCR de PDF en GdPicture requiere instanciar GdPictureImaging, GdPictureOCR, y GdPicturePDF — tres componentes separados con requisitos de eliminación individuales — además del registro del gestor de licencias y la asignación de la carpeta de recursos.IronOCR requiere una línea al iniciar y una clase en el momento de la llamada.

No hay seguridad nativa para subprocesos. Las instancias de GdPicture OCR no son seguras para subprocesos. El procesamiento paralelo de documentos requiere una gestión y sincronización cuidadosas de las instancias para evitar estados corruptos.IronOCR está diseñado para uso concurrente: crea un IronTesseract por hilo, o comparte una sola instancia bajo carga — cualquiera de los patrones funciona sin código de sincronización adicional.

El problema fundamental

GdPicture asigna memoria para cada imagen como un identificador entero administrado en tiempo de ejecución. Cada llamada RenderPageToGdPictureImage en un bucle de procesamiento TIFF crea una nueva asignación que debe liberarse manualmente:

// GdPicture: every frame = new integer ID = manual release required
using var pdf = new GdPicturePDF();
pdf.LoadFromFile("multi-page.tiff", false);

var frameIds = new List<int>();
try
{
    for (int i = 1; i <= pdf.GetPageCount(); i++)
    {
        pdf.SelectPage(i);
        int frameId = pdf.RenderPageToGdPictureImage(200, false); // new allocation
        if (frameId != 0) frameIds.Add(frameId);
        // ... OCR call here ...
    }
}
finally
{
    foreach (var id in frameIds) _imaging.ReleaseGdPictureImage(id); // manual per-frame release
}
// GdPicture: every frame = new integer ID = manual release required
using var pdf = new GdPicturePDF();
pdf.LoadFromFile("multi-page.tiff", false);

var frameIds = new List<int>();
try
{
    for (int i = 1; i <= pdf.GetPageCount(); i++)
    {
        pdf.SelectPage(i);
        int frameId = pdf.RenderPageToGdPictureImage(200, false); // new allocation
        if (frameId != 0) frameIds.Add(frameId);
        // ... OCR call here ...
    }
}
finally
{
    foreach (var id in frameIds) _imaging.ReleaseGdPictureImage(id); // manual per-frame release
}
Imports System.Collections.Generic

' GdPicture: every frame = new integer ID = manual release required
Using pdf As New GdPicturePDF()
    pdf.LoadFromFile("multi-page.tiff", False)

    Dim frameIds As New List(Of Integer)()
    Try
        For i As Integer = 1 To pdf.GetPageCount()
            pdf.SelectPage(i)
            Dim frameId As Integer = pdf.RenderPageToGdPictureImage(200, False) ' new allocation
            If frameId <> 0 Then frameIds.Add(frameId)
            ' ... OCR call here ...
        Next
    Finally
        For Each id In frameIds
            _imaging.ReleaseGdPictureImage(id) ' manual per-frame release
        Next
    End Try
End Using
$vbLabelText   $csharpLabel

IronOCR elimina por completo el ciclo de vida. OcrInput maneja la enumeración de marcos y la limpieza:

// IronOCR: the using statement handles everything
using var input = new OcrInput();
input.LoadImageFrames("multi-page.tiff");
var result = new IronTesseract().Read(input);
// IronOCR: the using statement handles everything
using var input = new OcrInput();
input.LoadImageFrames("multi-page.tiff");
var result = new IronTesseract().Read(input);
Imports IronOcr

Dim input As New OcrInput()
Using input
    input.LoadImageFrames("multi-page.tiff")
    Dim result = New IronTesseract().Read(input)
End Using
$vbLabelText   $csharpLabel

IronOCR vs GdPicture .NET: Comparación de características

La tabla que aparece a continuación muestra la cobertura de funciones entre las dos bibliotecas para flujos de trabajo centrados en OCR.

Característica GdPicture.NET IronOCR
Instalación Múltiples paquetes NuGet dotnet add package IronOcr
Activación de licencia LicenseManager.RegisterKEY() IronOcr.License.LicenseKey = "..."
Espacio de nombres en actualización importante Requiere buscar y reemplazar (GdPicture14 → siguiente) Sin cambios (IronOcr)
Inicialización de componentes GdPictureImaging + GdPictureOCR + GdPicturePDF new IronTesseract()
modelo de memoria de recursos Seguimiento y liberación manual de ID enteros IDisposable / declaración using
riesgo de fuga de memoria Alto (falta ReleaseGdPictureImage) Ninguno (aplicado por el compilador a través de using)
Carpeta de recursos externos Requerido (_ocr.ResourceFolder = path) No es necesario: viene incluido en el paquete.
OCR de imágenes
OCR de PDF Sí (se requiere el complemento PDF) Integrado, sin necesidad de licencia adicional.
Archivo TIFF de varias páginas Bucle de fotogramas manual + limpieza de ID por fotograma input.LoadImageFrames()
Entrada de flujo A través de la sobrecarga de flujo de GdPictureImaging input.LoadImage(stream)
Salida en PDF con capacidad de búsqueda pdf.OcrPage() + pdf.SaveToFile() result.SaveAsSearchablePdf()
Preprocesamiento de corrección de inclinación Se requiere el complemento de procesamiento de imágenes de documentos. input.Deskew() — integrado
Eliminación de ruido Se requiere el complemento de procesamiento de imágenes de documentos. input.DeNoise() — integrado
Idiomas Archivos de datos entrenados basados ​​en Tesseract en la carpeta Más de 125 paquetes NuGet
OCR multilingüe OcrLanguage.French + OcrLanguage.German
Seguridad de los hilos Gestión manual de instancias Thread-safe por diseño
OCR asíncrono No integrado ReadAsync()
Lectura de BarCodes Complemento de código de barras independiente ocr.Configuration.ReadBarCodes = true
Salida estructurada Acceso a bloques/líneas/palabras basado en índices Tipado .Pages, .Words, .Characters
Puntuación de confianza GetOCRResultConfidence(resultId) result.Confidence
Multiplataforma Windows, Linux, macOS Windows, Linux, macOS, Docker, AWS, Azure
Coste de entrada (OCR a partir de archivos PDF) Aproximadamente 8.000 dólares (Función principal + OCR + complementos PDF) $999–$2,999
Modelo de precios Licencia perpetua basada en plugins + 20 %/año de mantenimiento Plan plano perpetuo, actualizaciones anuales opcionales
Soporte comercial

Inicio rápido: Migración de GdPicture .NET a IronOCR

Paso 1: Sustituir el paquete NuGet

Elimine los paquetes GdPicture:

dotnet remove package GdPicture.NET
dotnet remove package GdPicture.NET.OCR
dotnet remove package GdPicture.NET.PDF
dotnet remove package GdPicture.NET
dotnet remove package GdPicture.NET.OCR
dotnet remove package GdPicture.NET.PDF
SHELL

Instale IronOCR desde la página del paquete NuGet :

dotnet add package IronOcr

Para idiomas distintos del inglés, instale el paquete de idioma correspondiente:

dotnet add package IronOcr.Languages.French, IronOcr.Languages.German

Paso 2: Actualizar los espacios de nombres

Reemplace el espacio de nombres GdPicture con el espacio de nombres IronOCR:

// Before (GdPicture)
using GdPicture14;

// After (IronOCR)
using IronOcr;
// Before (GdPicture)
using GdPicture14;

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

Paso 3: Inicializar licencia

Coloca esta línea en Program.cs o Startup.cs, antes de cualquier llamada al OCR:

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

Elimine por completo el bloqueo de registro de licencia de GdPicture:

// Remove this
LicenseManager lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
// Remove this
LicenseManager lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
$vbLabelText   $csharpLabel

En la página del producto IronOCR podrá descargar una clave de prueba gratuita para evaluarlo antes de comprarlo.

Ejemplos de migración de código

Procesamiento de fotogramas TIFF de varias páginas

El procesamiento de archivos TIFF de varias páginas en GdPicture requiere seleccionar cada fotograma a través de la API de PDF/imágenes, convertirlo en un nuevo ID de imagen, ejecutar el OCR y liberar el ID. Un solo marco que no se libera en el bloque finally pierde de 10 a 50 MB dependiendo del DPI.

Enfoque de GdPicture .NET :

using GdPicture14;

public class GdPictureTiffProcessor
{
    private readonly GdPictureImaging _imaging;
    private readonly GdPictureOCR _ocr;

    public string ExtractTextFromTiff(string tiffPath)
    {
        var text = new StringBuilder();

        // Load TIFF through the imaging component
        int tiffId = _imaging.CreateGdPictureImageFromFile(tiffPath);

        if (tiffId == 0)
            throw new Exception($"TIFF load failed: {_imaging.GetStat()}");

        // Outer try: release the original TIFF handle
        try
        {
            int frameCount = _imaging.GetPageCount(tiffId);

            for (int i = 1; i <= frameCount; i++)
            {
                // Switch to frame — modifies the existing ID in place
                _imaging.SelectPage(tiffId, i);

                // Clone frame to a new image ID for OCR
                int frameId = _imaging.CloneImage(tiffId);

                if (frameId == 0) continue;

                // Inner try: release each cloned frame ID
                try
                {
                    _ocr.SetImage(frameId);
                    _ocr.Language = "eng";

                    string resultId = _ocr.RunOCR();

                    if (!string.IsNullOrEmpty(resultId))
                    {
                        text.AppendLine($"[Frame {i}] {_ocr.GetOCRResultText(resultId)}");
                    }
                }
                finally
                {
                    // Release cloned frame — critical for each iteration
                    _imaging.ReleaseGdPictureImage(frameId);
                }
            }
        }
        finally
        {
            // Release original TIFF handle
            _imaging.ReleaseGdPictureImage(tiffId);
        }

        return text.ToString();
    }
}
using GdPicture14;

public class GdPictureTiffProcessor
{
    private readonly GdPictureImaging _imaging;
    private readonly GdPictureOCR _ocr;

    public string ExtractTextFromTiff(string tiffPath)
    {
        var text = new StringBuilder();

        // Load TIFF through the imaging component
        int tiffId = _imaging.CreateGdPictureImageFromFile(tiffPath);

        if (tiffId == 0)
            throw new Exception($"TIFF load failed: {_imaging.GetStat()}");

        // Outer try: release the original TIFF handle
        try
        {
            int frameCount = _imaging.GetPageCount(tiffId);

            for (int i = 1; i <= frameCount; i++)
            {
                // Switch to frame — modifies the existing ID in place
                _imaging.SelectPage(tiffId, i);

                // Clone frame to a new image ID for OCR
                int frameId = _imaging.CloneImage(tiffId);

                if (frameId == 0) continue;

                // Inner try: release each cloned frame ID
                try
                {
                    _ocr.SetImage(frameId);
                    _ocr.Language = "eng";

                    string resultId = _ocr.RunOCR();

                    if (!string.IsNullOrEmpty(resultId))
                    {
                        text.AppendLine($"[Frame {i}] {_ocr.GetOCRResultText(resultId)}");
                    }
                }
                finally
                {
                    // Release cloned frame — critical for each iteration
                    _imaging.ReleaseGdPictureImage(frameId);
                }
            }
        }
        finally
        {
            // Release original TIFF handle
            _imaging.ReleaseGdPictureImage(tiffId);
        }

        return text.ToString();
    }
}
Imports GdPicture14
Imports System.Text

Public Class GdPictureTiffProcessor
    Private ReadOnly _imaging As GdPictureImaging
    Private ReadOnly _ocr As GdPictureOCR

    Public Function ExtractTextFromTiff(tiffPath As String) As String
        Dim text As New StringBuilder()

        ' Load TIFF through the imaging component
        Dim tiffId As Integer = _imaging.CreateGdPictureImageFromFile(tiffPath)

        If tiffId = 0 Then
            Throw New Exception($"TIFF load failed: {_imaging.GetStat()}")
        End If

        ' Outer try: release the original TIFF handle
        Try
            Dim frameCount As Integer = _imaging.GetPageCount(tiffId)

            For i As Integer = 1 To frameCount
                ' Switch to frame — modifies the existing ID in place
                _imaging.SelectPage(tiffId, i)

                ' Clone frame to a new image ID for OCR
                Dim frameId As Integer = _imaging.CloneImage(tiffId)

                If frameId = 0 Then Continue For

                ' Inner try: release each cloned frame ID
                Try
                    _ocr.SetImage(frameId)
                    _ocr.Language = "eng"

                    Dim resultId As String = _ocr.RunOCR()

                    If Not String.IsNullOrEmpty(resultId) Then
                        text.AppendLine($"[Frame {i}] {_ocr.GetOCRResultText(resultId)}")
                    End If
                Finally
                    ' Release cloned frame — critical for each iteration
                    _imaging.ReleaseGdPictureImage(frameId)
                End Try
            Next
        Finally
            ' Release original TIFF handle
            _imaging.ReleaseGdPictureImage(tiffId)
        End Try

        Return text.ToString()
    End Function
End Class
$vbLabelText   $csharpLabel

Enfoque IronOCR:

using IronOcr;

public class IronOcrTiffProcessor
{
    public string ExtractTextFromTiff(string tiffPath)
    {
        using var input = new OcrInput();
        input.LoadImageFrames(tiffPath);  // all frames loaded, all cleanup automatic

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

        // Per-frame text is available on result.Pages
        foreach (var page in result.Pages)
            Console.WriteLine($"[Frame {page.PageNumber}] {page.Text}");

        return result.Text;
    }
}
using IronOcr;

public class IronOcrTiffProcessor
{
    public string ExtractTextFromTiff(string tiffPath)
    {
        using var input = new OcrInput();
        input.LoadImageFrames(tiffPath);  // all frames loaded, all cleanup automatic

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

        // Per-frame text is available on result.Pages
        foreach (var page in result.Pages)
            Console.WriteLine($"[Frame {page.PageNumber}] {page.Text}");

        return result.Text;
    }
}
Imports IronOcr

Public Class IronOcrTiffProcessor
    Public Function ExtractTextFromTiff(tiffPath As String) As String
        Using input As New OcrInput()
            input.LoadImageFrames(tiffPath) ' all frames loaded, all cleanup automatic

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

            ' Per-frame text is available on result.Pages
            For Each page In result.Pages
                Console.WriteLine($"[Frame {page.PageNumber}] {page.Text}")
            Next

            Return result.Text
        End Using
    End Function
End Class
$vbLabelText   $csharpLabel

LoadImageFrames carga cada marco de un TIFF de varias páginas en el pipeline OcrInput. El bloque using maneja toda la memoria asociada con cada marco al final del alcance. No hay List<int> para mantener, no se requieren bloques try/finally anidados. La guía de entrada de archivos TIFF y GIF cubre en detalle la selección de fotogramas y el manejo de múltiples formatos.

Inicialización de complementos que reemplazan la entrada basada en flujo

Las aplicaciones de servidor suelen recibir documentos como flujos de datos en lugar de rutas de archivo, ya sea mediante cargas HTTP, colas de mensajes o blobs de bases de datos. GdPicture requiere cargar el flujo a través de GdPictureImaging, que a su vez requiere la secuencia de inicialización de componentes y la configuración de la carpeta de recursos que preceden a cada operación.

Enfoque de GdPicture .NET :

using GdPicture14;

public class GdPictureStreamOcr : IDisposable
{
    private readonly GdPictureImaging _imaging;
    private readonly GdPictureOCR _ocr;

    public GdPictureStreamOcr()
    {
        // Plugin initialization required before stream loading is possible
        var lm = new LicenseManager();
        lm.RegisterKEY("GDPICTURE-LICENSE-KEY");

        _imaging = new GdPictureImaging();
        _ocr = new GdPictureOCR();
        _ocr.ResourceFolder = @"C:\GdPicture\Resources\OCR"; // path must exist at runtime
    }

    public string ExtractTextFromStream(Stream documentStream)
    {
        // Load stream into imaging component to get an image ID
        int imageId = _imaging.CreateGdPictureImageFromStream(documentStream);

        if (imageId == 0)
            throw new Exception($"Stream load failed: {_imaging.GetStat()}");

        try
        {
            _ocr.SetImage(imageId);
            _ocr.Language = "eng";

            string resultId = _ocr.RunOCR();

            if (string.IsNullOrEmpty(resultId))
                throw new Exception($"OCR failed: {_ocr.GetStat()}");

            return _ocr.GetOCRResultText(resultId);
        }
        finally
        {
            _imaging.ReleaseGdPictureImage(imageId);
        }
    }

    public void Dispose()
    {
        _ocr?.Dispose();
        _imaging?.Dispose();
    }
}
using GdPicture14;

public class GdPictureStreamOcr : IDisposable
{
    private readonly GdPictureImaging _imaging;
    private readonly GdPictureOCR _ocr;

    public GdPictureStreamOcr()
    {
        // Plugin initialization required before stream loading is possible
        var lm = new LicenseManager();
        lm.RegisterKEY("GDPICTURE-LICENSE-KEY");

        _imaging = new GdPictureImaging();
        _ocr = new GdPictureOCR();
        _ocr.ResourceFolder = @"C:\GdPicture\Resources\OCR"; // path must exist at runtime
    }

    public string ExtractTextFromStream(Stream documentStream)
    {
        // Load stream into imaging component to get an image ID
        int imageId = _imaging.CreateGdPictureImageFromStream(documentStream);

        if (imageId == 0)
            throw new Exception($"Stream load failed: {_imaging.GetStat()}");

        try
        {
            _ocr.SetImage(imageId);
            _ocr.Language = "eng";

            string resultId = _ocr.RunOCR();

            if (string.IsNullOrEmpty(resultId))
                throw new Exception($"OCR failed: {_ocr.GetStat()}");

            return _ocr.GetOCRResultText(resultId);
        }
        finally
        {
            _imaging.ReleaseGdPictureImage(imageId);
        }
    }

    public void Dispose()
    {
        _ocr?.Dispose();
        _imaging?.Dispose();
    }
}
IRON VB CONVERTER ERROR developers@ironsoftware.com
$vbLabelText   $csharpLabel

Enfoque IronOCR:

using IronOcr;

public class IronOcrStreamOcr
{
    public string ExtractTextFromStream(Stream documentStream)
    {
        using var input = new OcrInput();
        input.LoadImage(documentStream);  // stream accepted directly — no imaging component

        return new IronTesseract().Read(input).Text;
    }
}
using IronOcr;

public class IronOcrStreamOcr
{
    public string ExtractTextFromStream(Stream documentStream)
    {
        using var input = new OcrInput();
        input.LoadImage(documentStream);  // stream accepted directly — no imaging component

        return new IronTesseract().Read(input).Text;
    }
}
Imports IronOcr

Public Class IronOcrStreamOcr
    Public Function ExtractTextFromStream(documentStream As Stream) As String
        Using input As New OcrInput()
            input.LoadImage(documentStream) ' stream accepted directly — no imaging component

            Return New IronTesseract().Read(input).Text
        End Using
    End Function
End Class
$vbLabelText   $csharpLabel

El método GdPicture requiere la construcción de tres objetos y la configuración de una ruta del sistema de archivos antes de que se pueda consumir el primer flujo.IronOCR acepta el flujo directamente en OcrInput sin configuración previa. La guía de entrada de flujo cubre arrays de bytes, MemoryStream, y patrones FileStream para arquitecturas de canalización donde las escrituras de archivos temporales son indeseables.

Reemplazo del sondeo del código de estado por OCR asíncrono

El OCR de GdPicture es síncrono. Las aplicaciones que procesan documentos en puntos de acceso de ASP.NET Core o servicios en segundo plano deben encerrar RunOCR en Task.Run para evitar bloquear hilos de solicitud, y luego gestionar el ciclo de vida del ID de la imagen a través del límite del hilo.IronOCR proporciona soporte asíncrono de primera clase a través de ReadAsync.

Enfoque de GdPicture .NET :

using GdPicture14;
using System.Threading.Tasks;

public class GdPictureAsyncWrapper
{
    private readonly GdPictureImaging _imaging;
    private readonly GdPictureOCR _ocr;
    private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);

    public async Task<string> ExtractTextAsync(string imagePath)
    {
        // Must acquire lock: GdPictureOCR is not thread-safe
        await _lock.WaitAsync();

        try
        {
            return await Task.Run(() =>
            {
                int imageId = _imaging.CreateGdPictureImageFromFile(imagePath);

                if (imageId == 0)
                    throw new Exception($"Load failed: {_imaging.GetStat()}");

                try
                {
                    _ocr.SetImage(imageId);
                    _ocr.Language = "eng";

                    string resultId = _ocr.RunOCR();

                    if (string.IsNullOrEmpty(resultId))
                        throw new Exception($"OCR failed: {_ocr.GetStat()}");

                    return _ocr.GetOCRResultText(resultId);
                }
                finally
                {
                    _imaging.ReleaseGdPictureImage(imageId);
                }
            });
        }
        finally
        {
            _lock.Release();
        }
    }
}
using GdPicture14;
using System.Threading.Tasks;

public class GdPictureAsyncWrapper
{
    private readonly GdPictureImaging _imaging;
    private readonly GdPictureOCR _ocr;
    private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);

    public async Task<string> ExtractTextAsync(string imagePath)
    {
        // Must acquire lock: GdPictureOCR is not thread-safe
        await _lock.WaitAsync();

        try
        {
            return await Task.Run(() =>
            {
                int imageId = _imaging.CreateGdPictureImageFromFile(imagePath);

                if (imageId == 0)
                    throw new Exception($"Load failed: {_imaging.GetStat()}");

                try
                {
                    _ocr.SetImage(imageId);
                    _ocr.Language = "eng";

                    string resultId = _ocr.RunOCR();

                    if (string.IsNullOrEmpty(resultId))
                        throw new Exception($"OCR failed: {_ocr.GetStat()}");

                    return _ocr.GetOCRResultText(resultId);
                }
                finally
                {
                    _imaging.ReleaseGdPictureImage(imageId);
                }
            });
        }
        finally
        {
            _lock.Release();
        }
    }
}
Imports GdPicture14
Imports System.Threading.Tasks

Public Class GdPictureAsyncWrapper
    Private ReadOnly _imaging As GdPictureImaging
    Private ReadOnly _ocr As GdPictureOCR
    Private ReadOnly _lock As New SemaphoreSlim(1, 1)

    Public Async Function ExtractTextAsync(imagePath As String) As Task(Of String)
        ' Must acquire lock: GdPictureOCR is not thread-safe
        Await _lock.WaitAsync()

        Try
            Return Await Task.Run(Function()
                                      Dim imageId As Integer = _imaging.CreateGdPictureImageFromFile(imagePath)

                                      If imageId = 0 Then
                                          Throw New Exception($"Load failed: {_imaging.GetStat()}")
                                      End If

                                      Try
                                          _ocr.SetImage(imageId)
                                          _ocr.Language = "eng"

                                          Dim resultId As String = _ocr.RunOCR()

                                          If String.IsNullOrEmpty(resultId) Then
                                              Throw New Exception($"OCR failed: {_ocr.GetStat()}")
                                          End If

                                          Return _ocr.GetOCRResultText(resultId)
                                      Finally
                                          _imaging.ReleaseGdPictureImage(imageId)
                                      End Try
                                  End Function)
        Finally
            _lock.Release()
        End Try
    End Function
End Class
$vbLabelText   $csharpLabel

Enfoque IronOCR:

using IronOcr;

public class IronOcrAsyncService
{
    private readonly IronTesseract _ocr = new IronTesseract();

    public async Task<string> ExtractTextAsync(string imagePath)
    {
        // ReadAsync is natively async — no Task.Run wrapper, no lock required
        var result = await _ocr.ReadAsync(imagePath);
        return result.Text;
    }

    public async Task<string> ExtractFromStreamAsync(Stream stream)
    {
        using var input = new OcrInput();
        input.LoadImage(stream);

        var result = await _ocr.ReadAsync(input);
        return result.Text;
    }
}
using IronOcr;

public class IronOcrAsyncService
{
    private readonly IronTesseract _ocr = new IronTesseract();

    public async Task<string> ExtractTextAsync(string imagePath)
    {
        // ReadAsync is natively async — no Task.Run wrapper, no lock required
        var result = await _ocr.ReadAsync(imagePath);
        return result.Text;
    }

    public async Task<string> ExtractFromStreamAsync(Stream stream)
    {
        using var input = new OcrInput();
        input.LoadImage(stream);

        var result = await _ocr.ReadAsync(input);
        return result.Text;
    }
}
Imports IronOcr
Imports System.IO
Imports System.Threading.Tasks

Public Class IronOcrAsyncService
    Private ReadOnly _ocr As New IronTesseract()

    Public Async Function ExtractTextAsync(imagePath As String) As Task(Of String)
        ' ReadAsync is natively async — no Task.Run wrapper, no lock required
        Dim result = Await _ocr.ReadAsync(imagePath)
        Return result.Text
    End Function

    Public Async Function ExtractFromStreamAsync(stream As Stream) As Task(Of String)
        Using input As New OcrInput()
            input.LoadImage(stream)

            Dim result = Await _ocr.ReadAsync(input)
            Return result.Text
        End Using
    End Function
End Class
$vbLabelText   $csharpLabel

El enfoque de GdPicture requiere un SemaphoreSlim para serializar el acceso a la instancia compartida GdPictureOCR, además de un Task.Run para mover el trabajo de bloqueo sincrónico fuera del hilo llamante, además del ciclo de vida completo del ID de imagen dentro de esa lambda. La ReadAsync de IronOCR es genuinamente no bloqueante y segura para hilos. La guía de OCR asíncrono abarca la integración con el middleware de ASP.NET Core y los servicios en segundo plano alojados.

Procesamiento por lotes en paralelo con seguridad de hilos

Para procesar una carpeta de documentos escaneados lo más rápido posible, se requiere una ejecución en paralelo. GdPicture requiere una instancia GdPictureOCR por hilo — compartir una sola instancia provoca fallos no deterministas. Cada instancia por hilo también requiere su propio componente GdPictureImaging y configuración de carpeta de recursos, lo que hace impracticable el enfoque de pool de hilos sin un patrón de fabricación.

Enfoque de GdPicture .NET :

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

public class GdPictureParallelBatch
{
    private readonly string _resourceFolder = @"C:\GdPicture\Resources\OCR";

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

        // Each thread must have its own component instances
        Parallel.ForEach(imagePaths, imagePath =>
        {
            // Create per-thread instances — shared instances cause failures
            using var threadImaging = new GdPictureImaging();
            using var threadOcr = new GdPictureOCR();
            threadOcr.ResourceFolder = _resourceFolder;

            // Re-register license per thread (may be required depending on SDK version)
            var lm = new LicenseManager();
            lm.RegisterKEY("GDPICTURE-LICENSE-KEY");

            int imageId = threadImaging.CreateGdPictureImageFromFile(imagePath);

            if (imageId == 0)
            {
                results[imagePath] = $"ERROR: {threadImaging.GetStat()}";
                return;
            }

            try
            {
                threadOcr.SetImage(imageId);
                threadOcr.Language = "eng";

                string resultId = threadOcr.RunOCR();
                results[imagePath] = string.IsNullOrEmpty(resultId)
                    ? $"ERROR: {threadOcr.GetStat()}"
                    : threadOcr.GetOCRResultText(resultId);
            }
            finally
            {
                threadImaging.ReleaseGdPictureImage(imageId);
            }
        });

        return results;
    }
}
using GdPicture14;
using System.Collections.Concurrent;
using System.Threading.Tasks;

public class GdPictureParallelBatch
{
    private readonly string _resourceFolder = @"C:\GdPicture\Resources\OCR";

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

        // Each thread must have its own component instances
        Parallel.ForEach(imagePaths, imagePath =>
        {
            // Create per-thread instances — shared instances cause failures
            using var threadImaging = new GdPictureImaging();
            using var threadOcr = new GdPictureOCR();
            threadOcr.ResourceFolder = _resourceFolder;

            // Re-register license per thread (may be required depending on SDK version)
            var lm = new LicenseManager();
            lm.RegisterKEY("GDPICTURE-LICENSE-KEY");

            int imageId = threadImaging.CreateGdPictureImageFromFile(imagePath);

            if (imageId == 0)
            {
                results[imagePath] = $"ERROR: {threadImaging.GetStat()}";
                return;
            }

            try
            {
                threadOcr.SetImage(imageId);
                threadOcr.Language = "eng";

                string resultId = threadOcr.RunOCR();
                results[imagePath] = string.IsNullOrEmpty(resultId)
                    ? $"ERROR: {threadOcr.GetStat()}"
                    : threadOcr.GetOCRResultText(resultId);
            }
            finally
            {
                threadImaging.ReleaseGdPictureImage(imageId);
            }
        });

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

Public Class GdPictureParallelBatch
    Private ReadOnly _resourceFolder As String = "C:\GdPicture\Resources\OCR"

    Public Function ProcessBatch(imagePaths As String()) As ConcurrentDictionary(Of String, String)
        Dim results As New ConcurrentDictionary(Of String, String)()

        ' Each thread must have its own component instances
        Parallel.ForEach(imagePaths, Sub(imagePath)
            ' Create per-thread instances — shared instances cause failures
            Using threadImaging As New GdPictureImaging()
                Using threadOcr As New GdPictureOCR()
                    threadOcr.ResourceFolder = _resourceFolder

                    ' Re-register license per thread (may be required depending on SDK version)
                    Dim lm As New LicenseManager()
                    lm.RegisterKEY("GDPICTURE-LICENSE-KEY")

                    Dim imageId As Integer = threadImaging.CreateGdPictureImageFromFile(imagePath)

                    If imageId = 0 Then
                        results(imagePath) = $"ERROR: {threadImaging.GetStat()}"
                        Return
                    End If

                    Try
                        threadOcr.SetImage(imageId)
                        threadOcr.Language = "eng"

                        Dim resultId As String = threadOcr.RunOCR()
                        results(imagePath) = If(String.IsNullOrEmpty(resultId), $"ERROR: {threadOcr.GetStat()}", threadOcr.GetOCRResultText(resultId))
                    Finally
                        threadImaging.ReleaseGdPictureImage(imageId)
                    End Try
                End Using
            End Using
        End Sub)

        Return results
    End Function
End Class
$vbLabelText   $csharpLabel

Enfoque IronOCR:

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

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

        // IronTesseract is thread-safe — one instance handles all threads
        var ocr = new IronTesseract();

        Parallel.ForEach(imagePaths, imagePath =>
        {
            try
            {
                results[imagePath] = ocr.Read(imagePath).Text;
            }
            catch (Exception ex)
            {
                results[imagePath] = $"ERROR: {ex.Message}";
            }
        });

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

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

        // IronTesseract is thread-safe — one instance handles all threads
        var ocr = new IronTesseract();

        Parallel.ForEach(imagePaths, imagePath =>
        {
            try
            {
                results[imagePath] = ocr.Read(imagePath).Text;
            }
            catch (Exception ex)
            {
                results[imagePath] = $"ERROR: {ex.Message}";
            }
        });

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

Public Class IronOcrParallelBatch
    Public Function ProcessBatch(imagePaths As String()) As ConcurrentDictionary(Of String, String)
        Dim results As New ConcurrentDictionary(Of String, String)()

        ' IronTesseract is thread-safe — one instance handles all threads
        Dim ocr As New IronTesseract()

        Parallel.ForEach(imagePaths, Sub(imagePath)
            Try
                results(imagePath) = ocr.Read(imagePath).Text
            Catch ex As Exception
                results(imagePath) = $"ERROR: {ex.Message}"
            End Try
        End Sub)

        Return results
    End Function
End Class
$vbLabelText   $csharpLabel

La implementación paralela de GdPicture crea tres objetos por hilo y requiere el registro de una licencia por hilo.IronOCR maneja lecturas concurrentes de una sola instancia IronTesseract sin sobrecarga de sincronización. El ejemplo multihilo muestra los benchmarks de rendimiento y patrones Parallel.ForEach para cargas de trabajo de procesamiento de documentos de alto volumen.

Entrada de matriz de bytes y extracción de párrafos estructurados

Las aplicaciones que recuperan documentos de bases de datos o almacenamiento de objetos suelen trabajar con matrices de bytes en lugar de rutas de archivo. GdPicture requiere convertir el array de bytes en un flujo y cargarlo a través de GdPictureImaging. Para extraer datos estructurados a nivel de párrafo del resultado, es necesario navegar por la jerarquía del índice de bloques/líneas.

Enfoque de GdPicture .NET :

using GdPicture14;

public class GdPictureByteArrayOcr
{
    private readonly GdPictureImaging _imaging;
    private readonly GdPictureOCR _ocr;

    public List<string> ExtractParagraphsFromBytes(byte[] imageBytes)
    {
        var paragraphs = new List<string>();

        // Byte array must go through MemoryStream to reach CreateGdPictureImageFromStream
        using var ms = new MemoryStream(imageBytes);
        int imageId = _imaging.CreateGdPictureImageFromStream(ms);

        if (imageId == 0)
            throw new Exception($"Byte array load failed: {_imaging.GetStat()}");

        try
        {
            _ocr.SetImage(imageId);
            _ocr.Language = "eng";

            string resultId = _ocr.RunOCR();

            if (string.IsNullOrEmpty(resultId))
                throw new Exception($"OCR failed: {_ocr.GetStat()}");

            // Paragraph-level data requires iterating block structure
            int blockCount = _ocr.GetOCRResultBlockCount(resultId);

            for (int b = 0; b < blockCount; b++)
            {
                var blockText = new StringBuilder();
                int lineCount = _ocr.GetOCRResultBlockLineCount(resultId, b);

                for (int l = 0; l < lineCount; l++)
                {
                    int wordCount = _ocr.GetOCRResultBlockLineWordCount(resultId, b, l);

                    for (int w = 0; w < wordCount; w++)
                    {
                        blockText.Append(_ocr.GetOCRResultBlockLineWordText(resultId, b, l, w));
                        blockText.Append(" ");
                    }
                }

                string text = blockText.ToString().Trim();
                if (!string.IsNullOrEmpty(text))
                    paragraphs.Add(text);
            }
        }
        finally
        {
            _imaging.ReleaseGdPictureImage(imageId);
        }

        return paragraphs;
    }
}
using GdPicture14;

public class GdPictureByteArrayOcr
{
    private readonly GdPictureImaging _imaging;
    private readonly GdPictureOCR _ocr;

    public List<string> ExtractParagraphsFromBytes(byte[] imageBytes)
    {
        var paragraphs = new List<string>();

        // Byte array must go through MemoryStream to reach CreateGdPictureImageFromStream
        using var ms = new MemoryStream(imageBytes);
        int imageId = _imaging.CreateGdPictureImageFromStream(ms);

        if (imageId == 0)
            throw new Exception($"Byte array load failed: {_imaging.GetStat()}");

        try
        {
            _ocr.SetImage(imageId);
            _ocr.Language = "eng";

            string resultId = _ocr.RunOCR();

            if (string.IsNullOrEmpty(resultId))
                throw new Exception($"OCR failed: {_ocr.GetStat()}");

            // Paragraph-level data requires iterating block structure
            int blockCount = _ocr.GetOCRResultBlockCount(resultId);

            for (int b = 0; b < blockCount; b++)
            {
                var blockText = new StringBuilder();
                int lineCount = _ocr.GetOCRResultBlockLineCount(resultId, b);

                for (int l = 0; l < lineCount; l++)
                {
                    int wordCount = _ocr.GetOCRResultBlockLineWordCount(resultId, b, l);

                    for (int w = 0; w < wordCount; w++)
                    {
                        blockText.Append(_ocr.GetOCRResultBlockLineWordText(resultId, b, l, w));
                        blockText.Append(" ");
                    }
                }

                string text = blockText.ToString().Trim();
                if (!string.IsNullOrEmpty(text))
                    paragraphs.Add(text);
            }
        }
        finally
        {
            _imaging.ReleaseGdPictureImage(imageId);
        }

        return paragraphs;
    }
}
Imports GdPicture14
Imports System.IO
Imports System.Text

Public Class GdPictureByteArrayOcr
    Private ReadOnly _imaging As GdPictureImaging
    Private ReadOnly _ocr As GdPictureOCR

    Public Function ExtractParagraphsFromBytes(imageBytes As Byte()) As List(Of String)
        Dim paragraphs As New List(Of String)()

        ' Byte array must go through MemoryStream to reach CreateGdPictureImageFromStream
        Using ms As New MemoryStream(imageBytes)
            Dim imageId As Integer = _imaging.CreateGdPictureImageFromStream(ms)

            If imageId = 0 Then
                Throw New Exception($"Byte array load failed: {_imaging.GetStat()}")
            End If

            Try
                _ocr.SetImage(imageId)
                _ocr.Language = "eng"

                Dim resultId As String = _ocr.RunOCR()

                If String.IsNullOrEmpty(resultId) Then
                    Throw New Exception($"OCR failed: {_ocr.GetStat()}")
                End If

                ' Paragraph-level data requires iterating block structure
                Dim blockCount As Integer = _ocr.GetOCRResultBlockCount(resultId)

                For b As Integer = 0 To blockCount - 1
                    Dim blockText As New StringBuilder()
                    Dim lineCount As Integer = _ocr.GetOCRResultBlockLineCount(resultId, b)

                    For l As Integer = 0 To lineCount - 1
                        Dim wordCount As Integer = _ocr.GetOCRResultBlockLineWordCount(resultId, b, l)

                        For w As Integer = 0 To wordCount - 1
                            blockText.Append(_ocr.GetOCRResultBlockLineWordText(resultId, b, l, w))
                            blockText.Append(" "c)
                        Next
                    Next

                    Dim text As String = blockText.ToString().Trim()
                    If Not String.IsNullOrEmpty(text) Then
                        paragraphs.Add(text)
                    End If
                Next
            Finally
                _imaging.ReleaseGdPictureImage(imageId)
            End Try
        End Using

        Return paragraphs
    End Function
End Class
$vbLabelText   $csharpLabel

Enfoque IronOCR:

using IronOcr;

public class IronOcrByteArrayOcr
{
    public List<string> ExtractParagraphsFromBytes(byte[] imageBytes)
    {
        using var input = new OcrInput();
        input.LoadImage(imageBytes);  // byte array accepted directly

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

        // Paragraphs are a first-class typed collection
        return result.Paragraphs
            .Select(p => p.Text)
            .Where(t => !string.IsNullOrWhiteSpace(t))
            .ToList();
    }
}
using IronOcr;

public class IronOcrByteArrayOcr
{
    public List<string> ExtractParagraphsFromBytes(byte[] imageBytes)
    {
        using var input = new OcrInput();
        input.LoadImage(imageBytes);  // byte array accepted directly

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

        // Paragraphs are a first-class typed collection
        return result.Paragraphs
            .Select(p => p.Text)
            .Where(t => !string.IsNullOrWhiteSpace(t))
            .ToList();
    }
}
Imports IronOcr

Public Class IronOcrByteArrayOcr
    Public Function ExtractParagraphsFromBytes(imageBytes As Byte()) As List(Of String)
        Using input As New OcrInput()
            input.LoadImage(imageBytes) ' byte array accepted directly

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

            ' Paragraphs are a first-class typed collection
            Return result.Paragraphs _
                .Select(Function(p) p.Text) _
                .Where(Function(t) Not String.IsNullOrWhiteSpace(t)) _
                .ToList()
        End Using
    End Function
End Class
$vbLabelText   $csharpLabel

IronOCR acepta byte[] directamente en LoadImage sin el MemoryStream intermedio. El resultado expone .Paragraphs como una colección tipada consultable por LINQ — no se requiere un triple bucle de bloque/linea/palabra. La guía de lectura de resultados abarca el acceso por coordenadas, el filtrado de confianza y los patrones de salida estructurados para los flujos de trabajo de procesamiento de facturas y formularios. Para escanear áreas específicas de un documento, consulte el reconocimiento óptico de caracteres (OCR) basado en regiones .

Referencia de mapeo de la API .NET de GdPicture a IronOCR

GdPicture.NET Equivalente a IronOCR
using GdPicture14; using IronOcr;
LicenseManager.RegisterKEY("key") IronOcr.License.LicenseKey = "key"
new GdPictureImaging() No requerido — interno a OcrInput
new GdPictureOCR() new IronTesseract()
new GdPicturePDF() No requerido — OcrInput.LoadPdf() maneja esto
_ocr.ResourceFolder = path No es necesario: los recursos están incluidos en NuGet.
imaging.CreateGdPictureImageFromFile(path) input.LoadImage(path)
imaging.CreateGdPictureImageFromStream(stream) input.LoadImage(stream)
imaging.CreateGdPictureImageFromBytes(bytes) input.LoadImage(bytes)
imaging.CloneImage(tiffId) por marco input.LoadImageFrames(tiffPath)
imaging.ReleaseGdPictureImage(imageId) using var input = new OcrInput() — automático
ocr.SetImage(imageId) No requerido — OcrInput sostiene la imagen
ocr.Language = "eng" ocr.Language = OcrLanguage.English
ocr.RunOCR() → cadena resultId ocr.Read(input) → tipado OcrResult
ocr.GetOCRResultText(resultId) result.Text
ocr.GetOCRResultConfidence(resultId) result.Confidence
ocr.GetOCRResultBlockCount(resultId) result.Pages[i].Paragraphs.Count
ocr.GetOCRResultBlockLineWordText(resultId, b, l, w) result.Words[i].Text
imaging.GetStat() / ocr.GetStat() Excepciones de .NET Standard
Verificación GdPictureStatus.OK después de cada llamada No es necesario: las excepciones se propagan.
pdf.OcrPage("eng", resourcePath, "", 200) result.SaveAsSearchablePdf(outputPath)
pdf.RenderPageToGdPictureImage(200, false) No es necesario:IronOCR realiza el renderizado internamente.
pdf.SelectPage(i) No es necesario: todas las páginas se procesan por defecto.
pdf.GetPageCount() result.Pages.Count
Task.Run(() => ocr.RunOCR()) + SemaphoreSlim await ocr.ReadAsync(input)

Problemas comunes de migración y soluciones

Problema 1: Variables de ID de imagen que permanecen en el código después de la refactorización.

GdPicture.NET: El código existente declara int imageId en la parte superior de los métodos, lo rastrea a través de try/finality y lo pasa a múltiples llamadas de GdPicture. Después de reemplazar las llamadas a GdPicture, estas variables y sus llamadas a ReleaseGdPictureImage se convierten en código obsoleto huérfano.

Solución: Elimine todo el patrón de ID de imagen. Reemplace la declaración, el try, el finally, y la llamada de liberación con un bloque using var input = new OcrInput(). Busque ReleaseGdPictureImage para encontrar cada llamada de limpieza que debe ser eliminada:

grep -rn "ReleaseGdPictureImage\|imageId\|resultId" --include="*.cs" .
grep -rn "ReleaseGdPictureImage\|imageId\|resultId" --include="*.cs" .
SHELL
// Remove all of this
int imageId = _imaging.CreateGdPictureImageFromFile(path);
try
{
    _ocr.SetImage(imageId);
    string resultId = _ocr.RunOCR();
    return _ocr.GetOCRResultText(resultId);
}
finally
{
    _imaging.ReleaseGdPictureImage(imageId);
}

// Replace with
using var input = new OcrInput();
input.LoadImage(path);
return new IronTesseract().Read(input).Text;
// Remove all of this
int imageId = _imaging.CreateGdPictureImageFromFile(path);
try
{
    _ocr.SetImage(imageId);
    string resultId = _ocr.RunOCR();
    return _ocr.GetOCRResultText(resultId);
}
finally
{
    _imaging.ReleaseGdPictureImage(imageId);
}

// Replace with
using var input = new OcrInput();
input.LoadImage(path);
return new IronTesseract().Read(input).Text;
' Remove all of this
Dim imageId As Integer = _imaging.CreateGdPictureImageFromFile(path)
Try
    _ocr.SetImage(imageId)
    Dim resultId As String = _ocr.RunOCR()
    Return _ocr.GetOCRResultText(resultId)
Finally
    _imaging.ReleaseGdPictureImage(imageId)
End Try

' Replace with
Using input As New OcrInput()
    input.LoadImage(path)
    Return New IronTesseract().Read(input).Text
End Using
$vbLabelText   $csharpLabel

Problema 2: No se encuentra la ruta de la carpeta de recursos en el entorno desplegado.

GdPicture.NET: La ruta establecida en _ocr.ResourceFolder se resuelve en la máquina de desarrollo pero falla en producción. Los síntomas comunes son fallas silenciosas de OCR que devuelven resultados vacíos o errores GdPictureStatus genéricos que no nombran el archivo faltante.

Solución: Elimine por completo la asignación ResourceFolder. La compatibilidad con el inglés está integrada en el paquete NuGet de IronOCR. Los idiomas adicionales se instalan como paquetes NuGet . No hay ninguna ruta de sistema de archivos para configurar o implementar:

# Remove the filesystem folder from deployment artifacts
# Add language NuGet packages to the project instead
:InstallCmd dotnet add package IronOcr.Languages.French, IronOcr.Languages.German
# Remove the filesystem folder from deployment artifacts
# Add language NuGet packages to the project instead
:InstallCmd dotnet add package IronOcr.Languages.French, IronOcr.Languages.German
SHELL

Problema 3: El manejo de errores de GdPictureStatus se reemplaza por excepciones.

GdPicture.NET: Cada operación devuelve o establece un valor enum GdPictureStatus que debe verificarse inmediatamente. El código está lleno de protecciones if (status != GdPictureStatus.OK). Algunos códigos de estado son genéricos (por ejemplo, Error, InvalidParameter) y requieren consultar la documentación para determinar la causa raíz.

Solución:IronOCR lanza excepciones tipadas de .NET . Reemplazar las comprobaciones de estado con bloques try/catch. El estándar IOException y FileNotFoundException cubren errores de entrada; IronOcr.Exceptions.OcrException cubre errores específicos de OCR. Consulte la guía de configuración de IronTesseract para conocer los patrones de manejo de errores recomendados:

try
{
    var result = new IronTesseract().Read(imagePath);
    return result.Text;
}
catch (FileNotFoundException ex)
{
    _logger.LogError("Input file missing: {Path}", ex.FileName);
    throw;
}
catch (IronOcr.Exceptions.OcrException ex)
{
    _logger.LogError("OCR processing failed: {Message}", ex.Message);
    throw;
}
try
{
    var result = new IronTesseract().Read(imagePath);
    return result.Text;
}
catch (FileNotFoundException ex)
{
    _logger.LogError("Input file missing: {Path}", ex.FileName);
    throw;
}
catch (IronOcr.Exceptions.OcrException ex)
{
    _logger.LogError("OCR processing failed: {Message}", ex.Message);
    throw;
}
Imports IronOcr
Imports System.IO

Try
    Dim result = New IronTesseract().Read(imagePath)
    Return result.Text
Catch ex As FileNotFoundException
    _logger.LogError("Input file missing: {Path}", ex.FileName)
    Throw
Catch ex As IronOcr.Exceptions.OcrException
    _logger.LogError("OCR processing failed: {Message}", ex.Message)
    Throw
End Try
$vbLabelText   $csharpLabel

Problema 4: Espacio de nombres GdPicture14 en múltiples archivos

GdPicture.NET: El número de versión en el espacio de nombres significa que existe una directiva using GdPicture14; a nivel de proyecto en docenas de archivos. Después de la migración, todos estos deben ser reemplazados por using IronOcr;.

Solución: Use una búsqueda y reemplazo global en todos los archivos .cs, luego verifique que no queden referencias a GdPicture:

# Find all files with GdPicture namespace
grep -rln "using GdPicture" --include="*.cs" .

# After replacing, verify nothing remains
grep -rn "GdPicture14\|GdPictureOCR\|GdPictureImaging\|GdPicturePDF" --include="*.cs" .
# Find all files with GdPicture namespace
grep -rln "using GdPicture" --include="*.cs" .

# After replacing, verify nothing remains
grep -rn "GdPicture14\|GdPictureOCR\|GdPictureImaging\|GdPicturePDF" --include="*.cs" .
SHELL

Problema 5: Lógica de conteo de fotogramas TIFF

GdPicture.NET: El código que itera marcos TIFF a menudo mezcla llamadas a través de GdPictureImaging.GetPageCount(imageId) y GdPicturePDF.GetPageCount() dependiendo de cómo fue cargado el archivo. El índice del marco está basado en 1.

Solución: input.LoadImageFrames(path) maneja todos los marcos automáticamente. Si su código existente solo procesa marcos específicos, use input.LoadImageFrames(path, frameNumbers) con un array de índices basado en cero. Acceda a los resultados por marco a través de result.Pages, que también está indexado a cero. La guía de entrada TIFF documenta explícitamente el comportamiento del índice.

Problema 6: El preprocesamiento requiere el complemento de procesamiento de imágenes de documentos.

GdPicture.NET: Las operaciones de enderezamiento y eliminación de manchas pertenecen al complemento GdPictureDocumentImaging, que requiere una compra de licencia separada. Los equipos que no utilizan este complemento suelen tener problemas de precisión en el reconocimiento óptico de caracteres (OCR) de documentos escaneados con páginas torcidas o con mucho ruido.

Solución: Los métodos de preprocesamiento de IronOCR forman parte del paquete base. Agregue Deskew() y DeNoise() directamente en OcrInput. La guía de corrección de calidad de imagen cubre todos los filtros disponibles incluyendo Contrast(), Sharpen(), Binarize(), y DeepCleanBackgroundNoise() para escaneos severamente degradados:

using var input = new OcrInput();
input.LoadImage("scanned-document.tiff");
input.Deskew();        // no separate plugin license
input.DeNoise();
input.Contrast();

var result = new IronTesseract().Read(input);
using var input = new OcrInput();
input.LoadImage("scanned-document.tiff");
input.Deskew();        // no separate plugin license
input.DeNoise();
input.Contrast();

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

Using input As New OcrInput()
    input.LoadImage("scanned-document.tiff")
    input.Deskew() ' no separate plugin license
    input.DeNoise()
    input.Contrast()

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

Lista de verificación de migración de GdPicture .NET

Pre-Migración

Antes de realizar cambios, revise el código fuente para localizar todas las dependencias de GdPicture:

# Find all GdPicture namespace imports
grep -rn "using GdPicture" --include="*.cs" .

# Find all image ID creation points
grep -rn "CreateGdPictureImageFromFile\|CreateGdPictureImageFromStream\|RenderPageToGdPictureImage\|CloneImage" --include="*.cs" .

# Find all release calls — these map to using block boundaries
grep -rn "ReleaseGdPictureImage" --include="*.cs" .

# Find all resource folder assignments
grep -rn "ResourceFolder" --include="*.cs" .

# Find all OCR result ID accesses
grep -rn "RunOCR\|GetOCRResult\|resultId" --include="*.cs" .

# Find all GdPictureStatus checks
grep -rn "GdPictureStatus\|GetStat()" --include="*.cs" .

# Count GdPicture-dependent files
grep -rln "GdPicture14" --include="*.cs" . | wc -l
# Find all GdPicture namespace imports
grep -rn "using GdPicture" --include="*.cs" .

# Find all image ID creation points
grep -rn "CreateGdPictureImageFromFile\|CreateGdPictureImageFromStream\|RenderPageToGdPictureImage\|CloneImage" --include="*.cs" .

# Find all release calls — these map to using block boundaries
grep -rn "ReleaseGdPictureImage" --include="*.cs" .

# Find all resource folder assignments
grep -rn "ResourceFolder" --include="*.cs" .

# Find all OCR result ID accesses
grep -rn "RunOCR\|GetOCRResult\|resultId" --include="*.cs" .

# Find all GdPictureStatus checks
grep -rn "GdPictureStatus\|GetStat()" --include="*.cs" .

# Count GdPicture-dependent files
grep -rln "GdPicture14" --include="*.cs" . | wc -l
SHELL

Documente lo siguiente antes de modificar el código:

  • Qué archivos contienen referencias GdPictureImaging, GdPictureOCR, y GdPicturePDF
  • ¿Cuántos pares distintos de ID de creación/lanzamiento de imágenes existen?
  • Si el complemento de procesamiento de imágenes de documentos (corrección de inclinación, eliminación de ruido) está en uso.
  • Qué archivos de idioma traineddata están en la carpeta de recursos
  • ¿Qué scripts de despliegue o archivos Docker hacen referencia a la ruta de la carpeta de recursos?

Migración de código

  1. Elimine todos los paquetes NuGet de GdPicture del archivo del proyecto.
  2. Instalar IronOcr a través de NuGet
  3. Instalar paquetes IronOcr.Languages.* para cada idioma previamente en la carpeta de recursos
  4. Añadir IronOcr.License.LicenseKey = "..." a Program.cs o Startup.cs
  5. Eliminar la llamada LicenseManager.RegisterKEY() y el objeto de gestor de licencias
  6. Eliminar todas las declaraciones de campo GdPictureImaging e inicialización de constructor
  7. Eliminar todas las declaraciones de campo GdPictureOCR y la asignación ResourceFolder
  8. Eliminar todas las declaraciones de campo GdPicturePDF usadas para flujos de trabajo de OCR
  9. Reemplazar cada bloque int imageId = _imaging.CreateGdPictureImage*(...) con using var input = new OcrInput(); input.Load*(...)
  10. Reemplazar cada _ocr.SetImage(imageId); _ocr.Language = &quot;...&quot;; string resultId = _ocr.RunOCR(); with var result = new IronTesseract().Read(input);
  11. Reemplazar _ocr.GetOCRResultText(resultId) con result.Text
  12. Eliminar todas las llamadas _imaging.ReleaseGdPictureImage(imageId)
  13. Reemplazar verificaciones GdPictureStatus con bloques try/catch
  14. Reemplazar bucles de marcos TIFF con input.LoadImageFrames(path)
  15. Reemplazar pdf.OcrPage(...) + pdf.SaveToFile(...) con result.SaveAsSearchablePdf(outputPath)
  16. Elimine la ruta de la carpeta de recursos de los scripts de implementación y las imágenes de Docker.
  17. Actualizar using GdPicture14; a using IronOcr; en todos los archivos afectados

Posmigración

Una vez completados todos los cambios en el código, verifique lo siguiente antes de implementarlo en producción:

  • El OCR de una sola imagen devuelve el texto esperado sin NullReferenceException de componentes eliminados
  • El procesamiento de TIFF de varias páginas cubre todos los marcos y produce texto por página a través de result.Pages
  • El OCR de PDF procesa todas las páginas sin aumentar el consumo de memoria en lotes de más de 50 documentos.
  • La entrada de flujo acepta MemoryStream y FileStream sin escrituras de archivo intermedias
  • El OCR asíncrono se integra con los controladores de solicitudes de ASP.NET Core sin bloquear el grupo de subprocesos.
  • El procesamiento por lotes en paralelo con Parallel.ForEach produce resultados precisos en todos los hilos
  • Todos los paquetes de idioma (francés, alemán, etc.) se activan correctamente a través de paquetes NuGet.
  • Los filtros de preprocesamiento (corrección de sesgo, reducción de ruido) mejoran la precisión en los documentos escaneados.
  • La salida PDF con capacidad de búsqueda es legible por visores de PDF y aplicaciones de búsqueda de texto.
  • No quedan referencias al espacio de nombres GdPicture en ningún archivo .cs después de la migración
  • El perfil de memoria muestra un uso estable bajo carga sostenida (sin asignaciones de imágenes con fugas).
  • El despliegue se realiza correctamente en entornos Linux y Docker sin errores en las rutas del sistema de archivos.

Principales ventajas de migrar a IronOCR

Seguridad de memoria impuesta por el compilador. El ciclo de vida del ID de imagen que GdPicture exige que los desarrolladores mantengan manualmente desaparece por completo. OcrInput implementa IDisposable, y los bloques using refuerzan la limpieza al final de cada alcance — incluyendo los caminos de excepción. No hay List<int> para mantener, no hay bloques finally para recordar, no hay incidentes de memoria en producción por llamadas de liberación perdidas.

Paquete único para cada escenario de OCR. OCR de imagen, OCR de PDF, procesamiento de TIFF de varias páginas, generación de PDF buscable, filtros de preprocesamiento, lectura de códigos de barras y más de 125 paquetes de idiomas están todos disponibles desde el paquete NuGet IronOcr y sus compañeros de idioma. No existe ninguna restricción que requiera la compra de una segunda o tercera licencia antes de que sea posible un flujo de trabajo común. Consulte la descripción general de las funciones de IronOCR para obtener la lista completa de capacidades.

Asincronía nativa y seguridad de hilos. ReadAsync es un método asíncrono genuino, no un envoltorio Task.Run. IronTesseract es seguro de usar a través de hilos sin sincronización. Los servicios de procesamiento de documentos que antes requerían inicialización de componentes por hilo y serialización con semáforos se reducen a una sola instancia compartida con Parallel.ForEach. La guía de OCR asíncrono abarca tanto ASP.NET Core como patrones de servicios alojados.

Configuración de implementación cero.IronOCR no requiere rutas de sistema de archivos, archivos de idioma externos, ubicaciones de binarios nativos ni scripts de implementación. El paso de restauración de NuGet proporciona todo lo que la aplicación necesita. Las imágenes de Docker, las implementaciones de Azure App Service y los servidores Linux funcionan de forma idéntica a la máquina de desarrollo. La guía de implementación de Docker y la guía de implementación de Azure muestran configuraciones listas para producción.

Espacio de nombres estable por versión. using IronOcr no ha cambiado en las versiones principales. Los números de versión de NuGet gestionan el control de versiones mediante el modelo estándar de gestión de paquetes. Las futuras actualizaciones importantes no requieren una búsqueda y reemplazo de la importación del espacio de nombres en todo el código fuente.

Licencias perpetuas predecibles. El IronOCR tiene un precio de $999 (Lite), $1,499 (Plus), $2,999 (Professional) y $5,999 (Unlimited) — compras perpetuas únicas que incluyen un año de actualizaciones, con renovación opcional anual. Todas las funciones están disponibles en todos los niveles. No hay complementos por función, ni costes por página, ni obligación de mantenimiento después del primer año. Los equipos que anteriormente habían invertido más de 8.000 dólares en licencias de complementos de GdPicture para un flujo de trabajo exclusivamente de OCR recuperan esa inversión durante el primer ciclo de licencia. Encontrará información completa sobre los precios en la página de licencias de IronOCR .

Por favor notaGdPicture.NET y Tesseract son marcas registradas de sus respectivos propietarios. Este sitio no está afiliado, respaldado o patrocinado por Google, Nutrient, u ORPALIS. 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 GdPicture.NET OCR 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 GdPicture.NET OCR a IronOCR?

Sustituya las secuencias de inicialización de GdPicture.NET 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 tiene la misma precisión de reconocimiento óptico de caracteres que GdPicture.NET 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 GdPicture.NET OCR 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 GdPicture.NET OCR a IronOCR requiere cambios en la infraestructura de implantación?

IronOCR requiere menos cambios de infraestructura que GdPicture.NET OCR. 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 lo hace GdPicture.NET?

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 GdPicture.NET OCR 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 GdPicture.NET OCR 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