Migración de Tesseract.NET SDK a IronOCR
Esta guía guía a los desarrolladores de .NET a través de una migración concreta de SDK de Tesseract .NET (Tesseract.Net.SDK, namespace Patagames.Ocr) a IronOCR. Se centra específicamente en equipos que trasladan patrones de inicialización de la era del .NET Framework, expresiones idiomáticas de eliminación heredadas y canalizaciones exclusivamente sincrónicas a un entorno que ahora funciona con .NET 8, contenedores Linux y marcos web que priorizan la asincronía. Si su servicio OCR compila contra net472 y falla en el momento en que alguien agrega <TargetFramework>net8.0</TargetFramework> al .csproj, esta guía está escrita para usted.
¿Por qué migrar desde Tesseract.NET SDK?
El SDK de Patagames aportó un valor real cuando .NET Framework 4.5 era la base de implementación y Windows Server era el único destino. Ese contexto ha cambiado. La mayoría de las organizaciones ahora contenedorizan los servicios, ejecutan la integración continua (CI) en entornos Linux y se estandarizan en .NET 6, 8 o 9. SDK de Tesseract .NET no puede seguirles el ritmo.
Límite duro en .NET Framework 4.5. El paquete tiene como destino net20 a través de net45. No produce ni netstandard ni ensamblaje net6.0. Un archivo de proyecto que incluye Tesseract.Net.SDK no puede establecer <TargetFramework>net8.0</TargetFramework>. La actualización de .NET que el resto del código fuente completa en un sprint se queda atascada indefinidamente en la capa de OCR.
Sin ruta de contenedor. El SDK incluye llamadas P/Invoke exclusivas de Windows en binarios nativos de Windows. En cualquier imagen base de Linux — mcr.microsoft.com/dotnet/aspnet:8.0, ubuntu:22.04, alpine:3.19 — la aplicación lanza DllNotFoundException antes de procesar un solo documento. Los contenedores de Windows existen como solución alternativa, pero conllevan imágenes de mayor tamaño, un coste de licencia adicional y la incompatibilidad con la mayoría de los servicios gestionados de Kubernetes que utilizan por defecto grupos de nodos Linux.
API solo sincrónica bloquea los pipelines de ASP.NET Core. El método OcrApi.GetTextFromImage() es sincrónico. En .NET Core, llamar a operaciones síncronas que bloquean los subprocesos de solicitud degrada el rendimiento bajo carga y conlleva el riesgo de agotamiento del grupo de subprocesos.IronOCR proporciona ReadAsync() para integración no bloqueante. Consulte la guía de OCR asíncrono para ver el patrón.
La creación de motor por solicitud consume memoria. El código de .NET Framework comúnmente crea una instancia OcrApi por llamada al método o por solicitud, desechándola al salir. Se trata de la gestión del ciclo de vida de .NET Framework. También es caro: cada Init() carga 40–100 MB de datos de idioma. Diez solicitudes simultáneas cargan el mismo modelo de lenguaje diez veces. El IronTesseract de IronOCR es seguro para el uso por hilos — una instancia vive durante la vida útil de la aplicación y atiende a todos los llamadores concurrentes desde una sola carga de modelo de idioma.
Los patrones de eliminación heredados acumulan riesgos. El uso correcto del SDK requiere un using (var api = OcrApi.Create()) { ... } statement that predates declaraciones de var. Las bases de código escritas antes de C# 8.0 a menudo incluyen try/finally patrones de disposición o, en casos de errores, ninguna disposición en absoluto. Esos patrones se compilan y se ejecutan en .NET Framework, pero conllevan una deuda técnica que impide una refactorización moderna.
No async, no DI, no inicio moderno. El SDK no tiene concepto de integración de inyección de dependencias, vida útil del servicio alojado, o configuración de IOptions<T>. Su integración en una aplicación .NET Core requiere el registro manual del servicio y evitar cuidadosamente la instanciación por solicitud.IronOCR se integra perfectamente como un servicio singleton en el contenedor DI estándar.
El problema fundamental
// Tesseract.NET SDK: .NET Framework 4.5 ceiling — will not compile on net8.0
// Every project referencing this package is locked below the upgrade line
using Patagames.Ocr; // Patagames.Ocr targets net45; no netstandard or net8 assembly
public class OcrService
{
public string ProcessDocument(string imagePath)
{
// Synchronous-only — blocks ASP.NET Core request threads
// No DI support — must be instantiated manually each time
using (var api = OcrApi.Create()) // C# 1.0 using statement, 40-100MB load per call
{
api.Init(Languages.English);
return api.GetTextFromImage(imagePath);
}
// Project cannot target net6.0, net8.0, or any Linux container base image
}
}
// IronOCR: same logic, any runtime from net462 to net9.0, any platform
using IronOcr; // Single NuGet, supports .NET Framework 4.6.2+, .NET 5/6/7/8/9
// Register once as singleton — load language model once, share across all requests
// Call ReadAsync() in ASP.NET Core for non-blocking operation
var ocr = new IronTesseract();
var result = await ocr.ReadAsync("document.jpg"); // Async-first, no thread blocking
Console.WriteLine(result.Text);
IronOCR frente a Tesseract.NET SDK: comparación de características
La tabla siguiente recoge las capacidades directamente relevantes para una migración de modernización de .NET.
| Característica | SDK de Tesseract .NET | IronOCR |
|---|---|---|
| .NET Framework 2.0-4.5 | Sí | No |
| .NET Framework 4.6.2-4.8 | No | Sí |
| .NET Core 2.x / 3.x | No | Sí |
| .NET 5 | No | Sí |
| .NET 6 | No | Sí |
| .NET 7 | No | Sí |
| .NET 8 | No | Sí |
| .NET 9 | No | Sí |
| Implementación de Windows | Sí | Sí |
| Implementación de Linux | No | Sí |
| Implementación de macOS | No | Sí |
| Contenedores Linux de Docker | No | Sí |
| Azure App Service (Linux) | No | Sí |
| AWS Lambda | No | Sí |
API asíncrona (ReadAsync) | No | Sí |
| Instancia única segura para subprocesos | No | Sí |
| Integración de DI en ASP.NET Core.NET Core | Manual | Servicio Singleton |
| Entrada nativa de PDF | No | Sí |
| Preprocesamiento integrado | No | Sí |
| Salida en PDF con capacidad de búsqueda | No | Sí |
| Datos estructurados (palabras, líneas, párrafos) | No | Sí |
| Soporte comercial / SLA | No (desarrollador individual) | Sí |
| Precio de la licencia perpetua | ~20–50 $ (un solo desarrollador) | Desde $999 |
Inicio rápido: Migración de SDK de Tesseract .NET a IronOCR
Paso 1: Sustituir el paquete NuGet
Eliminar Tesseract.NET SDK:
dotnet remove package Tesseract.Net.SDK
Si se instaló PdfiumViewer o una biblioteca de renderización de PDF similar con el único fin de proporcionar páginas PDF al SDK, elimínela también;IronOCR lee archivos PDF de forma nativa:
dotnet remove package PdfiumViewer
Instala IronOCR desde NuGet :
Paso 2: Actualizar los espacios de nombres
// Before (Tesseract.NET SDK)
using Patagames.Ocr;
using Patagames.Ocr.Enums;
// After (IronOCR)
using IronOcr;
Paso 3: Inicializar licencia
Agregue la llamada de clave de licencia una vez en el inicio de la aplicación — en Program.cs, Startup.cs, o en el host builder de la aplicación:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"Hay disponible una licencia de prueba gratuita para evaluar el producto sin marcas de agua.
Ejemplos de migración de código
Patrón de inicio de .NET Framework para Modern Host Builder
Las aplicaciones de .NET Framework generalmente inicializan el motor OCR en un constructor estático, un evento Application_Start, o un manejador Global.asax. Ninguna de estas funciones existe en las aplicaciones .NET 6+ creadas sobre el modelo de host genérico.
Enfoque del SDK de Tesseract.NET:
// Global.asax.cs — .NET Framework MVC application
// OcrApi lifecycle managed manually; no DI container involved
public class MvcApplication : System.Web.HttpApplication
{
// Static field — one engine for the app lifetime
// But: NOT thread-safe; concurrent requests share a single OcrApi instance
private static OcrApi _globalApi;
protected void Application_Start()
{
// Initialize OCR engine on app startup
// Path to tessdata hardcoded for deployment environment
_globalApi = OcrApi.Create();
_globalApi.Init(Languages.English);
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
protected void Application_End()
{
// Must manually dispose on shutdown
_globalApi?.Dispose();
}
}
Enfoque IronOCR:
// Program.cs — .NET 8ASP.NET Core application
// IronTesseract es seguro para subprocesos; register as singleton, inject where needed
var builder = WebApplication.CreateBuilder(args);
IronOcr.License.LicenseKey = builder.Configuration["IronOcr:LicenseKey"];
// Register as singleton — one instance, thread-safe, shared across all requests
builder.Services.AddSingleton<IronTesseract>();
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();
El patrón Global.asax desaparece completamente. IronTesseract se registra como un servicio singleton estándar, inyectado en controladores y servicios a través del constructor. El modelo de idioma se carga una vez al iniciarlo y permanece en la memoria durante toda la vida útil de la aplicación. La guía de configuración de IronTesseract describe las opciones de configuración, incluida la selección de idioma y el modo del motor en el momento del registro.
Modernización del patrón de eliminación de sistemas heredados
El código de .NET Framework 2.0 utiliza la declaración de bloque using (var x = ...) { }. C# 8.0 introdujo declaraciones using var que delimitan la disposición al bloque envolvente. Las bases de código más antiguas también llevan try/finally guardas de disposición escritas cuando las declaraciones using no se confiaban en todos los escenarios. Todos estos patrones indican código escrito para .NET Framework y deben modernizarse durante la migración.
Enfoque del SDK de Tesseract.NET:
// .NET Framework 4.x disposal patterns — three variants encountered in production
public class LegacyOcrProcessor
{
// Pattern 1: try/finally guard (pre-C# 2.0 style, still common in legacy code)
public string ProcessWithTryFinally(string imagePath)
{
OcrApi api = null;
try
{
api = OcrApi.Create();
api.Init(Languages.English);
return api.GetTextFromImage(imagePath);
}
finally
{
if (api != null)
api.Dispose(); // Manualnull check required
}
}
// Pattern 2: nested using blocks — one for engine, one for image object
public string ProcessWithNestedUsing(string imagePath)
{
using (var api = OcrApi.Create())
{
api.Init(Languages.English);
using (var img = OcrImage.FromFile(imagePath))
{
api.SetImage(img);
return api.GetText();
} // img disposed here
} // api disposed here — nested indentation grows with each resource
}
// Pattern 3: missing disposal — memory leak, common in older service code
public string ProcessUnsafe(string imagePath)
{
var api = OcrApi.Create(); // WARNING: never disposed
api.Init(Languages.English);
return api.GetTextFromImage(imagePath);
}
}
Enfoque IronOCR:
// Modern C# 8.0+ disposal — flat, readable, no nesting
public class ModernOcrProcessor
{
private readonly IronTesseract _ocr; // Injected singleton, never disposed per-request
public ModernOcrProcessor(IronTesseract ocr) => _ocr = ocr;
// Pattern 1: using var declaration — scoped to method, no nesting
public string ProcessDocument(string imagePath)
{
using var input = new OcrInput(); // OcrInput is the disposable resource, not the engine
input.LoadImage(imagePath);
return _ocr.Read(input).Text;
} // input disposed here automatically — no nesting, no try/finally
// Pattern 2: multiple inputs in one scope — still flat
public string ProcessMultipleInputs(string imagePath, string pdfPath)
{
using var imageInput = new OcrInput();
imageInput.LoadImage(imagePath);
using var pdfInput = new OcrInput();
pdfInput.LoadPdf(pdfPath);
var imageText = _ocr.Read(imageInput).Text;
var pdfText = _ocr.Read(pdfInput).Text;
return $"{imageText}\n{pdfText}";
} // both inputs disposed here — zero nesting
}
OcrInput es el único recurso desechable en IronOCR. El motor mismo (IronTesseract) no se desecha por solicitud — es un singleton. Esto elimina la recarga del modelo de idioma de 40–100 MB por solicitud que imponían OcrApi.Create() + api.Init(). La guía de entrada de imágenes cubre todos los métodos de carga de OcrInput, incluidos flujos, matrices de bytes y URLs.
Integración asíncrona para controladores ASP.NET Core
Tesseract.NET SDK no tiene API asíncrona. Todas las llamadas son síncronas. En .NET Core, llamar a operaciones de bloqueo síncronas desde acciones de controlador asíncronas supone un riesgo de agotamiento del grupo de subprocesos bajo carga. La solución común — envolver llamadas síncronas en Task.Run() — delega el trabajo de bloqueo a un hilo del pool de hilos pero no elimina el consumo de hilos. El ReadAsync() de IronOCR proporciona una integración genuina de I/O asíncrono.
Enfoque del SDK de Tesseract.NET:
// ASP.NET Core controller — forced workaround for synchronous OCR API
[ApiController]
[Route("api/ocr")]
public class OcrController : ControllerBase
{
[HttpPost("extract")]
public async Task<IActionResult> ExtractText(IFormFile file)
{
// Must copy upload to temp file — OcrApi does not accept streams directly
var tempPath = Path.GetTempFileName();
await using (var stream = System.IO.File.OpenWrite(tempPath))
await file.CopyToAsync(stream);
string text;
try
{
// Task.Run wraps synchronous call — still consumes a thread-pool thread
// Does NOT free the calling thread during OCR processing
text = await Task.Run(() =>
{
using (var api = OcrApi.Create()) // 40-100MB load per request
{
api.Init(Languages.English);
return api.GetTextFromImage(tempPath); // synchronous, blocking
}
});
}
finally
{
System.IO.File.Delete(tempPath); // Manualtemp file cleanup
}
return Ok(new { text });
}
}
Enfoque IronOCR:
// ASP.NET Core controller — genuine async OCR, no temp files, no thread blocking
[ApiController]
[Route("api/ocr")]
public class OcrController : ControllerBase
{
private readonly IronTesseract _ocr; // Singleton injected via DI
public OcrController(IronTesseract ocr) => _ocr = ocr;
[HttpPost("extract")]
public async Task<IActionResult> ExtractText(IFormFile file)
{
// Load stream directly — no temp file needed
using var input = new OcrInput();
input.LoadImage(file.OpenReadStream()); // Stream input, no disk write
// ReadAsync — genuinely non-blocking, integrates with ASP.NET Core pipeline
var result = await _ocr.ReadAsync(input);
return Ok(new
{
text = result.Text,
confidence = result.Confidence
});
}
}
El ciclo de ida y vuelta del archivo temporal desaparece. El envoltorio Task.Run desaparece. Desaparecen el OcrApi.Create() por solicitud y la carga de 40–100 MB que la seguía. La guía práctica de OCR asíncrono y la guía de entrada de flujos documentan el proceso asíncrono completo, incluida la compatibilidad con tokens de cancelación.
Procesamiento de archivos TIFF multifotograma
El artículo comparativo de la Fase 1 cubrió el procesamiento básico de imágenes y PDF. El formato TIFF multiframe es un caso concreto muy común en el archivo de documentos, los sistemas de fax y los procesos de imágenes médicas. SDK de Tesseract .NET requiere iterar manualmente los cuadros TIFF utilizando System.Drawing.Bitmap, extrayendo cada cuadro a un archivo PNG temporal, ejecutando OCR en el archivo temporal y limpiando. El patrón obliga a llamadas explícitas a GC en documentos grandes para evitar errores de falta de memoria.
Enfoque del SDK de Tesseract.NET:
// Multi-frame TIFF: manual frame extraction to temp files + forced GC
using System.Drawing;
using System.Drawing.Imaging;
using Patagames.Ocr;
public List<string> ProcessMultiFrameTiff(string tiffPath)
{
var pageTexts = new List<string>();
using (var api = OcrApi.Create())
{
api.Init(Languages.English);
using (var bitmap = new Bitmap(tiffPath))
{
var dimension = new FrameDimension(bitmap.FrameDimensionsList[0]);
int frameCount = bitmap.GetFrameCount(dimension);
for (int i = 0; i < frameCount; i++)
{
bitmap.SelectActiveFrame(dimension, i);
// Must write each frame to a temp file — no in-memory path
var tempPath = Path.GetTempFileName() + ".png";
bitmap.Save(tempPath, ImageFormat.Png);
try
{
pageTexts.Add(api.GetTextFromImage(tempPath));
}
finally
{
File.Delete(tempPath); // Manualcleanup on every frame
}
// Force GC every 10 frames — workaround for memory pressure
// Slows processing; indicates memory management is manual
if (i % 10 == 0)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
}
}
return pageTexts;
}
Enfoque IronOCR:
// Multi-frame TIFF: one method call, no temp files, no manual GC
using IronOcr;
public List<string> ProcessMultiFrameTiff(string tiffPath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // Loads all frames natively — no temp files
var result = ocr.Read(input);
// Pages map directly to TIFF frames
return result.Pages.Select(page => page.Text).ToList();
}
De treinta líneas se reducen a ocho. No hay archivos temporales, no hay iteración de cuadros Bitmap, no hay llamadas GC.Collect(). LoadImageFrames maneja TIFFs multiframes de tamaño arbitrario sin escribir archivos intermedios. La guía de entrada de TIFF y GIF cubre la carga selectiva de fotogramas (por rango de índice) y las llamadas de retorno de progreso para documentos de gran tamaño.
Preparación para la implementación de contenedores Docker
El código del SDK de Tesseract.NET que se ejecuta en el equipo Windows de un desarrollador falla en el paso de compilación o ejecución de Docker cuando la imagen base es Linux. La solución no consiste en un ajuste del Dockerfile: los binarios nativos son exclusivos de Windows y no se pueden cargar en Linux en absoluto. El soporte de Linux de IronOCR requiere una pequeña adición apt-get al Dockerfile y nada más en el código de la aplicación.
Enfoque del SDK de Tesseract.NET:
# Dockerfile attempt — fails at runtime on Linux base image
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
# This base image is Linux (Debian) by default
# Tesseract.Net.SDK's Windows native DLLs cannot load here
# Application throws DllNotFoundException on first OCR call
WORKDIR /app
COPY --from=build /app/publish .
# Even copying the Windows tessdata folder has no effect —
# the P/Invoke DLL cannot be loaded regardless of file placement
COPY tessdata/ ./tessdata/
ENTRYPOINT ["dotnet", "MyApp.dll"]
# Runtime error: DllNotFoundException: Unable to load DLL 'libtesseract'
# No fix available within Tesseract.Net.SDK — requires replacing the library
Enfoque IronOCR:
# Dockerfile for IronOCR on Linux — add one apt-get line, nothing else changes
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
# Required system dependency for IronOCR on Debian/Ubuntu base images
RUN apt-get update && apt-get install -y libgdiplus \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=build /app/publish .
# No tessdata folder — language data is bundled with the IronOcr NuGet packages
# No platform check code —IronOCR runs identically on Windows and Linux
ENTRYPOINT ["dotnet", "MyApp.dll"]
Una línea apt-get. No hay carpeta tessdata. No hay código condicional a la plataforma en la aplicación. El mismo binario de la aplicación que se ejecuta en el equipo Windows de un desarrollador se ejecuta en este contenedor Linux sin modificaciones. La guía de implementación de Docker cubre imágenes basadas en Alpine (que utilizan apk en lugar de apt-get), optimización de construcción multi-etapa, y configuración de variables de entorno para la clave de licencia. La guía de implementación de Linux cubre escenarios de Linux en máquina física y WSL2.
Referencia de mapeo de la API del SDK de Tesseract.NET a IronOCR
| SDK de Tesseract .NET | Equivalente a IronOCR | Notas |
|---|---|---|
Install-Package Tesseract.Net.SDK | dotnet add package IronOcr | IronOCR está destinado a .NET Framework 4.6.2+ y .NET 5–9 |
using Patagames.Ocr; | using IronOcr; | Espacio de nombres único |
using Patagames.Ocr.Enums; | (not needed) | Los enums están en el namespace IronOcr |
OcrApi.Create() | new IronTesseract() | IronTesseract es seguro para subprocesos; utilizar como singleton |
api.Init(Languages.English) | ocr.Language = OcrLanguage.English | Asignación de propiedades, no llamada a métodos |
api.Init(Languages.English | Idiomas.Alemán) | ocr.Language = OcrLanguage.English + OcrLanguage.German | Operador +, no OR bit a bit |
api.GetTextFromImage(path) | ocr.Read("path.jpg").Text | Directo o a través de OcrInput |
api.GetTextFromImage(path) (async) | await ocr.ReadAsync(input) | Genuino async — no se necesita envoltura Task.Run |
OcrImage.FromFile(path) | input.LoadImage(path) | OcrInput reemplaza a OcrImage |
OcrImage.FromBitmap(bitmap) | input.LoadImage(bitmap) | |
new MemoryStream(bytes) → OcrImage.FromBitmap | input.LoadImage(bytes) | Compatibilidad directa con matrices de bytes |
api.SetImage(img); api.GetText() | ocr.Read(input).Text | OcrInput se pasa a Read |
api.GetMeanConfidence() | result.Confidence | Devuelve un porcentaje; also available per-word |
api.SetRectangle(x, y, w, h) | input.LoadImage(path, new CropRectangle(x, y, w, h)) | OCR basado en regiones a través de CropRectangle |
api.SetVariable("tessedit_char_whitelist", x) | ocr.Configuration.WhiteListCharacters = x | |
api.SetVariable("tessedit_char_blacklist", x) | ocr.Configuration.BlackListCharacters = x | |
| Iteración de fotogramas de mapa de bits + archivo temporal | input.LoadImageFrames(tiffPath) | Compatibilidad nativa con TIFF multiframe |
| (synchronous only) | result.SaveAsSearchablePdf("out.pdf") | No hay equivalente en SDK de Tesseract .NET |
| (no structured output) | result.Pages, result.Words, result.Lines | Coordenadas a nivel de palabra y nivel de confianza |
Soluciones alternativas GC.Collect() | (not needed) | IronOCR gestiona la memoria internamente |
Verificación de plataforma: IsOSPlatform(Windows) | (remove entirely) | IronOCR es multiplataforma |
| Gestión de carpetas de Tessdata | (remove entirely) | Idiomas incluidos en los paquetes NuGet |
Problemas comunes de migración y soluciones
Problema 1: Conflicto entre los marcos de trabajo de los proyectos
Tesseract.NET SDK: Después de eliminar Tesseract.Net.SDK y agregar IronOcr, el proyecto todavía está dirigido por net45 o net472 del requisito antiguo.IronOCR soporta net462 y posteriores, por lo que los proyectos net45 necesitan actualizar el framework objetivo antes de que el paquete se restaure correctamente.
Solución: Actualice el <TargetFramework> en el archivo .csproj antes de agregar IronOCR. Si el proyecto debe ser compatible tanto con entornos de ejecución antiguos como nuevos durante una migración por fases, utilice la traducción multiobjetivo:
<!-- Single modern target (preferred) -->
<TargetFramework>net8.0</TargetFramework>
<!-- Multi-targeting during phased migration — supports both simultaneously -->
<TargetFrameworks>net462;net8.0</TargetFrameworks>
IronOCR determina automáticamente el ensamblado correcto para cada destino. El mismo comando dotnet add package IronOcr funciona para ambos. La página de la biblioteca OCR de .NET enumera todos los marcos de trabajo compatibles.
Problema 2: Campo OcrApi estático sustituido por DI Singleton
Tesseract.NET SDK: El código heredado registra una sola instancia OcrApi como un campo estático (en Global.asax, un localizador de servicios estático, o una clase envoltura singleton). Este patrón era necesario porque OcrApi no es seguro para hilos — compartir una instancia entre hilos causa condiciones de carrera, por lo que el campo estático estaba protegido por un lock o realmente fue recreado por solicitud a pesar del nombre del campo.
Solución: Registrar IronTesseract como un verdadero singleton seguro para hilos a través del contenedor DI. Eliminar el bloqueo, eliminar el campo estático, eliminar cualquier recreación por solicitud:
// Remove: private static OcrApi _instance; / private static readonly object _lock = new();
// Replace with DI registration in Program.cs
builder.Services.AddSingleton<IronTesseract>();
// In consuming classes — constructor injection
public class DocumentProcessor
{
private readonly IronTesseract _ocr;
public DocumentProcessor(IronTesseract ocr) => _ocr = ocr;
public async Task<string> ProcessAsync(string path)
{
using var input = new OcrInput();
input.LoadImage(path);
var result = await _ocr.ReadAsync(input);
return result.Text;
}
}
Problema 3: Falta la carpeta Tessdata tras la implementación
Tesseract.NET SDK: Tras cambiar a IronOCR, los equipos a veces dejan los pasos de implementación de tessdata en los flujos de CI/CD. La carpeta tessdata/ referenciada en scripts de construcción y manifiestos de implementación ya no existe — era parte de la gestión de modelos de idioma del antiguo SDK. Los scripts fallan cuando intentan copiar o verificar una carpeta que ya no existe.
Solución: Eliminar todas las referencias a tessdata de scripts de implementación, objetivos de copia .csproj, comandos Docker COPY, y pasos de pipeline CI/CD. Los datos de idioma de IronOCR se distribuyen junto con los paquetes NuGet. Ejecutar dotnet restore y los datos de idioma estarán disponibles. No se necesita nada más:
# Remove from CI/CD pipeline
# BEFORE (delete these lines):
# - cp -r tessdata/ $DEPLOY_PATH/tessdata/
# - test -f $DEPLOY_PATH/tessdata/eng.traineddata
# AFTER: nothing — language data is in the NuGet package restore output
dotnet restore # Downloads IronOcr and any IronOcr.Languages.* packages
dotnet publish # Includes language data automatically
La guía de idiomas múltiples describe la instalación de paquetes de idiomas específicos como paquetes NuGet para implementaciones sin conexión o en entornos aislados.
Problema 4: BadImageFormatException en desajuste 32/64-bit
Tesseract.NET SDK: El SDK incluye binarios nativos de Windows para x86 y x64 por separado. Los proyectos dirigidos a AnyCPU a veces se resuelven al binario incorrecto dependiendo de la arquitectura del proceso. El error se presenta como BadImageFormatException o DllNotFoundException en tiempo de ejecución en máquinas donde la arquitectura de proceso no coincide con el DLL nativo en la carpeta de salida.
**Solución:**IronOCR incluye el binario nativo correcto para cada plataforma dentro del paquete NuGet y resuelve el binario correcto automáticamente a través de la carpeta runtimes/ en la estructura del paquete. No hay configuración de objetivo Platform, no hay comandos de copia condicional de arquitectura, no hay subcarpetas de x64 para gestionar:
<!-- Remove architecture-specific build configurations from .csproj -->
<!-- BEFORE: Conditional native DLL copy based on Platform target -->
<!--
<ItemGroup Condition="'$(Platform)' == 'x64'">
<Content Include="$(SolutionDir)libs\x64\*.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
-->
<!-- AFTER: Nothing.IronOCR resolves the correct binary automatically. -->
Tema 5: Migración de cadenas de configuración
Tesseract.NET SDK: Las variables del motor Tesseract se establecen a través de api.SetVariable(string name, string value) utilizando claves de cadena cruda de la referencia API de Tesseract (por ejemplo, "tessedit_char_whitelist", "tessedit_pageseg_mode"). Se trata de cadenas sin tipo y sin autocompletado en el IDE. Los errores tipográficos provocan fallos silenciosos: la variable se ignora, no se produce una excepción.
**Solución:**IronOCR expone la configuración del motor como propiedades tipadas en ocr.Configuration. Los errores tipográficos se convierten en errores de compilación:
// Before: untyped string variables, silent failures on typos
api.SetVariable("tessedit_char_whitelist", "0123456789");
api.SetVariable("tessedit_pageseg_mode", "7");
// After: typed properties, compile-time validation, IDE completion
ocr.Configuration.WhiteListCharacters = "0123456789";
ocr.Configuration.PageSegmentationMode = TesseractPageSegmentationMode.SingleLine;
La referencia de la API de IronTesseract documenta todas las propiedades de configuración con sus tipos y valores aceptados.
N.º 6: Informes de progreso para trabajos por lotes largos
Tesseract.NET SDK: El código de procesamiento por lotes que informa el progreso utilizando IProgress<T> funciona a nivel de tarea (incrementa un contador después de cada archivo) pero no puede informar dentro de un solo documento — no hay mecanismo de callback dentro de GetTextFromImage(). En un documento de 500 páginas, la barra de progreso se queda atascada hasta que se termina todo el documento.
**Solución:**IronOCR proporciona seguimiento de progreso incorporado a través del evento OcrProgress en OcrInput. Indicadores de progreso por página, lo que permite barras de progreso precisas para documentos largos de varias páginas:
// IronOCR: page-level progress tracking for multi-page documents
using IronOcr;
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf("large-archive.pdf");
// Subscribe to page-level progress events
input.OcrProgress += (sender, e) =>
{
Console.WriteLine($"Processing page {e.CurrentPage} of {e.TotalPages} " +
$"({e.ProgressPercent:F0}%)");
};
var result = ocr.Read(input);
Console.WriteLine($"Complete: {result.Pages.Count} pages extracted");
La guía de seguimiento del progreso cubre la integración con ASP.NET Core SignalR para el envío de notificaciones de progreso en tiempo real a los clientes del navegador.
Lista de verificación para la migración de Tesseract.NET SDK
Pre-Migración
Revisa el código fuente para detectar todo uso del SDK de Tesseract.NET antes de modificar ningún código:
# Find all files referencing Patagames namespace
grep -rl "Patagames" --include="*.cs" .
# Find all OcrApi instantiation points
grep -rn "OcrApi.Create" --include="*.cs" .
# Find tessdata references in project and build files
grep -rn "tessdata" --include="*.cs" --include="*.csproj" --include="*.yaml" --include="*.yml" .
# Find platform guard checks that can be removed after migration
grep -rn "IsOSPlatform.*Windows" --include="*.cs" .
# Find Task.Run wrappers around synchronous OCR calls
grep -rn "Task.Run" --include="*.cs" . | grep -i "ocr\|image\|text"
# Count distinct OcrApi.Create() call sites to estimate migration scope
grep -c "OcrApi.Create" $(find . -name "*.cs")
Documentar el conteo de sitios de llamada OcrApi.Create() — cada uno es un candidato para la inyección de singleton de reemplazo. Note cualquier patrón de disposición try/finally para modernización. Identificar cualquier inicialización Global.asax, Application_Start, o de constructor estático que se moverá a Program.cs.
Migración de código
- Actualizar
<TargetFramework>anet8.0(o el runtime moderno objetivo) en todos los archivos.csproj - Ejecutar
dotnet remove package Tesseract.Net.SDKen cada proyecto - Ejecutar
dotnet remove package PdfiumViewer(o el paquete de rendering PDF equivalente) si está presente - Ejecutar
dotnet add package IronOcren cada proyecto - Añadir
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";aProgram.cso al host builder - Registrar
IronTesseractcomo un singleton en el contenedor DI:services.AddSingleton<IronTesseract>() - Reemplazar todos los
using Patagames.Ocr;yusing Patagames.Ocr.Enums;conusing IronOcr; - Reemplazar
OcrApi.Create()+api.Init(Languages.X)conIronTesseractinyectado en el constructor - Reemplazar
using (var api = OcrApi.Create()) { ... }blocks withusing var input = new OcrInput()declarations - Reemplazar
api.GetTextFromImage(path)conocr.Read(input).Textoawait ocr.ReadAsync(input) - Reemplazar
Task.Run(() => { /* synchronous OCR */ })conawait ocr.ReadAsync(input)directo - Reemplazar
api.GetMeanConfidence()conresult.Confidence - Reemplazar bucles TIFF de iteración de cuadros Bitmap con
input.LoadImageFrames(tiffPath) - Reemplazar
api.SetVariable("tessedit_char_whitelist", x)conocr.Configuration.WhiteListCharacters = x - Eliminar la carpeta tessdata del proyecto y eliminar todas las referencias a tessdata en los scripts de implementación
Posmigración
- Compile el proyecto dirigido a
net8.0y confirme que no quedan referenciasPatagamesen la salida de la construcción - Ejecutar la aplicación en un host Linux o contenedor Linux Docker y confirmar no
DllNotFoundException - Verificar que el texto resultante del OCR coincide con el resultado previo a la migración en una muestra representativa de documentos de producción (10-20 documentos)
- Probar el procesamiento de archivos TIFF de varias páginas y confirmar que el recuento de páginas coincide con el recuento de fotogramas original
- Ejecutar pruebas de carga en los puntos finales de ASP.NET Core usando
ReadAsync()y verificar que las métricas del pool de hilos no muestran bloqueos - Confirme que el contenedor DI resuelve
IronTesseractcomo un singleton (misma instancia entre solicitudes) - Verificar que el proceso de CI/CD se completa sin errores ahora que se han eliminado los pasos de copia de tessdata
- Probar la compilación de la imagen de Docker y la ejecución del contenedor en una imagen base de Linux
- Confirma que los eventos de progreso se activan correctamente en un documento de varias páginas (PDF o TIFF)
- Comprueba que las puntuaciones de confianza se encuentren dentro del rango esperado para documentos de calidad contrastada
Principales ventajas de migrar a IronOCR
El bloqueo de actualización de .NET ha desaparecido. Antes de la migración, cualquier plan para trasladar el servicio de .NET Framework 4.x a .NET 8se detenía en la capa de OCR. Tras la migración, el servicio OCR se compila y se ejecuta en .NET Framework 4.6.2, .NET 6, .NET 8y .NET 9desde la misma referencia de paquete. La ruta de actualización está desbloqueada. Los equipos que mantenían una implementación de tiempo de ejecución heredada independiente solo para el OCR pueden consolidarse en un único entorno de tiempo de ejecución moderno.
La implementación en contenedor funciona sin compromiso. Se elimina el DllNotFoundException en imágenes base de Linux. El mismo binario de aplicación que se ejecuta en la estación de trabajo de Windows de un desarrollador se ejecuta dentro de un contenedor Debian o Alpine con una línea apt-get en el Dockerfile. Las implementaciones de Kubernetes, Aplicaciones de contenedores de Azure y tareas de AWS ECS en pools de nodo Linux funcionan sin licencias de contenedores de Windows, tamaños de imagen más grandes, o rutas de código condicionales de arquitectura. La guía de implementación de Docker y la guía de Azure documentan la configuración exacta para cada entorno de destino.
Las pipelines async-first eliminan la presión del pool de hilos. La solución alternativa Task.Run que envolvía OCR síncrono en un método async es reemplazada por ReadAsync(). Los subprocesos de solicitud de ASP.NET Core se liberan durante el procesamiento OCR en lugar de bloquearse. En condiciones de alta concurrencia, esto se traduce directamente en un mayor rendimiento de solicitudes y una menor latencia para toda la aplicación, no solo para los puntos finales de OCR.
El consumo de memoria disminuye proporcionalmente con la concurrencia. Un servicio que previamente creaba una instancia OcrApi por solicitud concurrente — cada una cargando 40–100 MB de datos de idioma — ahora carga esos datos una vez en una instancia singleton IronTesseract. Con diez solicitudes simultáneas, la diferencia es de 400-1000 MB frente a una única carga fija. Esta reducción se refleja inmediatamente en las métricas de recursos de los contenedores y permite límites de memoria de pod más reducidos, una mayor densidad de pods y un menor coste de la infraestructura en la nube.
Los patrones modernos de C# reemplazan la ceremonia de .NET Framework. Los guardas de disposición try/finally, los bloques anidados using, las llamadas GC.Collect() entre cuadros TIFF — todos estos desaparecen. using var input = new OcrInput() es todo el patrón de gestión de recursos. Las revisiones de código son más breves. La incorporación de nuevos desarrolladores al servicio de OCR lleva menos tiempo. La referencia de la API OcrResult documenta el modelo de objetos de resultados completo, incluyendo datos estructurados, puntuaciones de confianza y salida en PDF con capacidad de búsqueda, que sustituyen a los patrones de gestión manual de resultados del SDK heredado.
El soporte comercial sustituye a la dependencia de un único desarrollador. SDK de Tesseract .NET es gestionado por un desarrollador individual sin SLA y sin garantía de continuidad organizativa.IronOCR ha sido desarrollado por Iron Software, una entidad comercial con canales de asistencia dedicados, procesos documentados de divulgación de seguridad y condiciones de licencia que satisfacen los requisitos de adquisición de las empresas. La página de licencias de IronOCR cubre niveles de soporte y el modelo de licencia perpetua (de $999) que reemplaza tanto la tarifa del SDK de Patagames como el costo oculto de mantener infraestructura solo para Windows en un stack .NET en modernización.
