Saltar al pie de página
VIDEOS

Cómo leer GIF y TIFF multiimagen en C#

Esta guía lleva a los desarrolladores de .NET a través de una migración completa de PaddleSharp OCR(Sdcb.PaddleOCR) a IronOCR. Abarca la sustitución de la gestión de sesiones de inferencia, la eliminación de la dependencia del preprocesamiento de OpenCV, la eliminación de la lógica de selección de backend para CPU, GPU y OpenVINO, y la migración de los flujos de trabajo de reconocimiento de tablas. Cada sección proporciona código de antes y después, extraído de patrones específicos de PaddleSharp que no aparecen en las comparaciones OCR genéricas.

¿Por qué migrar desde PaddleSharp OCR?

PaddleSharp expone un sistema de inferencia de aprendizaje profundo en la capa de aplicación. Esa arquitectura te da acceso al rendimiento del modelo PaddlePaddle, pero exige que tu aplicación gestione lo que de otro modo serían cuestiones de infraestructura. Los siguientes problemas son los que llevan a la mayoría de los equipos .NET a buscar alternativas.

La configuración del backend de inferencia es código de aplicación. Elegir entre los backends de CPU, GPU y OpenVINO en PaddleSharp requiere construir y configurar objetos PaddleConfig, seleccionar el paquete NuGet de tiempo de ejecución nativo adecuado para el objetivo de despliegue y ramificar condicionalmente tu código de inicialización basado en el hardware disponible en tiempo de ejecución. Esta lógica reside en tu aplicación, no en la biblioteca, y se rompe cuando el entorno objetivo cambia.

OpenCV es una dependencia necesaria para la entrada de imágenes. PaddleSharp no puede aceptar una ruta de archivo ni un flujo de datos directamente. Cada imagen pasa por el Cv2.ImRead() de OpenCV antes de llegar al motor OCR. Eso fuerza OpenCvSharp4 y un paquete específico de la plataforma OpenCvSharp4.runtime.* en tu gráfico de dependencias. Actualizar el entorno de ejecución de una plataforma sin actualizar el otro provoca fallos en el tiempo de ejecución que son difíciles de reproducir en diferentes entornos.

La duración de la sesión de inferencia requiere un diseño explícito. PaddleOcrAll carga tres binarios modelo desde el disco en el momento de la construcción. Ese costo, medible en cientos de milisegundos, significa que el objeto no puede ser instanciado por solicitud. Los equipos deben diseñar una estrategia de ciclo de vida: singleton, agrupado o con alcance. En ASP.NET Core, esto típicamente significa un servicio registrado con un análisis cuidadoso de seguridad de hilos, porque PaddleOcrAll comparte estado nativo subyacente.

El reconocimiento de tablas requiere la descarga de modelos adicionales. La extracción de documentos estructurados en PaddleSharp requiere un modelo de reconocimiento de tablas específico, además del proceso estándar de detección, clasificación y reconocimiento en tres etapas. Este modelo constituye un cuarto archivo que debe descargarse, versionarse y configurarse. No existe una interfaz API unificada: el reconocimiento de tablas utiliza una ruta de código distinta con su propio tipo de resultado.

No se genera un archivo PDF con texto seleccionable. PaddleSharp produce cadenas de texto. No puede escribir archivos PDF con texto editable. Los equipos que necesiten archivar documentos escaneados como archivos PDF con capacidad de búsqueda de texto deben integrar una biblioteca de PDF independiente, gestionar esa dependencia adicional y escribir una capa de conversión. La brecha en el formato de salida es total: no hay hOCR, ni PDF estructurado con capacidad de búsqueda, ni superposición de capas de texto.

La cadena de dependencias ascendente no pertenece a la comunidad .NET . PaddleSharp encapsula el marco de inferencia PaddlePaddle de Baidu. Los cambios en el formato del modelo entre las versiones de PaddleOCR han provocado fallos en la capa de enlace .NET en el pasado. La mayor parte del seguimiento de incidencias, la documentación y los debates sobre las versiones se realizan en chino. Para un equipo .NET que no cuenta con hablantes de mandarín que supervisen los proyectos existentes, los cambios incompatibles llegan sin previo aviso.

El problema fundamental

Seleccionar e inicializar un backend en PaddleSharp requiere código de configuración que pertenece a la infraestructura, no a la lógica de OCR:

// PaddleSharp: Backend selection sprawls into application startup
// Simplified — see Sdcb.PaddleInference documentation for full API
using Sdcb.PaddleInference;
using Sdcb.PaddleOCR;

// CPU-only deployment
var config = PaddleConfig.FromModelDir("models/det");
config.SetCpuMathLibraryNumThreads(4);

// GPU deployment — different package, different init path
// var config = PaddleConfig.FromModelDir("models/det");
// config.EnableGpu(500, 0);  // memoryMB, deviceId

// OpenVINO deployment — third conditional branch
// config.EnableMkldnn();

// Application code now owns the hardware topology decision
// PaddleSharp: Backend selection sprawls into application startup
// Simplified — see Sdcb.PaddleInference documentation for full API
using Sdcb.PaddleInference;
using Sdcb.PaddleOCR;

// CPU-only deployment
var config = PaddleConfig.FromModelDir("models/det");
config.SetCpuMathLibraryNumThreads(4);

// GPU deployment — different package, different init path
// var config = PaddleConfig.FromModelDir("models/det");
// config.EnableGpu(500, 0);  // memoryMB, deviceId

// OpenVINO deployment — third conditional branch
// config.EnableMkldnn();

// Application code now owns the hardware topology decision
Imports Sdcb.PaddleInference
Imports Sdcb.PaddleOCR

' PaddleSharp: Backend selection sprawls into application startup
' Simplified — see Sdcb.PaddleInference documentation for full API

' CPU-only deployment
Dim config = PaddleConfig.FromModelDir("models/det")
config.SetCpuMathLibraryNumThreads(4)

' GPU deployment — different package, different init path
' Dim config = PaddleConfig.FromModelDir("models/det")
' config.EnableGpu(500, 0)  ' memoryMB, deviceId

' OpenVINO deployment — third conditional branch
' config.EnableMkldnn()

' Application code now owns the hardware topology decision
$vbLabelText   $csharpLabel
// IronOCR: No backend selection. No config objects. Zero hardware decisions.
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";

var result = new IronTesseract().Read("document.jpg");
Console.WriteLine(result.Text);
// Runs on CPU, Linux, Docker, or ARM without a code change
// IronOCR: No backend selection. No config objects. Zero hardware decisions.
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";

var result = new IronTesseract().Read("document.jpg");
Console.WriteLine(result.Text);
// Runs on CPU, Linux, Docker, or ARM without a code change
Imports IronOcr

' IronOCR: No backend selection. No config objects. Zero hardware decisions.
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"

Dim result = (New IronTesseract()).Read("document.jpg")
Console.WriteLine(result.Text)
' Runs on CPU, Linux, Docker, or ARM without a code change
$vbLabelText   $csharpLabel

IronOCR vs PaddleSharp OCR: Comparación de características

Aquí se presenta una comparación directa de capacidades en las dimensiones que más importan durante la migración:

Característica PaddleSharp OCR IronOCR
Se requieren paquetes NuGet 3–4 mínimo 1
Método de entrada de imagen OpenCV Cv2.ImRead() Ruta directa, flujo o matriz de bytes
Entrada de PDF (nativa) No
PDF protegido con contraseña No
TIFF de varias páginas Mediante OpenCV Nativo
Salida en PDF con capacidad de búsqueda No Sí (result.SaveAsSearchablePdf())
Exportación hOCR No
Selección de backend (CPU/GPU/OpenVINO) Manual PaddleConfig Automático
Pipeline de preprocesamiento Operaciones manuales de OpenCV Incorporado (Deskew, DeNoise, Contrast, etc.)
Gestión del ciclo de vida de las sesiones de inferencia Manual (construcción costosa) IronTesseract ligero
Modelo de reconocimiento de tablas Ruta de descarga y código por separado input.LoadImage() + resultado estructurado
Idiomas compatibles ~10-20 125+
Instalación del idioma Descarga del archivo del modelo Paquete NuGet
Multilingüe simultáneo Limitado Sí (OcrLanguage.French + OcrLanguage.German)
OCR basado en la región No incorporado CropRectangle
Lectura de BarCodes durante el OCR No Sí (ocr.Configuration.ReadBarCodes = true)
Puntuaciones de confianza Por región Por palabra, por línea, por página
Jerarquía de salida estructurada Lista de regiones planas Páginas → Párrafos → Líneas → Palabras → Caracteres
Implementación multiplataforma Paquetes de tiempo de ejecución de plataforma complejos NuGet único, para todas las plataformas.
Despliegue de Docker Múltiples capas, paquetes de tiempo de ejecución Capa única
Apoyo comercial Problemas de GitHub (principalmente en chino) Soporte por correo electrónico
Modelo de licencia Apache 2.0 Perpetual ($999 Lite, $1,499 Pro, $2,999 Enterprise)

