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
// 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
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 | Sí |
| PDF protegido con contraseña | No | Sí |
| TIFF de varias páginas | Mediante OpenCV | Nativo |
| Salida en PDF con capacidad de búsqueda | No | Sí (result.SaveAsSearchablePdf()) |
| Exportación hOCR | No | Sí |
| 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
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
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"
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)()
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)()
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
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
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
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
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
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
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
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
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
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
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" .
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)()
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
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
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
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" .
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
- Elimina todos los paquetes NuGet
Sdcb.PaddleOCR,Sdcb.PaddleInference,OpenCvSharp4yOpenCvSharp4.runtime.*de cada archivo de proyecto. - Instala el paquete NuGet
IronOcr. - Instala los paquetes NuGet de idiomas para los idiomas requeridos (por ejemplo,
IronOcr.Languages.ChineseSimplified). - Agrega
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";al inicio de la aplicación. - Reemplaza todas las sentencias
using Sdcb.PaddleOCR,using Sdcb.PaddleInferenceyusing OpenCvSharpconusing IronOcr. - Reemplaza la instanciación
PaddleOcrAlly la carga de modelos connew IronTesseract(). - Elimina todos los bloques de selección de backend
PaddleConfig(condicionales de CPU, GPU, OpenVINO). - Reemplaza las llamadas
Cv2.ImRead(path)coninput.LoadImage(path)usandoOcrInput. - Reemplaza las operaciones de preprocesamiento de OpenCV (
CvtColor,GaussianBlur,Threshold, etc.) con métodos de filtroOcrInput(Deskew(),DeNoise(),Contrast(),Binarize()). - Reemplaza las llamadas
ocr.Run(mat)conocr.Read(input). - Reemplaza la enumeración
result.Regionsconresult.Pages,result.Pages[n].Linesoresult.Pages[n].Words. - 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. - Reemplaza la inicialización
PaddleOcrTabley el análisis de resultados con el enfoque de orientación basado en regionesOcrInputo agrupación de palabras basada en coordenadas. - Agrega
result.SaveAsSearchablePdf(path)en cualquier lugar que se requiera un archivo PDF buscatamo. - 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.
- Elimine los archivos del modelo del disco y elimine los directorios del modelo de los contextos de compilación de Docker.
- Elimina cualquier bloque condicional
.csprojPackageReferencepara 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
OpenCvSharpen 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
IronTesseractcreadas por solicitud no produzcan presión de memoria comparable a la construcciónPaddleOcrAllpor 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
PaddleOcrAllen 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.
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.