Inicio rápido: Migración de PaddleSharp OCRa IronOCR

Paso 1: Sustituir el paquete NuGet

Elimine PaddleSharp y su dependencia de OpenCV:

dotnet remove package Sdcb.PaddleOCR
dotnet remove package Sdcb.PaddleInference
dotnet remove package OpenCvSharp4
dotnet remove package OpenCvSharp4.runtime.win
dotnet remove package Sdcb.PaddleOCR
dotnet remove package Sdcb.PaddleInference
dotnet remove package OpenCvSharp4
dotnet remove package OpenCvSharp4.runtime.win
SHELL

Instala IronOCR desde NuGet :

dotnet add package IronOcr

Paso 2: Actualizar los espacios de nombres

Reemplace los espacios de nombres de PaddleSharp con el único espacio de nombres de IronOCR:

// Before (PaddleSharp)
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using Sdcb.PaddleInference;
using OpenCvSharp;

// After (IronOCR)
using IronOcr;
// Before (PaddleSharp)
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using Sdcb.PaddleInference;
using OpenCvSharp;

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

Paso 3: Inicializar licencia

Agrega la inicialización de la licencia una vez al inicio de la aplicación, en Program.cs, Startup.cs, o en tu raíz de composición:

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

Ejemplos de migración de código

Reemplazo del ciclo de vida de la sesión de inferencia

El PaddleOcrAll de PaddleSharp es costoso de construir porque carga tres binarios modelo de forma sincrónica en la instanciación. Las aplicaciones de producción deben tratarlo como un objeto de larga duración, lo que exige un patrón específico de inyección de dependencias. La cadena de eliminación también requiere atención, ya que los recursos nativos subyacentes deben liberarse en el orden correcto.

Enfoque OCR de PaddleSharp:

// Simplified — see Sdcb.PaddleOCR documentation for full API
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using OpenCvSharp;
using Microsoft.Extensions.DependencyInjection;

// Expensive: loads 3 model files from disk on construction (~300–800ms)
public class PaddleOcrEngine : IDisposable
{
    private readonly PaddleOcrAll _ocr;
    private bool _disposed;

    public PaddleOcrEngine()
    {
        var detModel = LocalFullModels.ChineseV3.DetectionModel;
        var clsModel = LocalFullModels.ChineseV3.ClassifierModel;
        var recModel = LocalFullModels.ChineseV3.RecognitionModel;

        // Must be singleton — cannot afford per-request construction
        _ocr = new PaddleOcrAll(detModel, clsModel, recModel);
    }

    public string Read(string imagePath)
    {
        using var mat = Cv2.ImRead(imagePath); // OpenCV required even for a file path
        var result = _ocr.Run(mat);
        return string.Join(" ", result.Regions.Select(r => r.Text));
    }

    public void Dispose()
    {
        if (!_disposed)
        {
            _ocr?.Dispose();
            _disposed = true;
        }
    }
}

// Startup.cs — forced singleton because of construction cost
services.AddSingleton<PaddleOcrEngine>();
// Simplified — see Sdcb.PaddleOCR documentation for full API
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using OpenCvSharp;
using Microsoft.Extensions.DependencyInjection;

// Expensive: loads 3 model files from disk on construction (~300–800ms)
public class PaddleOcrEngine : IDisposable
{
    private readonly PaddleOcrAll _ocr;
    private bool _disposed;

    public PaddleOcrEngine()
    {
        var detModel = LocalFullModels.ChineseV3.DetectionModel;
        var clsModel = LocalFullModels.ChineseV3.ClassifierModel;
        var recModel = LocalFullModels.ChineseV3.RecognitionModel;

        // Must be singleton — cannot afford per-request construction
        _ocr = new PaddleOcrAll(detModel, clsModel, recModel);
    }

    public string Read(string imagePath)
    {
        using var mat = Cv2.ImRead(imagePath); // OpenCV required even for a file path
        var result = _ocr.Run(mat);
        return string.Join(" ", result.Regions.Select(r => r.Text));
    }

    public void Dispose()
    {
        if (!_disposed)
        {
            _ocr?.Dispose();
            _disposed = true;
        }
    }
}

// Startup.cs — forced singleton because of construction cost
services.AddSingleton<PaddleOcrEngine>();
Imports Sdcb.PaddleOCR
Imports Sdcb.PaddleOCR.Models
Imports OpenCvSharp
Imports Microsoft.Extensions.DependencyInjection

' Expensive: loads 3 model files from disk on construction (~300–800ms)
Public Class PaddleOcrEngine
    Implements IDisposable

    Private ReadOnly _ocr As PaddleOcrAll
    Private _disposed As Boolean

    Public Sub New()
        Dim detModel = LocalFullModels.ChineseV3.DetectionModel
        Dim clsModel = LocalFullModels.ChineseV3.ClassifierModel
        Dim recModel = LocalFullModels.ChineseV3.RecognitionModel

        ' Must be singleton — cannot afford per-request construction
        _ocr = New PaddleOcrAll(detModel, clsModel, recModel)
    End Sub

    Public Function Read(imagePath As String) As String
        Using mat = Cv2.ImRead(imagePath) ' OpenCV required even for a file path
            Dim result = _ocr.Run(mat)
            Return String.Join(" ", result.Regions.Select(Function(r) r.Text))
        End Using
    End Function

    Public Sub Dispose() Implements IDisposable.Dispose
        If Not _disposed Then
            _ocr?.Dispose()
            _disposed = True
        End If
    End Sub
End Class

' Startup.vb — forced singleton because of construction cost
services.AddSingleton(Of PaddleOcrEngine)()
$vbLabelText   $csharpLabel

Enfoque IronOCR:

using IronOcr;
using Microsoft.Extensions.DependencyInjection;

// IronTesseract has lightweight initialization — no model loading on construction
public class OcrEngine
{
    public string Read(string imagePath)
    {
        return new IronTesseract().Read(imagePath).Text;
    }
}

// Flexible registration — singleton, scoped, or transient all work
services.AddTransient<OcrEngine>();

// Or skip the wrapper entirely and inject IronTesseract directly
services.AddTransient<IronTesseract>();
using IronOcr;
using Microsoft.Extensions.DependencyInjection;

// IronTesseract has lightweight initialization — no model loading on construction
public class OcrEngine
{
    public string Read(string imagePath)
    {
        return new IronTesseract().Read(imagePath).Text;
    }
}

// Flexible registration — singleton, scoped, or transient all work
services.AddTransient<OcrEngine>();

// Or skip the wrapper entirely and inject IronTesseract directly
services.AddTransient<IronTesseract>();
Imports IronOcr
Imports Microsoft.Extensions.DependencyInjection

' IronTesseract has lightweight initialization — no model loading on construction
Public Class OcrEngine
    Public Function Read(imagePath As String) As String
        Return New IronTesseract().Read(imagePath).Text
    End Function
End Class

' Flexible registration — singleton, scoped, or transient all work
services.AddTransient(Of OcrEngine)()

' Or skip the wrapper entirely and inject IronTesseract directly
services.AddTransient(Of IronTesseract)()
$vbLabelText   $csharpLabel

El Shift de un modelo de instancia única forzada a un modelo de vida útil flexible es significativo. El coste de construcción de PaddleSharp determina la duración de su decisión sobre el servicio;IronOCR te permite elegir en función de las necesidades de multihilo y aislamiento de solicitudes de tu aplicación. La guía de configuración de IronTesseract abarca las opciones de configuración que se aplican a nivel de instancia.

Migración del pipeline de preprocesamiento de OpenCV

Los equipos de PaddleSharp que trabajan con escaneos de baja calidad suelen crear un proceso de preprocesamiento con OpenCV antes de usar el motor OCR. Este proceso requiere conocer la API de OpenCV, que es mucho más extensa que la que necesita cualquier tarea de preprocesamiento OCR. Las operaciones comunes —desviación, eliminación de ruido, estiramiento de contraste— requieren múltiples operaciones Mat y una gestión cuidadosa de la memoria con bloques using para prevenir fugas de memoria nativa.

Enfoque OCR de PaddleSharp:

// Simplified — see OpenCvSharp documentation for full API
using OpenCvSharp;
using Sdcb.PaddleOCR;

public string ReadWithPreprocessing(string imagePath, PaddleOcrAll ocr)
{
    using var original = Cv2.ImRead(imagePath);

    // Step 1: Grayscale conversion
    using var gray = new Mat();
    Cv2.CvtColor(original, gray, ColorConversionCodes.BGR2GRAY);

    // Step 2: Denoise (Gaussian blur to reduce noise)
    using var denoised = new Mat();
    Cv2.GaussianBlur(gray, denoised, new Size(3, 3), 0);

    // Step 3: Adaptive threshold for binarization
    using var binary = new Mat();
    Cv2.AdaptiveThreshold(denoised, binary, 255,
        AdaptiveThresholdTypes.GaussianC, ThresholdTypes.Binary, 11, 2);

    // Step 4: Deskew — requires custom rotation detection logic (not shown)
    // Several dozen lines of custom Mat operations

    var result = ocr.Run(binary);
    return string.Join(" ", result.Regions.Select(r => r.Text));
    // Each Mat must be disposed; missing a using block leaks native memory
}
// Simplified — see OpenCvSharp documentation for full API
using OpenCvSharp;
using Sdcb.PaddleOCR;

public string ReadWithPreprocessing(string imagePath, PaddleOcrAll ocr)
{
    using var original = Cv2.ImRead(imagePath);

    // Step 1: Grayscale conversion
    using var gray = new Mat();
    Cv2.CvtColor(original, gray, ColorConversionCodes.BGR2GRAY);

    // Step 2: Denoise (Gaussian blur to reduce noise)
    using var denoised = new Mat();
    Cv2.GaussianBlur(gray, denoised, new Size(3, 3), 0);

    // Step 3: Adaptive threshold for binarization
    using var binary = new Mat();
    Cv2.AdaptiveThreshold(denoised, binary, 255,
        AdaptiveThresholdTypes.GaussianC, ThresholdTypes.Binary, 11, 2);

    // Step 4: Deskew — requires custom rotation detection logic (not shown)
    // Several dozen lines of custom Mat operations

    var result = ocr.Run(binary);
    return string.Join(" ", result.Regions.Select(r => r.Text));
    // Each Mat must be disposed; missing a using block leaks native memory
}
Imports OpenCvSharp
Imports Sdcb.PaddleOCR

Public Function ReadWithPreprocessing(imagePath As String, ocr As PaddleOcrAll) As String
    Using original As Mat = Cv2.ImRead(imagePath)

        ' Step 1: Grayscale conversion
        Using gray As New Mat()
            Cv2.CvtColor(original, gray, ColorConversionCodes.BGR2GRAY)

            ' Step 2: Denoise (Gaussian blur to reduce noise)
            Using denoised As New Mat()
                Cv2.GaussianBlur(gray, denoised, New Size(3, 3), 0)

                ' Step 3: Adaptive threshold for binarization
                Using binary As New Mat()
                    Cv2.AdaptiveThreshold(denoised, binary, 255, AdaptiveThresholdTypes.GaussianC, ThresholdTypes.Binary, 11, 2)

                    ' Step 4: Deskew — requires custom rotation detection logic (not shown)
                    ' Several dozen lines of custom Mat operations

                    Dim result = ocr.Run(binary)
                    Return String.Join(" ", result.Regions.Select(Function(r) r.Text))
                End Using
            End Using
        End Using
    End Using
End Function
$vbLabelText   $csharpLabel

Enfoque IronOCR:

using IronOcr;

public string ReadWithPreprocessing(string imagePath)
{
    using var input = new OcrInput();
    input.LoadImage(imagePath);

    // Named operations replace OpenCV knowledge requirements
    input.Deskew();
    input.DeNoise();
    input.Contrast();
    input.Binarize();

    var result = new IronTesseract().Read(input);
    return result.Text;
    // OcrInput implements IDisposable; using block handles cleanup
}
using IronOcr;

public string ReadWithPreprocessing(string imagePath)
{
    using var input = new OcrInput();
    input.LoadImage(imagePath);

    // Named operations replace OpenCV knowledge requirements
    input.Deskew();
    input.DeNoise();
    input.Contrast();
    input.Binarize();

    var result = new IronTesseract().Read(input);
    return result.Text;
    // OcrInput implements IDisposable; using block handles cleanup
}
Imports IronOcr

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

        ' Named operations replace OpenCV knowledge requirements
        input.Deskew()
        input.DeNoise()
        input.Contrast()
        input.Binarize()

        Dim result = New IronTesseract().Read(input)
        Return result.Text
        ' OcrInput implements IDisposable; using block handles cleanup
    End Using
End Function
$vbLabelText   $csharpLabel

No hay asignaciones Mat. No se conocen los parámetros del umbral adaptativo. No se requiere cálculo personalizado de rotación de inclinación. El mismo proceso de preprocesamiento que requería entre 30 y 50 líneas de código de OpenCV se reduce a cuatro llamadas a métodos. La guía de corrección de la calidad de imagen documenta todos los filtros disponibles con ejemplos de antes y después. Para documentos con mucho ruido de fondo, input.DeepCleanBackgroundNoise() va más allá de DeNoise() sin ningún parámetro adicional.

Para los equipos cuyos requisitos de preprocesamiento no son estándar, el asistente de filtros proporciona una herramienta interactiva para evaluar combinaciones de filtros en sus tipos de documentos específicos antes de escribir el código.

Eliminación de la selección de backend

PaddleSharp expone el backend de inferencia como una cuestión de nivel de aplicación. Una implementación que necesita ejecutarse en una máquina virtual en la nube solo con CPU utiliza un código de inicialización diferente al de una destinada a una estación de trabajo con GPU o a un dispositivo perimetral compatible con Intel OpenVINO. Esa lógica condicional suele terminar en el código de inicio de la aplicación, en la comprobación de variables de entorno o en indicadores de características; un trabajo de infraestructura que no tiene nada que ver con la lectura de texto de las imágenes.

Enfoque OCR de PaddleSharp:

// Simplified — see Sdcb.PaddleInference documentation for full API
using Sdcb.PaddleInference;
using Sdcb.PaddleOCR;

public PaddleOcrAll CreateOcrEngine(string backendMode)
{
    // Each backend requires a different NuGet runtime package installed
    switch (backendMode)
    {
        case "gpu":
            // Requires: Sdcb.PaddleInference.runtime.win64.cuda
            // Requires: CUDA toolkit + cuDNN installed on host
            var gpuConfig = PaddleConfig.FromModelDir("models/");
            gpuConfig.EnableGpu(500, deviceId: 0); // Simplified
            break;

        case "openvino":
            // Requires: Sdcb.PaddleInference.runtime.win64.mkl
            var oviConfig = PaddleConfig.FromModelDir("models/");
            oviConfig.EnableMkldnn(); // Simplified
            break;

        default:
            // CPU-only — still requires platform-specific runtime package
            var cpuConfig = PaddleConfig.FromModelDir("models/");
            cpuConfig.SetCpuMathLibraryNumThreads(Environment.ProcessorCount);
            break;
    }

    // Backend-specific config passed to model constructors — Simplified
    var detModel = LocalFullModels.ChineseV3.DetectionModel;
    var clsModel = LocalFullModels.ChineseV3.ClassifierModel;
    var recModel = LocalFullModels.ChineseV3.RecognitionModel;
    return new PaddleOcrAll(detModel, clsModel, recModel);
}
// Simplified — see Sdcb.PaddleInference documentation for full API
using Sdcb.PaddleInference;
using Sdcb.PaddleOCR;

public PaddleOcrAll CreateOcrEngine(string backendMode)
{
    // Each backend requires a different NuGet runtime package installed
    switch (backendMode)
    {
        case "gpu":
            // Requires: Sdcb.PaddleInference.runtime.win64.cuda
            // Requires: CUDA toolkit + cuDNN installed on host
            var gpuConfig = PaddleConfig.FromModelDir("models/");
            gpuConfig.EnableGpu(500, deviceId: 0); // Simplified
            break;

        case "openvino":
            // Requires: Sdcb.PaddleInference.runtime.win64.mkl
            var oviConfig = PaddleConfig.FromModelDir("models/");
            oviConfig.EnableMkldnn(); // Simplified
            break;

        default:
            // CPU-only — still requires platform-specific runtime package
            var cpuConfig = PaddleConfig.FromModelDir("models/");
            cpuConfig.SetCpuMathLibraryNumThreads(Environment.ProcessorCount);
            break;
    }

    // Backend-specific config passed to model constructors — Simplified
    var detModel = LocalFullModels.ChineseV3.DetectionModel;
    var clsModel = LocalFullModels.ChineseV3.ClassifierModel;
    var recModel = LocalFullModels.ChineseV3.RecognitionModel;
    return new PaddleOcrAll(detModel, clsModel, recModel);
}
Imports Sdcb.PaddleInference
Imports Sdcb.PaddleOCR

Public Function CreateOcrEngine(ByVal backendMode As String) As PaddleOcrAll
    ' Each backend requires a different NuGet runtime package installed
    Select Case backendMode
        Case "gpu"
            ' Requires: Sdcb.PaddleInference.runtime.win64.cuda
            ' Requires: CUDA toolkit + cuDNN installed on host
            Dim gpuConfig As PaddleConfig = PaddleConfig.FromModelDir("models/")
            gpuConfig.EnableGpu(500, deviceId:=0) ' Simplified

        Case "openvino"
            ' Requires: Sdcb.PaddleInference.runtime.win64.mkl
            Dim oviConfig As PaddleConfig = PaddleConfig.FromModelDir("models/")
            oviConfig.EnableMkldnn() ' Simplified

        Case Else
            ' CPU-only — still requires platform-specific runtime package
            Dim cpuConfig As PaddleConfig = PaddleConfig.FromModelDir("models/")
            cpuConfig.SetCpuMathLibraryNumThreads(Environment.ProcessorCount)
    End Select

    ' Backend-specific config passed to model constructors — Simplified
    Dim detModel = LocalFullModels.ChineseV3.DetectionModel
    Dim clsModel = LocalFullModels.ChineseV3.ClassifierModel
    Dim recModel = LocalFullModels.ChineseV3.RecognitionModel
    Return New PaddleOcrAll(detModel, clsModel, recModel)
End Function
$vbLabelText   $csharpLabel

Enfoque IronOCR:

using IronOcr;

// No backend selection. No switch statement. No environment variable check.
// The same code runs on CPU-only VMs, GPU workstations, and ARM devices.
public IronTesseract CreateOcrEngine()
{
    return new IronTesseract();
}

// Parallel processing across CPU cores — no GPU configuration required
public IEnumerable<string> ReadBatch(IEnumerable<string> imagePaths)
{
    var results = new System.Collections.Concurrent.ConcurrentBag<string>();
    Parallel.ForEach(imagePaths, path =>
    {
        var result = new IronTesseract().Read(path);
        results.Add(result.Text);
    });
    return results;
}
using IronOcr;

// No backend selection. No switch statement. No environment variable check.
// The same code runs on CPU-only VMs, GPU workstations, and ARM devices.
public IronTesseract CreateOcrEngine()
{
    return new IronTesseract();
}

// Parallel processing across CPU cores — no GPU configuration required
public IEnumerable<string> ReadBatch(IEnumerable<string> imagePaths)
{
    var results = new System.Collections.Concurrent.ConcurrentBag<string>();
    Parallel.ForEach(imagePaths, path =>
    {
        var result = new IronTesseract().Read(path);
        results.Add(result.Text);
    });
    return results;
}
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Threading.Tasks

Public Class OcrProcessor
    ' No backend selection. No switch statement. No environment variable check.
    ' The same code runs on CPU-only VMs, GPU workstations, and ARM devices.
    Public Function CreateOcrEngine() As IronTesseract
        Return New IronTesseract()
    End Function

    ' Parallel processing across CPU cores — no GPU configuration required
    Public Function ReadBatch(imagePaths As IEnumerable(Of String)) As IEnumerable(Of String)
        Dim results As New ConcurrentBag(Of String)()
        Parallel.ForEach(imagePaths, Sub(path)
                                         Dim result = New IronTesseract().Read(path)
                                         results.Add(result.Text)
                                     End Sub)
        Return results
    End Function
End Class
$vbLabelText   $csharpLabel

El patrón Parallel.ForEach aquí es seguro para hilos desde el principio. Cada instancia IronTesseract es independiente y no tiene estado nativo compartido. Para los equipos cuyo despliegue de PaddleSharp dedica tiempo a gestionar las condiciones del backend, esa simplificación también supone una mejora en la fiabilidad del despliegue: el mismo artefacto de compilación se ejecuta en todas partes sin código de detección de hardware. La guía de optimización de velocidad abarca las opciones de configuración para escenarios donde el rendimiento es crucial.

Migración de reconocimiento de tablas

La extracción de tablas en PaddleSharp requiere un modelo de reconocimiento de tablas específico: un cuarto archivo de modelo que se suma al conjunto estándar de detección, clasificación y reconocimiento. El modelo de tabla utiliza una llamada a la API independiente y devuelve su propia estructura de resultados. Los equipos que desarrollan sistemas de procesamiento de facturas, formularios u hojas de cálculo mantienen dos rutas de inicialización paralelas y dos estrategias de análisis de resultados.

Enfoque OCR de PaddleSharp:

// Simplified — see Sdcb.PaddleOCR documentation for full API
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using OpenCvSharp;

public class TableRecognitionService
{
    // Standard OCR engine — 3 models
    private readonly PaddleOcrAll _textOcr;

    // Table engine — 4th model, separate initialization
    // private readonly PaddleOcrTable _tableOcr; // Simplified

    public TableRecognitionService()
    {
        var detModel = LocalFullModels.ChineseV3.DetectionModel;
        var clsModel = LocalFullModels.ChineseV3.ClassifierModel;
        var recModel = LocalFullModels.ChineseV3.RecognitionModel;
        _textOcr = new PaddleOcrAll(detModel, clsModel, recModel);

        // Table model: separate download, separate version tracking
        // var tableModel = LocalFullModels.TableEnV2.Model; // Simplified
        // _tableOcr = new PaddleOcrTable(tableModel); // Simplified
    }

    public void ProcessDocument(string imagePath)
    {
        using var image = Cv2.ImRead(imagePath);

        // Text extraction path
        var textResult = _textOcr.Run(image);
        var text = string.Join(" ", textResult.Regions.Select(r => r.Text));

        // Table extraction path — different API, different result structure
        // var tableResult = _tableOcr.Run(image); // Simplified
        // foreach (var cell in tableResult.Cells) { ... } // Simplified
    }
}
// Simplified — see Sdcb.PaddleOCR documentation for full API
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using OpenCvSharp;

public class TableRecognitionService
{
    // Standard OCR engine — 3 models
    private readonly PaddleOcrAll _textOcr;

    // Table engine — 4th model, separate initialization
    // private readonly PaddleOcrTable _tableOcr; // Simplified

    public TableRecognitionService()
    {
        var detModel = LocalFullModels.ChineseV3.DetectionModel;
        var clsModel = LocalFullModels.ChineseV3.ClassifierModel;
        var recModel = LocalFullModels.ChineseV3.RecognitionModel;
        _textOcr = new PaddleOcrAll(detModel, clsModel, recModel);

        // Table model: separate download, separate version tracking
        // var tableModel = LocalFullModels.TableEnV2.Model; // Simplified
        // _tableOcr = new PaddleOcrTable(tableModel); // Simplified
    }

    public void ProcessDocument(string imagePath)
    {
        using var image = Cv2.ImRead(imagePath);

        // Text extraction path
        var textResult = _textOcr.Run(image);
        var text = string.Join(" ", textResult.Regions.Select(r => r.Text));

        // Table extraction path — different API, different result structure
        // var tableResult = _tableOcr.Run(image); // Simplified
        // foreach (var cell in tableResult.Cells) { ... } // Simplified
    }
}
Imports Sdcb.PaddleOCR
Imports Sdcb.PaddleOCR.Models
Imports OpenCvSharp

Public Class TableRecognitionService
    ' Standard OCR engine — 3 models
    Private ReadOnly _textOcr As PaddleOcrAll

    ' Table engine — 4th model, separate initialization
    ' Private ReadOnly _tableOcr As PaddleOcrTable ' Simplified

    Public Sub New()
        Dim detModel = LocalFullModels.ChineseV3.DetectionModel
        Dim clsModel = LocalFullModels.ChineseV3.ClassifierModel
        Dim recModel = LocalFullModels.ChineseV3.RecognitionModel
        _textOcr = New PaddleOcrAll(detModel, clsModel, recModel)

        ' Table model: separate download, separate version tracking
        ' Dim tableModel = LocalFullModels.TableEnV2.Model ' Simplified
        ' _tableOcr = New PaddleOcrTable(tableModel) ' Simplified
    End Sub

    Public Sub ProcessDocument(imagePath As String)
        Using image = Cv2.ImRead(imagePath)
            ' Text extraction path
            Dim textResult = _textOcr.Run(image)
            Dim text = String.Join(" ", textResult.Regions.Select(Function(r) r.Text))

            ' Table extraction path — different API, different result structure
            ' Dim tableResult = _tableOcr.Run(image) ' Simplified
            ' For Each cell In tableResult.Cells ' Simplified
            ' ... 
            ' Next
        End Using
    End Sub
End Class
$vbLabelText   $csharpLabel

Enfoque IronOCR:

using IronOcr;

public class TableRecognitionService
{
    // One engine handles both text and table regions
    public void ProcessDocument(string imagePath)
    {
        var ocr = new IronTesseract();
        var result = ocr.Read(imagePath);

        // Structured hierarchy: pages → paragraphs → lines → words
        foreach (var page in result.Pages)
        {
            foreach (var paragraph in page.Paragraphs)
            {
                Console.WriteLine($"Block at ({paragraph.X},{paragraph.Y}): {paragraph.Text}");
            }
        }

        Console.WriteLine($"Full document text: {result.Text}");
    }
}
using IronOcr;

public class TableRecognitionService
{
    // One engine handles both text and table regions
    public void ProcessDocument(string imagePath)
    {
        var ocr = new IronTesseract();
        var result = ocr.Read(imagePath);

        // Structured hierarchy: pages → paragraphs → lines → words
        foreach (var page in result.Pages)
        {
            foreach (var paragraph in page.Paragraphs)
            {
                Console.WriteLine($"Block at ({paragraph.X},{paragraph.Y}): {paragraph.Text}");
            }
        }

        Console.WriteLine($"Full document text: {result.Text}");
    }
}
Imports IronOcr

Public Class TableRecognitionService
    ' One engine handles both text and table regions
    Public Sub ProcessDocument(imagePath As String)
        Dim ocr As New IronTesseract()
        Dim result = ocr.Read(imagePath)

        ' Structured hierarchy: pages → paragraphs → lines → words
        For Each page In result.Pages
            For Each paragraph In page.Paragraphs
                Console.WriteLine($"Block at ({paragraph.X},{paragraph.Y}): {paragraph.Text}")
            Next
        Next

        Console.WriteLine($"Full document text: {result.Text}")
    End Sub
End Class
$vbLabelText   $csharpLabel

Para documentos en los que la estructura de la tabla debe extraerse como filas y columnas,IronOCR proporciona una función específica para la extracción de tablas:

using IronOcr;

var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("invoice-with-table.jpg");

var result = ocr.Read(input);

// Access structured page layout for table region extraction
foreach (var page in result.Pages)
{
    foreach (var line in page.Lines)
    {
        // Lines within a table region preserve spatial ordering
        Console.WriteLine($"Row text: {line.Text} | Y position: {line.Y}");
        foreach (var word in line.Words)
        {
            Console.WriteLine($"  Cell: '{word.Text}' at X={word.X}");
        }
    }
}
using IronOcr;

var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("invoice-with-table.jpg");

var result = ocr.Read(input);

// Access structured page layout for table region extraction
foreach (var page in result.Pages)
{
    foreach (var line in page.Lines)
    {
        // Lines within a table region preserve spatial ordering
        Console.WriteLine($"Row text: {line.Text} | Y position: {line.Y}");
        foreach (var word in line.Words)
        {
            Console.WriteLine($"  Cell: '{word.Text}' at X={word.X}");
        }
    }
}
Imports IronOcr

Dim ocr As New IronTesseract()
Using input As New OcrInput()
    input.LoadImage("invoice-with-table.jpg")

    Dim result = ocr.Read(input)

    ' Access structured page layout for table region extraction
    For Each page In result.Pages
        For Each line In page.Lines
            ' Lines within a table region preserve spatial ordering
            Console.WriteLine($"Row text: {line.Text} | Y position: {line.Y}")
            For Each word In line.Words
                Console.WriteLine($"  Cell: '{word.Text}' at X={word.X}")
            Next
        Next
    Next
End Using
$vbLabelText   $csharpLabel

Se eliminó una descarga de modelo. Se eliminó una ruta de inicialización. La jerarquía de resultados estructurada en IronOCR, con coordenadas X/Y a nivel de palabra, proporciona los datos posicionales necesarios para reconstruir filas y columnas de tablas sin un modelo de reconocimiento independiente. La guía de lectura de tablas y la guía de lectura de resultados cubren la API completa de salida estructurada.

Archivos PDF con capacidad de búsqueda generados a partir de documentos escaneados.

PaddleSharp produce cadenas de texto y nada más. Para crear un archivo de documentos que permita realizar búsquedas de texto en archivos PDF escaneados, es necesario integrar una biblioteca de PDF independiente, escribir una capa de superposición de texto y gestionar ambas bibliotecas de forma conjunta. Los equipos que han aceptado esa limitación a menudo descubren que es el detonante de la migración: el esfuerzo que supone la integración de las dos bibliotecas supera el esfuerzo de cambiar de proveedor de OCR.

Enfoque OCR de PaddleSharp:

// Simplified — PaddleSharp has no PDF output. Requires a separate PDF library.
// Example of what teams typically build:

// using Sdcb.PaddleOCR;
// using SomePdfLibrary; // Third dependency to produce searchable PDF

public void ArchiveScannedDocument(string imagePath, string outputPdfPath)
{
    // Step 1: OCR via PaddleSharp — produces text only
    // var text = _ocr.Run(Cv2.ImRead(imagePath));

    // Step 2: Build a PDF with text overlay using a separate PDF library
    // Requires: text positions mapped to PDF coordinate space
    // Requires: image embedded as background
    // Requires: invisible text layer positioned over image
    // ~50–100 lines of PDF construction code
    throw new NotImplementedException("Requires a separate PDF library");
}
// Simplified — PaddleSharp has no PDF output. Requires a separate PDF library.
// Example of what teams typically build:

// using Sdcb.PaddleOCR;
// using SomePdfLibrary; // Third dependency to produce searchable PDF

public void ArchiveScannedDocument(string imagePath, string outputPdfPath)
{
    // Step 1: OCR via PaddleSharp — produces text only
    // var text = _ocr.Run(Cv2.ImRead(imagePath));

    // Step 2: Build a PDF with text overlay using a separate PDF library
    // Requires: text positions mapped to PDF coordinate space
    // Requires: image embedded as background
    // Requires: invisible text layer positioned over image
    // ~50–100 lines of PDF construction code
    throw new NotImplementedException("Requires a separate PDF library");
}
Public Sub ArchiveScannedDocument(imagePath As String, outputPdfPath As String)
    ' Step 1: OCR via PaddleSharp — produces text only
    ' Dim text = _ocr.Run(Cv2.ImRead(imagePath))

    ' Step 2: Build a PDF with text overlay using a separate PDF library
    ' Requires: text positions mapped to PDF coordinate space
    ' Requires: image embedded as background
    ' Requires: invisible text layer positioned over image
    ' ~50–100 lines of PDF construction code
    Throw New NotImplementedException("Requires a separate PDF library")
End Sub
$vbLabelText   $csharpLabel

Enfoque IronOCR:

using IronOcr;

public void ArchiveScannedDocument(string imagePath, string outputPdfPath)
{
    using var input = new OcrInput();
    input.LoadImage(imagePath);
    input.Deskew();   // Straighten scan before archiving
    input.DeNoise();  // Clean up scan artifacts

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

    // One call: OCR + searchable PDF with text layer + image background
    result.SaveAsSearchablePdf(outputPdfPath);
}

// Multi-page document — same pattern
public void ArchiveMultiPageDocument(string[] imageFiles, string outputPdfPath)
{
    using var input = new OcrInput();
    foreach (var file in imageFiles)
        input.LoadImage(file);

    var result = new IronTesseract().Read(input);
    result.SaveAsSearchablePdf(outputPdfPath);
}
using IronOcr;

public void ArchiveScannedDocument(string imagePath, string outputPdfPath)
{
    using var input = new OcrInput();
    input.LoadImage(imagePath);
    input.Deskew();   // Straighten scan before archiving
    input.DeNoise();  // Clean up scan artifacts

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

    // One call: OCR + searchable PDF with text layer + image background
    result.SaveAsSearchablePdf(outputPdfPath);
}

// Multi-page document — same pattern
public void ArchiveMultiPageDocument(string[] imageFiles, string outputPdfPath)
{
    using var input = new OcrInput();
    foreach (var file in imageFiles)
        input.LoadImage(file);

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

Public Sub ArchiveScannedDocument(imagePath As String, outputPdfPath As String)
    Using input As New OcrInput()
        input.LoadImage(imagePath)
        input.Deskew()   ' Straighten scan before archiving
        input.DeNoise()  ' Clean up scan artifacts

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

        ' One call: OCR + searchable PDF with text layer + image background
        result.SaveAsSearchablePdf(outputPdfPath)
    End Using
End Sub

' Multi-page document — same pattern
Public Sub ArchiveMultiPageDocument(imageFiles As String(), outputPdfPath As String)
    Using input As New OcrInput()
        For Each file In imageFiles
            input.LoadImage(file)
        Next

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

No hay biblioteca de PDF. Sin mapeo de coordenadas. Sin posicionamiento de capa de texto. El formato de salida PDF con capacidad de búsqueda de IronOCR incrusta una capa de texto invisible sobre la imagen original, lo que produce un archivo que es visualmente fiel al documento escaneado y que además permite realizar búsquedas de texto completas. El manual en formato PDF, que permite realizar búsquedas, abarca la selección de páginas, las opciones de calidad y el control de metadatos.

Referencia de mapeo de la API de OCR de PaddleSharp a IronOCR

PaddleSharp OCR IronOCR
Sdcb.PaddleOCR (namespace) IronOcr (namespace)
Sdcb.PaddleInference (namespace) No es necesario: se configura automáticamente.
PaddleOcrAll IronTesseract
new PaddleOcrAll(det, cls, rec) new IronTesseract()
LocalFullModels.ChineseV3.DetectionModel Sin equivalente: no hay selección de modelo.
LocalFullModels.ChineseV3.ClassifierModel Sin equivalente: no hay selección de modelo.
LocalFullModels.ChineseV3.RecognitionModel Sin equivalente: no hay selección de modelo.
PaddleConfig.FromModelDir() No hay equivalente: no hay objeto de configuración
config.EnableGpu(memMB, deviceId) No hay equivalente: el backend es automático.
config.EnableMkldnn() No hay equivalente: el backend es automático.
config.SetCpuMathLibraryNumThreads(n) No existe un equivalente; se gestiona internamente.
Cv2.ImRead(path) (OpenCV carga) input.LoadImage(path)
ocr.Run(mat) ocr.Read(input) o ocr.Read("file.jpg")
result.Regions result.Pages[0].Words o result.Pages[0].Lines
region.Text word.Text, line.Text, paragraph.Text
region.Rect.Center.X/.Y word.X, word.Y
region.Score (confianza) word.Confidence, result.Confidence
Intercambio de lenguaje a nivel de modelo ocr.Language = OcrLanguage.French
Modelo de tabla (descarga por separado) Jerarquía de resultados estructurada integrada
Cv2.CvtColor(..., GRAY) input.Binarize() o input.Contrast()
Cv2.GaussianBlur(...) input.DeNoise()
No se genera un archivo PDF con capacidad de búsqueda. result.SaveAsSearchablePdf("output.pdf")

Problemas comunes de migración y soluciones

Problema 1: La dependencia de OpenCV no se descarga correctamente.

PaddleSharp OCR: OpenCvSharp4.runtime.win y paquetes de tiempo de ejecución similares específicos de la plataforma instalan DLL nativas no gestionadas. Estas DLL pueden impedir una limpieza adecuada en algunos escenarios de alojamiento, en particular al reciclar grupos de aplicaciones de IIS, y provocar fallos en la carga de ensamblados cuando se hace referencia al paquete de tiempo de ejecución de plataforma incorrecto durante la compilación. Para eliminarlas, es necesario desinstalar el paquete NuGet y borrar los binarios nativos almacenados en caché en el directorio de salida.

Solución: Después de eliminar los paquetes OpenCvSharp4 y OpenCvSharp4.runtime.*, limpia el directorio de salida de la construcción antes de reconstruir:

dotnet remove package OpenCvSharp4
dotnet remove package OpenCvSharp4.runtime.win
dotnet clean
dotnet build
dotnet remove package OpenCvSharp4
dotnet remove package OpenCvSharp4.runtime.win
dotnet clean
dotnet build
SHELL

IronOCR agrupa internamente sus dependencias nativas y gestiona el ciclo de vida no administrado. No es necesario seleccionar ningún paquete de tiempo de ejecución específico de la plataforma. La guía de configuración de IronTesseract documenta los requisitos de la plataforma que IronOCR gestiona automáticamente.

Problema 2: Archivos del modelo que permanecen en el disco después de la migración.

PaddleSharp OCR: Los archivos de modelo descargados por PaddleSharp (detección, clasificación, reconocimiento y cualquier modelo de tabla) generalmente se almacenan en un directorio models/ relativo a la aplicación o en una ruta configurada. Estos archivos no se eliminan al desinstalar el paquete NuGet . En una imagen de Docker, añaden un tamaño de capa innecesario. En una canalización de despliegue, los archivos de modelo obsoletos en rutas antiguas pueden provocar fallos de inicio si algún código de inicialización residual hace referencia a ellos.

Solución: Eliminar explícitamente los directorios del modelo como parte de la migración. Revise la configuración de inicio en busca de referencias a rutas:

# Locate model directory references in application code
grep -r "LocalFullModels\|ModelPath\|models/" --include="*.cs" .
grep -r "DetectionModel\|RecognitionModel\|ClassifierModel" --include="*.cs" .
# Locate model directory references in application code
grep -r "LocalFullModels\|ModelPath\|models/" --include="*.cs" .
grep -r "DetectionModel\|RecognitionModel\|ClassifierModel" --include="*.cs" .
SHELL

Una vez eliminadas las referencias al modelo e inicializado IronOCR, elimine el directorio del modelo del repositorio y del contexto de compilación de Docker.

Problema 3: El supuesto de ciclo de vida único se rompe después de la migración.

PaddleSharp OCR: PaddleOcrAll fue registrado como un singleton porque su costo de construcción hacía impráctica la instanciación por solicitud. El código de migración que integra IronOCR en el mismo registro singleton introduce un intercambio de estado innecesario entre las solicitudes. Mientras que IronTesseract es seguro para hilos cuando se usa simultáneamente, no es necesario compartir una sola instancia — cada instancia es independiente.

Solución: Evaluar si el registro singleton tiene algún propósito más allá del rendimiento. Para la mayoría de las aplicaciones ASP.NET Core , el registro transitorio es la opción más limpia con IronOCR:

// PaddleSharp — forced singleton due to construction cost
services.AddSingleton<PaddleOcrAll>(sp =>
{
    var det = LocalFullModels.ChineseV3.DetectionModel;  // Simplified
    var cls = LocalFullModels.ChineseV3.ClassifierModel; // Simplified
    var rec = LocalFullModels.ChineseV3.RecognitionModel; // Simplified
    return new PaddleOcrAll(det, cls, rec);
});

//IronOCR— transient works; no expensive construction
services.AddTransient<IronTesseract>();
// PaddleSharp — forced singleton due to construction cost
services.AddSingleton<PaddleOcrAll>(sp =>
{
    var det = LocalFullModels.ChineseV3.DetectionModel;  // Simplified
    var cls = LocalFullModels.ChineseV3.ClassifierModel; // Simplified
    var rec = LocalFullModels.ChineseV3.RecognitionModel; // Simplified
    return new PaddleOcrAll(det, cls, rec);
});

//IronOCR— transient works; no expensive construction
services.AddTransient<IronTesseract>();
Imports Microsoft.Extensions.DependencyInjection

' PaddleSharp — forced singleton due to construction cost
services.AddSingleton(Of PaddleOcrAll)(Function(sp)
    Dim det = LocalFullModels.ChineseV3.DetectionModel ' Simplified
    Dim cls = LocalFullModels.ChineseV3.ClassifierModel ' Simplified
    Dim rec = LocalFullModels.ChineseV3.RecognitionModel ' Simplified
    Return New PaddleOcrAll(det, cls, rec)
End Function)

' IronOCR— transient works; no expensive construction
services.AddTransient(Of IronTesseract)()
$vbLabelText   $csharpLabel

Para escenarios de procesamiento por lotes de alto rendimiento donde se desea la reutilización explícita de instancias, un patrón singleton o pooled sigue funcionando, pero es una elección de rendimiento, no un requisito de corrección.

Problema 4: Ya no es necesario ordenar los resultados por región.

PaddleSharp OCR: result.Regions devuelve regiones de texto detectadas en orden de detección, que no necesariamente coincide con el orden de lectura (de izquierda a derecha, de arriba a abajo). Los equipos típicamente aplican una clasificación por .Rect.Center.Y luego .Rect.Center.X antes de unir el texto de la región — un patrón que aparece en casi todas las implementaciones de extracción de texto de PaddleSharp. Migrar este patrón literalmente a IronOCR produce código redundante.

Solución:IronOCR devuelve los resultados en orden de lectura por defecto. Eliminar la ordenación:

// PaddleSharp — manual reading-order sort required
var text = string.Join("\n", result.Regions
    .OrderBy(r => r.Rect.Center.Y)
    .ThenBy(r => r.Rect.Center.X)
    .Select(r => r.Text));

//IronOCR— result.Text is already in reading order; no sort needed
var text = result.Text;

// For word-level access with position, use the structured hierarchy directly
foreach (var word in result.Pages[0].Words)
{
    Console.WriteLine($"{word.Text} at ({word.X},{word.Y})");
}
// PaddleSharp — manual reading-order sort required
var text = string.Join("\n", result.Regions
    .OrderBy(r => r.Rect.Center.Y)
    .ThenBy(r => r.Rect.Center.X)
    .Select(r => r.Text));

//IronOCR— result.Text is already in reading order; no sort needed
var text = result.Text;

// For word-level access with position, use the structured hierarchy directly
foreach (var word in result.Pages[0].Words)
{
    Console.WriteLine($"{word.Text} at ({word.X},{word.Y})");
}
Imports System
Imports System.Linq

' PaddleSharp — manual reading-order sort required
Dim text = String.Join(vbLf, result.Regions _
    .OrderBy(Function(r) r.Rect.Center.Y) _
    .ThenBy(Function(r) r.Rect.Center.X) _
    .Select(Function(r) r.Text))

' IronOCR— result.Text is already in reading order; no sort needed
text = result.Text

' For word-level access with position, use the structured hierarchy directly
For Each word In result.Pages(0).Words
    Console.WriteLine($"{word.Text} at ({word.X},{word.Y})")
Next
$vbLabelText   $csharpLabel

Problema 5: Los paquetes condicionales del backend rompen la restauración.

PaddleSharp OCR: Algunas configuraciones de PaddleSharp hacen referencia condicionalmente a diferentes paquetes Sdcb.PaddleInference.runtime.* en función del entorno objetivo (CUDA para GPU, MKL para OpenVINO, solo CPU). Esto a veces aparece como condiciones .csproj o como archivos de proyecto separados por objetivo de despliegue. La matriz de compilación resultante interrumpe las canalizaciones de CI cuando se restaura un conjunto de paquetes incorrecto.

Solución: Después de eliminar los paquetes de PaddleSharp, audita el archivo .csproj para bloques PackageReference condicionales que hagan referencia a cualquier paquete Sdcb.* o OpenCvSharp* y elimínalos por completo:

grep -n "Sdcb\|OpenCvSharp\|PaddleInference" *.csproj
grep -n "Sdcb\|OpenCvSharp\|PaddleInference" *.csproj
SHELL

IronOCR utiliza una única referencia de paquete IronOcr sin condicionales de plataforma. El mismo paquete se restaura correctamente en Windows, Linux y macOS.

Problema 6: La estructura de resultados de tabla no tiene un equivalente directo.

PaddleSharp OCR: PaddleOcrTable devuelve una estructura basada en celdas con índices de fila y columna por cada celda reconocida. El código que consume esta estructura típicamente construye un arreglo bidimensional indexado por (row, column).IronOCR no proporciona una estructura de índice de celdas idéntica, sino que proporciona coordenadas de palabras y líneas que requieren agrupación espacial para reconstruir una cuadrícula de celdas.

Solución: Reconstruir la estructura de la tabla a partir de las coordenadas de las palabras de IronOCR utilizando la agrupación por posición Y para las filas y la ordenación por posición X para las columnas. Para los formatos de tabla más comunes, la guía sobre cómo leer tablas ofrece un método de agrupación espacial. Para facturas estructuradas con posiciones de campo conocidas, la OCR basado en regiones con CropRectangle es un patrón más limpio que la extracción de tablas de página completa:

using IronOcr;

// Target specific table cells by region instead of full-page table detection
var totalAmountRegion = new CropRectangle(400, 600, 200, 30); // x, y, width, height
using var input = new OcrInput();
input.LoadImage("invoice.jpg", totalAmountRegion);

var result = new IronTesseract().Read(input);
Console.WriteLine($"Total: {result.Text}");
using IronOcr;

// Target specific table cells by region instead of full-page table detection
var totalAmountRegion = new CropRectangle(400, 600, 200, 30); // x, y, width, height
using var input = new OcrInput();
input.LoadImage("invoice.jpg", totalAmountRegion);

var result = new IronTesseract().Read(input);
Console.WriteLine($"Total: {result.Text}");
Imports IronOcr

' Target specific table cells by region instead of full-page table detection
Dim totalAmountRegion As New CropRectangle(400, 600, 200, 30) ' x, y, width, height
Using input As New OcrInput()
    input.LoadImage("invoice.jpg", totalAmountRegion)

    Dim result = New IronTesseract().Read(input)
    Console.WriteLine($"Total: {result.Text}")
End Using
$vbLabelText   $csharpLabel

Lista de verificación para la migración de OCR de PaddleSharp

Pre-Migración

Antes de eliminar los paquetes, revise todas las referencias a PaddleSharp en el código fuente:

# Find all PaddleSharp and PaddleInference usages
grep -rn "Sdcb\.PaddleOCR\|Sdcb\.PaddleInference" --include="*.cs" .

# Find OpenCV usages that will need replacement
grep -rn "OpenCvSharp\|Cv2\.\|using var.*Mat\b" --include="*.cs" .

# Find model path references and configuration
grep -rn "LocalFullModels\|ModelDir\|DetectionModel\|RecognitionModel\|ClassifierModel" --include="*.cs" .

# Find backend selection logic
grep -rn "EnableGpu\|EnableMkldnn\|PaddleConfig\|SetCpuMath" --include="*.cs" .

# Find table recognition usages
grep -rn "PaddleOcrTable\|TableModel\|table.*ocr\|ocr.*table" --include="*.cs" .

# Find result region access patterns that need updating
grep -rn "\.Regions\b\|Rect\.Center\|region\.Text" --include="*.cs" .
# Find all PaddleSharp and PaddleInference usages
grep -rn "Sdcb\.PaddleOCR\|Sdcb\.PaddleInference" --include="*.cs" .

# Find OpenCV usages that will need replacement
grep -rn "OpenCvSharp\|Cv2\.\|using var.*Mat\b" --include="*.cs" .

# Find model path references and configuration
grep -rn "LocalFullModels\|ModelDir\|DetectionModel\|RecognitionModel\|ClassifierModel" --include="*.cs" .

# Find backend selection logic
grep -rn "EnableGpu\|EnableMkldnn\|PaddleConfig\|SetCpuMath" --include="*.cs" .

# Find table recognition usages
grep -rn "PaddleOcrTable\|TableModel\|table.*ocr\|ocr.*table" --include="*.cs" .

# Find result region access patterns that need updating
grep -rn "\.Regions\b\|Rect\.Center\|region\.Text" --include="*.cs" .
SHELL

Inventaría los archivos del modelo en el disco y anota sus rutas. Inventaria todos los objetivos de despliegue y si alguno tiene condicionales NuGet específicos de GPU o OpenVINO en .csproj. Tenga en cuenta cualquier servicio registrado como único debido al costo de construcción de PaddleSharp.

Migración de código

  1. Elimina todos los paquetes NuGet Sdcb.PaddleOCR, Sdcb.PaddleInference, OpenCvSharp4 y OpenCvSharp4.runtime.* de cada archivo de proyecto.
  2. Instala el paquete NuGet IronOcr.
  3. Instala los paquetes NuGet de idiomas para los idiomas requeridos (por ejemplo, IronOcr.Languages.ChineseSimplified).
  4. Agrega IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; al inicio de la aplicación.
  5. Reemplaza todas las sentencias using Sdcb.PaddleOCR, using Sdcb.PaddleInference y using OpenCvSharp con using IronOcr.
  6. Reemplaza la instanciación PaddleOcrAll y la carga de modelos con new IronTesseract().
  7. Elimina todos los bloques de selección de backend PaddleConfig (condicionales de CPU, GPU, OpenVINO).
  8. Reemplaza las llamadas Cv2.ImRead(path) con input.LoadImage(path) usando OcrInput.
  9. Reemplaza las operaciones de preprocesamiento de OpenCV (CvtColor, GaussianBlur, Threshold, etc.) con métodos de filtro OcrInput (Deskew(), DeNoise(), Contrast(), Binarize()).
  10. Reemplaza las llamadas ocr.Run(mat) con ocr.Read(input).
  11. Reemplaza la enumeración result.Regions con result.Pages, result.Pages[n].Lines o result.Pages[n].Words.
  12. Elimina las cadenas de clasificación .OrderBy(r => r.Rect.Center.Y).ThenBy(r => r.Rect.Center.X) — el orden de lectura es automático.
  13. Reemplaza la inicialización PaddleOcrTable y el análisis de resultados con el enfoque de orientación basado en regiones OcrInput o agrupación de palabras basada en coordenadas.
  14. Agrega result.SaveAsSearchablePdf(path) en cualquier lugar que se requiera un archivo PDF buscatamo.
  15. Reevaluar los registros de vida útil del servicio: los registros únicos impulsados ​​por el costo de construcción de PaddleSharp normalmente pueden volverse transitorios o limitados.
  16. Elimine los archivos del modelo del disco y elimine los directorios del modelo de los contextos de compilación de Docker.
  17. Elimina cualquier bloque condicional .csproj PackageReference para paquetes de tiempo de ejecución específicos de Paddle o OpenCV.

Posmigración

  • Verificar que el resultado de la extracción de texto coincida o supere el resultado de PaddleSharp en una muestra representativa de 20 a 30 documentos de cada tipo de documento en el proceso.
  • Confirma que no haya excepciones de carga de ensamblado relacionadas con OpenCvSharp en los registros de inicio de la aplicación.
  • Pruebe la implementación en cada plataforma de destino (Windows, Linux, Docker) utilizando el mismo artefacto de compilación; no debería ser necesario seleccionar un paquete específico para cada plataforma.
  • Verifica que los documentos que anteriormente requerían clasificación manual de resultados produzcan texto correctamente ordenado a través de result.Text.
  • Confirme que los archivos PDF de salida con capacidad de búsqueda permitan realizar búsquedas de texto en Adobe Acrobat Reader o en un visor de PDF de su elección.
  • Ejecuta la aplicación bajo carga para confirmar que las instancias IronTesseract creadas por solicitud no produzcan presión de memoria comparable a la construcción PaddleOcrAll por solicitud.
  • Verificar que los paquetes de idioma instalados como paquetes NuGet se restauren correctamente en la integración continua sin pasos adicionales de implementación de archivos.
  • Pruebe cualquier escenario de extracción de tablas comparándolo con la estructura esperada de filas/columnas utilizando el enfoque basado en regiones o en agrupamiento por coordenadas.
  • Confirma que el tiempo de inicio de la aplicación disminuye después de eliminar la construcción de singleton PaddleOcrAll en la ruta de inicio.

Principales ventajas de migrar a IronOCR

Un paquete reemplaza a una pila de cuatro paquetes. Después de la migración, la huella de dependencia de OCR es una sola referencia NuGet IronOcr. La pila de cuatro paquetes — Sdcb.PaddleOCR, Sdcb.PaddleInference, OpenCvSharp4 y un tiempo de ejecución específico de la plataforma — se convierte en una entrada en el archivo del proyecto. Las auditorías de dependencias, los escaneos de licencias y el monitoreo de vulnerabilidades ahora cubren una superficie en lugar de cuatro.

Los artefactos de despliegue son uniformes en todos los entornos. Las condiciones de selección del backend (CPU, GPU u OpenVINO) han desaparecido. El mismo artefacto de compilación se implementa en un portátil de desarrollador, un ejecutor de CI, un contenedor Linux y una máquina virtual en la nube sin necesidad de seleccionar paquetes específicos del entorno ni realizar ramificaciones de inicialización. Las imágenes de Docker se reducen porque no hay archivos de modelo para COPY ni paquetes de tiempo de ejecución de plataforma para instalar.

Las canalizaciones de archivo de documentos ya no requieren una segunda biblioteca. result.SaveAsSearchablePdf() elimina la dependencia de la biblioteca PDF que la mayoría de los equipos de PaddleSharp habían agregado para producir archivos buscables. El proceso de reconocimiento óptico de caracteres (OCR) y la generación de un PDF con capacidad de búsqueda se realizan mediante una única llamada a la API. Para los equipos que procesan miles de documentos escaneados al día, esta simplificación elimina toda una categoría de conflictos de versiones entre bibliotecas. La entrada del blog sobre archivos PDF con función de búsqueda aborda las consideraciones relativas a la producción a gran escala.

Las decisiones de duración del servicio reflejan los requisitos de la aplicación, no las limitaciones de la biblioteca. IronTesseract tiene una construcción ligera. El patrón de singleton forzado impulsado por la costosa carga de modelos de PaddleSharp ya no es necesario. En ASP.NET Core, los servicios pueden definirse por solicitud, lo que crea un aislamiento más claro entre los usuarios concurrentes y elimina las preocupaciones relacionadas con los subprocesos de estado compartido. Para obtener más información sobre las opciones de implementación, consulte la página de casos de uso de ASP.NET OCR .

La expansión de idiomas es una instalación de paquetes, no un proyecto de investigación. El catálogo de más de 125 idiomas abarca alfabetos europeos, asiáticos, de Oriente Medio y especializados, disponibles como paquetes NuGet . Agregar francés, alemán, árabe o japonés a una canalización que comenzó siendo solo en chino es dotnet add package IronOcr.Languages.French y una línea de configuración. Sin búsqueda de archivo de modelo, sin investigación de disponibilidad upstream, sin despliegue de archivo manual.

El preprocesamiento forma parte de la API OCR. El conocimiento de OpenCV que requería el preprocesamiento de PaddleSharp — comprender núcleos de filtro, gestionar la disposición Mat, seleccionar parámetros de umbral adaptativo — ya no es un requisito previo para el trabajo de OCR. OcrInput proporciona operaciones nombradas con valores predeterminados sensatos. Los equipos que no fueran especialistas en OpenCV pero que mantuvieran el código de preprocesamiento de OpenCV pueden eliminar ese código sin necesidad de reemplazarlo. La página de funciones de preprocesamiento enumera todos los filtros disponibles, junto con la documentación sobre cuándo aplicar cada uno.

Por favor notaAdobe Acrobat, PaddleOCR y Tesseract son marcas registradas de sus respectivos propietarios. Este sitio no está afiliado, respaldado ni patrocinado por Adobe Inc., Baidu, Google o PaddlePaddle. 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 PaddleSharp 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 PaddleSharp OCR a IronOCR?

Sustituya las secuencias de inicialización de PaddleSharp 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 alcanza la precisión de OCR de PaddleSharp OCR 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 PaddleSharp 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 PaddleSharp OCR a IronOCR requiere cambios en la infraestructura de implementación?

IronOCR requiere menos cambios de infraestructura que PaddleSharp 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 PaddleSharp?

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