Saltar al pie de página
VIDEOS

Migración de Dynamsoft OCR a IronOCR

Esta guía proporciona una ruta de migración completa para desarrolladores .NET que pasan de Reconocedor de etiquetas Dynamsoft a IronOCR . Abarca la sustitución de paquetes NuGet , las actualizaciones de espacios de nombres, la inicialización de licencias y ejemplos prácticos de migración de código para escenarios distintos de las comparaciones de capacidades generales, incluyendo la eliminación de la configuración de plantillas, la eliminación de JSON de la configuración en tiempo de ejecución, la migración de la definición de regiones y la simplificación del análisis de resultados.

¿Por qué migrar desde Dynamsoft OCR?

Dynamsoft Label Recognizer es una herramienta especializada diseñada para un propósito específico. Esta precisión funciona bien para implementaciones limitadas, pero genera problemas en cuanto el alcance de la aplicación se extiende más allá del caso de uso original de MRZ o VIN.

La configuración de tiempo de ejecución JSON es una superficie de mantenimiento. Cada flujo de trabajo de reconocimiento de Dynamsoft comienza con AppendSettingsFromString, cargando un documento JSON que declara arreglos de parámetros, modelos de caracteres y referencias de regiones. Esas plantillas JSON son artefactos de despliegue independientes, no están compiladas en el binario. Requieren control de versiones, gestión de la canalización de despliegue y actualizaciones manuales cuando Dynamsoft cambia los esquemas de las plantillas entre las diferentes versiones del SDK.IronOCR no requiere archivos de configuración: instancie IronTesseract, llame a .Read(), y el motor selecciona automáticamente la configuración adecuada.

Precios de suscripción anual para compuestos. Licencias de Reconocedor de etiquetas Dynamsoft a partir de $599 por dispositivo al año. No existe una opción perpetua. Un clúster de procesamiento de cinco dispositivos cuesta más de 2995 dólares al año, y ese presupuesto solo cubre el reconocimiento de etiquetas estructuradas y de la zona de resonancia magnética (MRZ). Si se añade Dynamsoft Barcode Reader para códigos de barras o Dynamsoft Document Normalizer para la corrección de bordes, la factura anual se multiplica por la cantidad de productos. La licencia Lite de IronOCR es $999 de una sola vez y cubre OCR general, etiquetas estructuradas, códigos de barras, PDF nativo, salida de PDF buscable y más de 125 idiomas desde un solo paquete.

La definición de región requiere plantillas JSON. Definir una región de reconocimiento en Dynamsoft significa crear un bloque JSON de ReferenceRegionArray, referenciarlo en un LabelRecognizerParameterArray, y cargar el documento completo antes de que comience cualquier procesamiento de imágenes.IronOCR utiliza un único constructor CropRectangle(x, y, width, height) pasado directamente a OcrInput.LoadImage. Cambiar una región es una edición numérica, no un análisis de un documento JSON.

El análisis de resultados es completamente su responsabilidad. RecognizeFile y RecognizeByFile devuelven arreglos de LineResult que contienen cadenas de texto sin procesar. Cada estructura que necesite (desplazamientos de campo, conversiones de fecha, validación de dígitos de control, manejo de separadores de nombres) requiere código de análisis personalizado sobre esa salida sin procesar.IronOCR devuelve un OcrResult con .Pages, .Lines, .Words, y .Characters como colecciones tipadas con coordenadas de cuadro delimitador, puntuaciones de confianza y metadatos de fuente ya poblados.

La falta de compatibilidad nativa con PDF crea una dependencia oculta. Reconocedor de etiquetas Dynamsoft no tiene capacidad de entrada de PDF. Cualquier archivo PDF que llegue a su canalización de procesamiento requiere una biblioteca externa para convertir cada página en un mapa de bits, un bucle de iteración de páginas y un paso de agregación de resultados antes de la primera llamada a Dynamsoft.IronOCR lee PDFs nativamente: new IronTesseract().Read("document.pdf") procesa cada página sin una dependencia secundaria, un bucle de renderizado o una carpeta de imágenes temporales.

Los bloques de inicialización de múltiples productos crecen con el alcance. Una aplicación de Dynamsoft que maneja etiquetas estructuradas, códigos de barras y normalización de documentos lleva tres llamadas estáticas InitLicense en el inicio, tres cadenas de disposición y tres dependencias de versión de SDK independientes. Actualizar el SDK implica coordinar todos los productos.IronOCR tiene una línea de inicialización y una versión de paquete, independientemente de las funcionalidades que utilice la aplicación.

El problema fundamental

Dynamsoft requiere que se cargue un archivo de configuración JSON en tiempo de ejecución antes de que pueda comenzar el reconocimiento:

// Dynamsoft: JSON template required before any image processing
LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();

string settings = @"{
    ""LabelRecognizerParameterArray"": [{
        ""Name"": ""VIN_Label"",
        ""ReferenceRegionNameArray"": [""VinRegion""],
        ""CharacterModelName"": ""VIN""
    }],
    ""ReferenceRegionArray"": [{
        ""Name"": ""VinRegion"",
        ""Localization"": {
            ""SourceType"": ""LST_MANUAL_SPECIFICATION"",
            ""FirstPoint"":  [5, 40],
            ""SecondPoint"": [95, 40],
            ""ThirdPoint"":  [95, 60],
            ""FourthPoint"": [5, 60]
        }
    }]
}";

recognizer.AppendSettingsFromString(settings);  // fails silently if JSON is malformed
var results = recognizer.RecognizeFile(imagePath);
// Dynamsoft: JSON template required before any image processing
LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();

string settings = @"{
    ""LabelRecognizerParameterArray"": [{
        ""Name"": ""VIN_Label"",
        ""ReferenceRegionNameArray"": [""VinRegion""],
        ""CharacterModelName"": ""VIN""
    }],
    ""ReferenceRegionArray"": [{
        ""Name"": ""VinRegion"",
        ""Localization"": {
            ""SourceType"": ""LST_MANUAL_SPECIFICATION"",
            ""FirstPoint"":  [5, 40],
            ""SecondPoint"": [95, 40],
            ""ThirdPoint"":  [95, 60],
            ""FourthPoint"": [5, 60]
        }
    }]
}";

recognizer.AppendSettingsFromString(settings);  // fails silently if JSON is malformed
var results = recognizer.RecognizeFile(imagePath);
' Dynamsoft: JSON template required before any image processing
LabelRecognizer.InitLicense(licenseKey)
Dim recognizer As New LabelRecognizer()

Dim settings As String = "{
    ""LabelRecognizerParameterArray"": [{
        ""Name"": ""VIN_Label"",
        ""ReferenceRegionNameArray"": [""VinRegion""],
        ""CharacterModelName"": ""VIN""
    }],
    ""ReferenceRegionArray"": [{
        ""Name"": ""VinRegion"",
        ""Localization"": {
            ""SourceType"": ""LST_MANUAL_SPECIFICATION"",
            ""FirstPoint"":  [5, 40],
            ""SecondPoint"": [95, 40],
            ""ThirdPoint"":  [95, 60],
            ""FourthPoint"": [5, 60]
        }
    }]
}"

recognizer.AppendSettingsFromString(settings)  ' fails silently if JSON is malformed
Dim results = recognizer.RecognizeFile(imagePath)
$vbLabelText   $csharpLabel
// IronOCR: no JSON, no template files — region is a constructor argument
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";

var region = new CropRectangle(x: 50, y: 400, width: 900, height: 200);
using var input = new OcrInput();
input.LoadImage(imagePath, region);

var result = new IronTesseract().Read(input);
Console.WriteLine(result.Text);
// IronOCR: no JSON, no template files — region is a constructor argument
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";

var region = new CropRectangle(x: 50, y: 400, width: 900, height: 200);
using var input = new OcrInput();
input.LoadImage(imagePath, region);

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

' IronOCR: no JSON, no template files — region is a constructor argument
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"

Dim region As New CropRectangle(x:=50, y:=400, width:=900, height:=200)
Using input As New OcrInput()
    input.LoadImage(imagePath, region)

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

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

La tabla que aparece a continuación detalla la diferencia total de funcionalidades entre las dos bibliotecas.

Característica Reconocedor de etiquetas Dynamsoft IronOCR
OCR de documentos generales No soportado Soporte completo
Reconocimiento de MRZ Especializado (salida de texto sin formato) Integrado con campos estructurados
reconocimiento del VIN Especializado (se requiere plantilla) OCR estándar con segmentación por región
Lectura de etiquetas industriales Especializado (se requiere plantilla) Sí, a través de CropRectangle
Configuración de los ajustes de tiempo de ejecución Obligatorio (JSON a través de AppendSettingsFromString) No es necesario
Artefactos de despliegue de plantillas Archivos JSON obligatorios None
Entrada nativa de PDF No soportado
PDF protegido con contraseña No soportado
Entrada TIFF de varias páginas Repetición manual de páginas Nativo (LoadImageFrames)
Salida en PDF con capacidad de búsqueda No soportado Sí (SaveAsSearchablePdf)
Lectura de códigos de barras Producto independiente (lector de códigos de barras Dynamsoft) Integrado (ReadBarCodes = true)
Preprocesamiento automático Limitado Sí (Desinclinar, Reducir ruido, Contraste, Binarizar, Enfocar, Dilatar, Erosionar)
Salida estructurada (palabras, líneas, párrafos) Cadenas de texto sin procesar LineResult Completamente escrito con coordenadas y confianza
Soporte de idiomas Limitado (Zona de Reparación de Mercancías Latina) Más de 125 idiomas, agrupados como paquetes NuGet.
Multilingüe simultáneo No soportado Sí (OcrLanguage.French + OcrLanguage.German)
Puntuaciones de confianza Solo por línea Por palabra, línea, página
Exportación hOCR No soportado
Modelo de licencia Suscripción anual (599 $ o más por dispositivo al año) Perpetua ($999 de una sola vez, nivel Lite)
Se requieren varios productos Sí (3+ para cobertura total) No (un paquete)
Compatibilidad con .NET Framework .NET Framework 4.x+ .NET Framework 4.6.2+, .NET 5/6/7/8/9
Implementación multiplataforma Sí (Windows, Linux, macOS, Docker, Azure, AWS)
Paquete NuGet Dynamsoft.LabelRecognizer IronOcr

Inicio rápido: Migración de Dynamsoft OCR a IronOCR

Paso 1: Sustituir el paquete NuGet

Elimine el paquete Dynamsoft:

dotnet remove package Dynamsoft.LabelRecognizer
dotnet remove package Dynamsoft.LabelRecognizer
SHELL

Instala IronOCR desde la galería NuGet :

dotnet add package IronOcr

Paso 2: Actualizar los espacios de nombres

// Before (Dynamsoft)
using Dynamsoft.LabelRecognizer;
using Dynamsoft.Core;

// After (IronOCR)
using IronOcr;
// Before (Dynamsoft)
using Dynamsoft.LabelRecognizer;
using Dynamsoft.Core;

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

Paso 3: Inicializar licencia

Reemplace cada llamada estática de Dynamsoft InitLicense con una sola asignación de licencia de IronOCR al inicio de la aplicación:

// Before (Dynamsoft — one call per product)
LabelRecognizer.InitLicense("DYNAMSOFT-LICENSE-KEY");

// After (IronOCR — one line, all features)
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
// Before (Dynamsoft — one call per product)
LabelRecognizer.InitLicense("DYNAMSOFT-LICENSE-KEY");

// After (IronOCR — one line, all features)
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
' Before (Dynamsoft — one call per product)
LabelRecognizer.InitLicense("DYNAMSOFT-LICENSE-KEY")

' After (IronOCR — one line, all features)
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
$vbLabelText   $csharpLabel

Ejemplos de migración de código

Reconocimiento basado en plantillas para un motor de configuración cero

El sistema de plantillas JSON de Dynamsoft proporciona al SDK instrucciones detalladas sobre los modelos de caracteres, las especificaciones de región y los parámetros de reconocimiento antes de que se procese cualquier imagen. Para la mayoría de las cargas de trabajo de OCR del mundo real, esa configuración añade complejidad a la configuración sin beneficios de reconocimiento, porque el motor Tesseract subyacente gestiona la detección del diseño automáticamente.

Enfoque de Dynamsoft:

using Dynamsoft.LabelRecognizer;
using Dynamsoft.Core;

// JSON template required before recognition
LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();

string invoiceTemplate = @"{
    ""LabelRecognizerParameterArray"": [{
        ""Name"": ""Invoice_Header"",
        ""ReferenceRegionNameArray"": [""HeaderRegion""],
        ""CharacterModelName"": ""NumberLetter""
    }],
    ""ReferenceRegionArray"": [{
        ""Name"": ""HeaderRegion"",
        ""Localization"": {
            ""SourceType"": ""LST_MANUAL_SPECIFICATION"",
            ""FirstPoint"":  [0, 0],
            ""SecondPoint"": [100, 0],
            ""ThirdPoint"":  [100, 20],
            ""FourthPoint"": [0, 20]
        }
    }]
}";

recognizer.AppendSettingsFromString(invoiceTemplate);

var results = recognizer.RecognizeFile("invoice.jpg");
var headerText = new StringBuilder();
foreach (var result in results)
    foreach (var line in result.LineResults)
        headerText.AppendLine(line.Text);

Console.WriteLine(headerText.ToString());
recognizer.Dispose();
using Dynamsoft.LabelRecognizer;
using Dynamsoft.Core;

// JSON template required before recognition
LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();

string invoiceTemplate = @"{
    ""LabelRecognizerParameterArray"": [{
        ""Name"": ""Invoice_Header"",
        ""ReferenceRegionNameArray"": [""HeaderRegion""],
        ""CharacterModelName"": ""NumberLetter""
    }],
    ""ReferenceRegionArray"": [{
        ""Name"": ""HeaderRegion"",
        ""Localization"": {
            ""SourceType"": ""LST_MANUAL_SPECIFICATION"",
            ""FirstPoint"":  [0, 0],
            ""SecondPoint"": [100, 0],
            ""ThirdPoint"":  [100, 20],
            ""FourthPoint"": [0, 20]
        }
    }]
}";

recognizer.AppendSettingsFromString(invoiceTemplate);

var results = recognizer.RecognizeFile("invoice.jpg");
var headerText = new StringBuilder();
foreach (var result in results)
    foreach (var line in result.LineResults)
        headerText.AppendLine(line.Text);

Console.WriteLine(headerText.ToString());
recognizer.Dispose();
Imports Dynamsoft.LabelRecognizer
Imports Dynamsoft.Core
Imports System.Text

' JSON template required before recognition
LabelRecognizer.InitLicense(licenseKey)
Dim recognizer As New LabelRecognizer()

Dim invoiceTemplate As String = "{
    ""LabelRecognizerParameterArray"": [{
        ""Name"": ""Invoice_Header"",
        ""ReferenceRegionNameArray"": [""HeaderRegion""],
        ""CharacterModelName"": ""NumberLetter""
    }],
    ""ReferenceRegionArray"": [{
        ""Name"": ""HeaderRegion"",
        ""Localization"": {
            ""SourceType"": ""LST_MANUAL_SPECIFICATION"",
            ""FirstPoint"":  [0, 0],
            ""SecondPoint"": [100, 0],
            ""ThirdPoint"":  [100, 20],
            ""FourthPoint"": [0, 20]
        }
    }]
}"

recognizer.AppendSettingsFromString(invoiceTemplate)

Dim results = recognizer.RecognizeFile("invoice.jpg")
Dim headerText As New StringBuilder()
For Each result In results
    For Each line In result.LineResults
        headerText.AppendLine(line.Text)
    Next
Next

Console.WriteLine(headerText.ToString())
recognizer.Dispose()
$vbLabelText   $csharpLabel

Enfoque IronOCR:

using IronOcr;

// No template file — region specified inline, engine auto-configures
var region = new CropRectangle(x: 0, y: 0, width: 1200, height: 200);
using var input = new OcrInput();
input.LoadImage("invoice.jpg", region);
input.Deskew();

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

// No template file — region specified inline, engine auto-configures
var region = new CropRectangle(x: 0, y: 0, width: 1200, height: 200);
using var input = new OcrInput();
input.LoadImage("invoice.jpg", region);
input.Deskew();

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

' No template file — region specified inline, engine auto-configures
Dim region As New CropRectangle(x:=0, y:=0, width:=1200, height:=200)
Using input As New OcrInput()
    input.LoadImage("invoice.jpg", region)
    input.Deskew()

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

El método de Dynamsoft requiere la creación, validación e implementación de una plantilla JSON antes de procesar cualquier imagen. Un error tipográfico en AppendSettingsFromString devuelve resultados vacíos en tiempo de ejecución — no hay validación en tiempo de compilación. El CropRectangle de IronOCR es un constructor simple: verificación de tipo en tiempo de compilación, sin archivos externos, sin artefactos de implementación. Consulte la guía de OCR basada en regiones para conocer todos los patrones de segmentación.

Escaneo del código VIN con migración de región

La extracción del número de identificación del vehículo es una especialidad de Dynamsoft Label Recognizer. Su modelo de plantilla VIN está optimizado para el formato alfanumérico específico de 17 caracteres. La migración a IronOCR reemplaza el modelo de plantilla con una canalización de preprocesamiento y una segmentación de regiones, que maneja las mismas condiciones de la placa VIN sin necesidad de configurar un modelo de caracteres.

Enfoque de Dynamsoft:

using Dynamsoft.LabelRecognizer;

LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();

// VIN requires a specific character model and region configuration
string vinTemplate = @"{
    ""LabelRecognizerParameterArray"": [{
        ""Name"": ""VIN_Scan"",
        ""ReferenceRegionNameArray"": [""VIN_Zone""],
        ""CharacterModelName"": ""VIN""
    }],
    ""ReferenceRegionArray"": [{
        ""Name"": ""VIN_Zone"",
        ""Localization"": {
            ""SourceType"": ""LST_MANUAL_SPECIFICATION"",
            ""FirstPoint"":  [10, 35],
            ""SecondPoint"": [90, 35],
            ""ThirdPoint"":  [90, 65],
            ""FourthPoint"": [10, 65]
        }
    }]
}";
recognizer.AppendSettingsFromString(vinTemplate);

var results = recognizer.RecognizeFile("vehicle-vin.jpg");
string rawVin = results
    .SelectMany(r => r.LineResults)
    .Select(l => l.Text)
    .FirstOrDefault() ?? string.Empty;

Console.WriteLine($"Raw VIN text: {rawVin}");  // still requires validation
recognizer.Dispose();
using Dynamsoft.LabelRecognizer;

LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();

// VIN requires a specific character model and region configuration
string vinTemplate = @"{
    ""LabelRecognizerParameterArray"": [{
        ""Name"": ""VIN_Scan"",
        ""ReferenceRegionNameArray"": [""VIN_Zone""],
        ""CharacterModelName"": ""VIN""
    }],
    ""ReferenceRegionArray"": [{
        ""Name"": ""VIN_Zone"",
        ""Localization"": {
            ""SourceType"": ""LST_MANUAL_SPECIFICATION"",
            ""FirstPoint"":  [10, 35],
            ""SecondPoint"": [90, 35],
            ""ThirdPoint"":  [90, 65],
            ""FourthPoint"": [10, 65]
        }
    }]
}";
recognizer.AppendSettingsFromString(vinTemplate);

var results = recognizer.RecognizeFile("vehicle-vin.jpg");
string rawVin = results
    .SelectMany(r => r.LineResults)
    .Select(l => l.Text)
    .FirstOrDefault() ?? string.Empty;

Console.WriteLine($"Raw VIN text: {rawVin}");  // still requires validation
recognizer.Dispose();
Imports Dynamsoft.LabelRecognizer

LabelRecognizer.InitLicense(licenseKey)
Dim recognizer As New LabelRecognizer()

' VIN requires a specific character model and region configuration
Dim vinTemplate As String = "{
    ""LabelRecognizerParameterArray"": [{
        ""Name"": ""VIN_Scan"",
        ""ReferenceRegionNameArray"": [""VIN_Zone""],
        ""CharacterModelName"": ""VIN""
    }],
    ""ReferenceRegionArray"": [{
        ""Name"": ""VIN_Zone"",
        ""Localization"": {
            ""SourceType"": ""LST_MANUAL_SPECIFICATION"",
            ""FirstPoint"":  [10, 35],
            ""SecondPoint"": [90, 35],
            ""ThirdPoint"":  [90, 65],
            ""FourthPoint"": [10, 65]
        }
    }]
}"
recognizer.AppendSettingsFromString(vinTemplate)

Dim results = recognizer.RecognizeFile("vehicle-vin.jpg")
Dim rawVin As String = results _
    .SelectMany(Function(r) r.LineResults) _
    .Select(Function(l) l.Text) _
    .FirstOrDefault() OrElse String.Empty

Console.WriteLine($"Raw VIN text: {rawVin}")  ' still requires validation
recognizer.Dispose()
$vbLabelText   $csharpLabel

Enfoque IronOCR:

using IronOcr;
using System.Text.RegularExpressions;

// Target the VIN plate region directly — no template file
var vinRegion = new CropRectangle(x: 100, y: 350, width: 800, height: 300);

using var input = new OcrInput();
input.LoadImage("vehicle-vin.jpg", vinRegion);
input.Contrast();   // recover faded stamped digits
input.Sharpen();    // clarify character boundaries on embossed plates

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

// VIN: 17 chars, no I/O/Q
var vinMatch = Regex.Match(result.Text, @"\b[A-HJ-NPR-Z0-9]{17}\b");
string vin = vinMatch.Success ? vinMatch.Value : result.Text.Trim();

Console.WriteLine($"VIN: {vin}");
Console.WriteLine($"Confidence: {result.Confidence}%");
using IronOcr;
using System.Text.RegularExpressions;

// Target the VIN plate region directly — no template file
var vinRegion = new CropRectangle(x: 100, y: 350, width: 800, height: 300);

using var input = new OcrInput();
input.LoadImage("vehicle-vin.jpg", vinRegion);
input.Contrast();   // recover faded stamped digits
input.Sharpen();    // clarify character boundaries on embossed plates

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

// VIN: 17 chars, no I/O/Q
var vinMatch = Regex.Match(result.Text, @"\b[A-HJ-NPR-Z0-9]{17}\b");
string vin = vinMatch.Success ? vinMatch.Value : result.Text.Trim();

Console.WriteLine($"VIN: {vin}");
Console.WriteLine($"Confidence: {result.Confidence}%");
Imports IronOcr
Imports System.Text.RegularExpressions

' Target the VIN plate region directly — no template file
Dim vinRegion As New CropRectangle(x:=100, y:=350, width:=800, height:=300)

Using input As New OcrInput()
    input.LoadImage("vehicle-vin.jpg", vinRegion)
    input.Contrast()   ' recover faded stamped digits
    input.Sharpen()    ' clarify character boundaries on embossed plates

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

    ' VIN: 17 chars, no I/O/Q
    Dim vinMatch = Regex.Match(result.Text, "\b[A-HJ-NPR-Z0-9]{17}\b")
    Dim vin As String = If(vinMatch.Success, vinMatch.Value, result.Text.Trim())

    Console.WriteLine($"VIN: {vin}")
    Console.WriteLine($"Confidence: {result.Confidence}%")
End Using
$vbLabelText   $csharpLabel

Las coordenadas CropRectangle usan el mismo marco de referencia de píxeles que la especificación basada en porcentaje de Dynamsoft de SecondPoint — convierta multiplicando los valores de porcentaje de Dynamsoft por las dimensiones de su imagen. La tubería de preprocesamiento (Contrast, Sharpen) reemplaza la optimización del modelo de caracteres que Dynamsoft integra en su plantilla VIN. La guía de corrección de la calidad de imagen abarca la selección de filtros para superficies con relieve y de bajo contraste.

Procesamiento por lotes de archivos TIFF de varias páginas

Dynamsoft Label Recognizer procesa imágenes individuales. Los documentos TIFF de varias páginas, habituales en sistemas de gestión documental, archivos de imágenes médicas y sistemas de fax, requieren una iteración externa: cargar cada fotograma, pasarlo individualmente por el reconocedor y agregar los resultados.IronOCR maneja archivos TIFF de múltiples cuadros nativamente a través de LoadImageFrames.

Enfoque de Dynamsoft:

using Dynamsoft.LabelRecognizer;
// Requires an external TIFF library to extract frames

LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();
recognizer.AppendSettingsFromString(genericTemplate);

// External library needed to iterate TIFF frames
var tiffImage = System.Drawing.Image.FromFile("scanned-batch.tiff");
int frameCount = tiffImage.GetFrameCount(
    System.Drawing.Imaging.FrameDimension.Page);

var allText = new StringBuilder();
for (int i = 0; i < frameCount; i++)
{
    tiffImage.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, i);
    string tempPath = $"frame_{i}.jpg";
    tiffImage.Save(tempPath, System.Drawing.Imaging.ImageFormat.Jpeg);

    var results = recognizer.RecognizeFile(tempPath);
    foreach (var r in results)
        foreach (var line in r.LineResults)
            allText.AppendLine(line.Text);

    System.IO.File.Delete(tempPath);  // clean up temp files
}

Console.WriteLine(allText.ToString());
recognizer.Dispose();
using Dynamsoft.LabelRecognizer;
// Requires an external TIFF library to extract frames

LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();
recognizer.AppendSettingsFromString(genericTemplate);

// External library needed to iterate TIFF frames
var tiffImage = System.Drawing.Image.FromFile("scanned-batch.tiff");
int frameCount = tiffImage.GetFrameCount(
    System.Drawing.Imaging.FrameDimension.Page);

var allText = new StringBuilder();
for (int i = 0; i < frameCount; i++)
{
    tiffImage.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, i);
    string tempPath = $"frame_{i}.jpg";
    tiffImage.Save(tempPath, System.Drawing.Imaging.ImageFormat.Jpeg);

    var results = recognizer.RecognizeFile(tempPath);
    foreach (var r in results)
        foreach (var line in r.LineResults)
            allText.AppendLine(line.Text);

    System.IO.File.Delete(tempPath);  // clean up temp files
}

Console.WriteLine(allText.ToString());
recognizer.Dispose();
Imports Dynamsoft.LabelRecognizer
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.IO
Imports System.Text

' Requires an external TIFF library to extract frames

LabelRecognizer.InitLicense(licenseKey)
Dim recognizer As New LabelRecognizer()
recognizer.AppendSettingsFromString(genericTemplate)

' External library needed to iterate TIFF frames
Dim tiffImage As Image = Image.FromFile("scanned-batch.tiff")
Dim frameCount As Integer = tiffImage.GetFrameCount(FrameDimension.Page)

Dim allText As New StringBuilder()
For i As Integer = 0 To frameCount - 1
    tiffImage.SelectActiveFrame(FrameDimension.Page, i)
    Dim tempPath As String = $"frame_{i}.jpg"
    tiffImage.Save(tempPath, Imaging.ImageFormat.Jpeg)

    Dim results = recognizer.RecognizeFile(tempPath)
    For Each r In results
        For Each line In r.LineResults
            allText.AppendLine(line.Text)
        Next
    Next

    File.Delete(tempPath)  ' clean up temp files
Next

Console.WriteLine(allText.ToString())
recognizer.Dispose()
$vbLabelText   $csharpLabel

Enfoque IronOCR:

using IronOcr;

// No frame iteration, no temp files, no external TIFF library
using var input = new OcrInput();
input.LoadImageFrames("scanned-batch.tiff");  // loads all frames natively
input.Deskew();
input.DeNoise();

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

// Results organized per page
foreach (var page in result.Pages)
{
    Console.WriteLine($"Frame {page.PageNumber}: {page.Lines.Count} lines");
    Console.WriteLine(page.Text);
}

// Save entire batch as a single searchable PDF
result.SaveAsSearchablePdf("batch-searchable.pdf");
using IronOcr;

// No frame iteration, no temp files, no external TIFF library
using var input = new OcrInput();
input.LoadImageFrames("scanned-batch.tiff");  // loads all frames natively
input.Deskew();
input.DeNoise();

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

// Results organized per page
foreach (var page in result.Pages)
{
    Console.WriteLine($"Frame {page.PageNumber}: {page.Lines.Count} lines");
    Console.WriteLine(page.Text);
}

// Save entire batch as a single searchable PDF
result.SaveAsSearchablePdf("batch-searchable.pdf");
Imports IronOcr

' No frame iteration, no temp files, no external TIFF library
Using input As New OcrInput()
    input.LoadImageFrames("scanned-batch.tiff") ' loads all frames natively
    input.Deskew()
    input.DeNoise()

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

    ' Results organized per page
    For Each page In result.Pages
        Console.WriteLine($"Frame {page.PageNumber}: {page.Lines.Count} lines")
        Console.WriteLine(page.Text)
    Next

    ' Save entire batch as a single searchable PDF
    result.SaveAsSearchablePdf("batch-searchable.pdf")
End Using
$vbLabelText   $csharpLabel

El enfoque de Dynamsoft requiere System.Drawing para la extracción de cuadros, un archivo temporal en disco por cuadro y limpieza manual después de cada pasada. El LoadImageFrames de IronOCR lee todos los cuadros en una sola llamada — sin archivos temporales, sin seguimiento de índices de cuadros. Después del OCR, SaveAsSearchablePdf archiva el lote completo como un documento buscable en una llamada de método. La guía de formato TIFF multifotograma y la guía de salida PDF con función de búsqueda cubren ambas capacidades en detalle.

Análisis estructurado de resultados a nivel de palabra

Dynamsoft RecognizeFile devuelve un arreglo DLRResult. Cada DLRResult contiene una colección LineResults de objetos DLRLineResult con una propiedad Text y una propiedad Location que mantiene el cuadrilátero delimitador. La extracción de posiciones a nivel de palabra requiere dividir el texto de la línea por espacios en blanco y dividir proporcionalmente el cuadro delimitador de la línea, una aproximación que falla en fuentes proporcionales.IronOCR muestra cuadros delimitadores a nivel de palabra como propiedades tipadas en cada objeto OcrResult.Word.

Enfoque de Dynamsoft:

using Dynamsoft.LabelRecognizer;

LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();
recognizer.AppendSettingsFromString(settingsJson);

var results = recognizer.RecognizeFile("form-scan.jpg");

foreach (var dlrResult in results)
{
    foreach (var lineResult in dlrResult.LineResults)
    {
        // Line-level data only — word boundaries are not provided
        Console.WriteLine($"Text: {lineResult.Text}");
        Console.WriteLine($"Confidence: {lineResult.Confidence}");

        // Location is a quadrilateral — four corner points
        var loc = lineResult.Location;
        Console.WriteLine($"Top-left: ({loc.Points[0].X}, {loc.Points[0].Y})");

        // To get word positions: split text and divide bounding box manually
        var words = lineResult.Text.Split(' ');
        int approxWidth = (loc.Points[1].X - loc.Points[0].X) / words.Length;
        for (int i = 0; i < words.Length; i++)
        {
            int wordX = loc.Points[0].X + (i * approxWidth);
            Console.WriteLine($"  Word '{words[i]}' approx at x={wordX}");
        }
    }
}
recognizer.Dispose();
using Dynamsoft.LabelRecognizer;

LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();
recognizer.AppendSettingsFromString(settingsJson);

var results = recognizer.RecognizeFile("form-scan.jpg");

foreach (var dlrResult in results)
{
    foreach (var lineResult in dlrResult.LineResults)
    {
        // Line-level data only — word boundaries are not provided
        Console.WriteLine($"Text: {lineResult.Text}");
        Console.WriteLine($"Confidence: {lineResult.Confidence}");

        // Location is a quadrilateral — four corner points
        var loc = lineResult.Location;
        Console.WriteLine($"Top-left: ({loc.Points[0].X}, {loc.Points[0].Y})");

        // To get word positions: split text and divide bounding box manually
        var words = lineResult.Text.Split(' ');
        int approxWidth = (loc.Points[1].X - loc.Points[0].X) / words.Length;
        for (int i = 0; i < words.Length; i++)
        {
            int wordX = loc.Points[0].X + (i * approxWidth);
            Console.WriteLine($"  Word '{words[i]}' approx at x={wordX}");
        }
    }
}
recognizer.Dispose();
Imports Dynamsoft.LabelRecognizer

LabelRecognizer.InitLicense(licenseKey)
Dim recognizer As New LabelRecognizer()
recognizer.AppendSettingsFromString(settingsJson)

Dim results = recognizer.RecognizeFile("form-scan.jpg")

For Each dlrResult In results
    For Each lineResult In dlrResult.LineResults
        ' Line-level data only — word boundaries are not provided
        Console.WriteLine($"Text: {lineResult.Text}")
        Console.WriteLine($"Confidence: {lineResult.Confidence}")

        ' Location is a quadrilateral — four corner points
        Dim loc = lineResult.Location
        Console.WriteLine($"Top-left: ({loc.Points(0).X}, {loc.Points(0).Y})")

        ' To get word positions: split text and divide bounding box manually
        Dim words = lineResult.Text.Split(" "c)
        Dim approxWidth As Integer = (loc.Points(1).X - loc.Points(0).X) \ words.Length
        For i As Integer = 0 To words.Length - 1
            Dim wordX As Integer = loc.Points(0).X + (i * approxWidth)
            Console.WriteLine($"  Word '{words(i)}' approx at x={wordX}")
        Next
    Next
Next
recognizer.Dispose()
$vbLabelText   $csharpLabel

Enfoque IronOCR:

using IronOcr;

var result = new IronTesseract().Read("form-scan.jpg");

// Word-level positions are first-class properties — no manual division
foreach (var page in result.Pages)
{
    foreach (var paragraph in page.Paragraphs)
    {
        Console.WriteLine($"Paragraph at ({paragraph.X}, {paragraph.Y}): {paragraph.Text}");

        foreach (var line in paragraph.Lines)
        {
            foreach (var word in line.Words)
            {
                Console.WriteLine(
                    $"  Word: '{word.Text}' " +
                    $"at ({word.X},{word.Y}) " +
                    $"size {word.Width}x{word.Height} " +
                    $"confidence {word.Confidence}%");
            }
        }
    }
}
using IronOcr;

var result = new IronTesseract().Read("form-scan.jpg");

// Word-level positions are first-class properties — no manual division
foreach (var page in result.Pages)
{
    foreach (var paragraph in page.Paragraphs)
    {
        Console.WriteLine($"Paragraph at ({paragraph.X}, {paragraph.Y}): {paragraph.Text}");

        foreach (var line in paragraph.Lines)
        {
            foreach (var word in line.Words)
            {
                Console.WriteLine(
                    $"  Word: '{word.Text}' " +
                    $"at ({word.X},{word.Y}) " +
                    $"size {word.Width}x{word.Height} " +
                    $"confidence {word.Confidence}%");
            }
        }
    }
}
Imports IronOcr

Dim result = New IronTesseract().Read("form-scan.jpg")

' Word-level positions are first-class properties — no manual division
For Each page In result.Pages
    For Each paragraph In page.Paragraphs
        Console.WriteLine($"Paragraph at ({paragraph.X}, {paragraph.Y}): {paragraph.Text}")

        For Each line In paragraph.Lines
            For Each word In line.Words
                Console.WriteLine(
                    $"  Word: '{word.Text}' " &
                    $"at ({word.X},{word.Y}) " &
                    $"size {word.Width}x{word.Height} " &
                    $"confidence {word.Confidence}%")
            Next
        Next
    Next
Next
$vbLabelText   $csharpLabel

IronOCR proporciona una jerarquía real: las páginas contienen párrafos, los párrafos contienen líneas, las líneas contienen palabras y las palabras contienen caracteres. Cada nivel expone .X, .Y, .Width, .Height, y .Confidence como propiedades tipadas de enteros y dobles — no se requiere aritmética de coordenadas de cuadrilátero. La guía de resultados estructurados documenta todas las propiedades accesibles, y la guía de puntuación de confianza explica los umbrales de confianza para los procesos automatizados de revisión de documentos.

Procesamiento paralelo asíncrono para el escaneo de etiquetas de alto volumen

El reconocedor de etiquetas de Dynamsoft es seguro para hilos, pero su llamada RecognizeFile es sincrónica. Encapsularlo en Task.Run produce procesamiento en paralelo, pero cada llamada comparte el estado de configuración a través de AppendSettingsFromString — cargar plantillas en múltiples hilos requiere una secuencia de inicialización cuidadosa. Las instancias IronTesseract de IronOCR son independientes: cree una por tarea, llame a Read, y el modelo de hilos es sencillo.

Enfoque de Dynamsoft:

using Dynamsoft.LabelRecognizer;
using System.Threading.Tasks;

// InitLicense must be called once before parallel work begins
LabelRecognizer.InitLicense(licenseKey);

// Each thread needs its own recognizer instance with its own template load
var results = new System.Collections.Concurrent.ConcurrentBag<string>();

await Task.WhenAll(imagePaths.Select(async path =>
{
    // Cannot share recognizer instances safely across tasks
    var recognizer = new LabelRecognizer();
    recognizer.AppendSettingsFromString(templateJson);  // reload JSON per instance

    await Task.Run(() =>
    {
        var dlrResults = recognizer.RecognizeFile(path);
        var text = string.Join("\n",
            dlrResults.SelectMany(r => r.LineResults).Select(l => l.Text));
        results.Add(text);
    });

    recognizer.Dispose();
}));

Console.WriteLine($"Processed {results.Count} images");
using Dynamsoft.LabelRecognizer;
using System.Threading.Tasks;

// InitLicense must be called once before parallel work begins
LabelRecognizer.InitLicense(licenseKey);

// Each thread needs its own recognizer instance with its own template load
var results = new System.Collections.Concurrent.ConcurrentBag<string>();

await Task.WhenAll(imagePaths.Select(async path =>
{
    // Cannot share recognizer instances safely across tasks
    var recognizer = new LabelRecognizer();
    recognizer.AppendSettingsFromString(templateJson);  // reload JSON per instance

    await Task.Run(() =>
    {
        var dlrResults = recognizer.RecognizeFile(path);
        var text = string.Join("\n",
            dlrResults.SelectMany(r => r.LineResults).Select(l => l.Text));
        results.Add(text);
    });

    recognizer.Dispose();
}));

Console.WriteLine($"Processed {results.Count} images");
Imports Dynamsoft.LabelRecognizer
Imports System.Threading.Tasks
Imports System.Collections.Concurrent

' InitLicense must be called once before parallel work begins
LabelRecognizer.InitLicense(licenseKey)

' Each thread needs its own recognizer instance with its own template load
Dim results As New ConcurrentBag(Of String)()

Await Task.WhenAll(imagePaths.Select(Async Function(path)
    ' Cannot share recognizer instances safely across tasks
    Dim recognizer As New LabelRecognizer()
    recognizer.AppendSettingsFromString(templateJson)  ' reload JSON per instance

    Await Task.Run(Sub()
        Dim dlrResults = recognizer.RecognizeFile(path)
        Dim text = String.Join(vbLf, dlrResults.SelectMany(Function(r) r.LineResults).Select(Function(l) l.Text))
        results.Add(text)
    End Sub)

    recognizer.Dispose()
End Function))

Console.WriteLine($"Processed {results.Count} images")
$vbLabelText   $csharpLabel

Enfoque IronOCR:

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

var extractedTexts = new ConcurrentDictionary<string, string>();

// Each IronTesseract instance is independent — no shared state
Parallel.ForEach(imagePaths, imagePath =>
{
    var ocr = new IronTesseract();
    var result = ocr.Read(imagePath);
    extractedTexts[imagePath] = result.Text;
});

foreach (var (path, text) in extractedTexts)
    Console.WriteLine($"{System.IO.Path.GetFileName(path)}: {text.Length} characters");
using IronOcr;
using System.Collections.Concurrent;
using System.Threading.Tasks;

var extractedTexts = new ConcurrentDictionary<string, string>();

// Each IronTesseract instance is independent — no shared state
Parallel.ForEach(imagePaths, imagePath =>
{
    var ocr = new IronTesseract();
    var result = ocr.Read(imagePath);
    extractedTexts[imagePath] = result.Text;
});

foreach (var (path, text) in extractedTexts)
    Console.WriteLine($"{System.IO.Path.GetFileName(path)}: {text.Length} characters");
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Threading.Tasks

Dim extractedTexts As New ConcurrentDictionary(Of String, String)()

' Each IronTesseract instance is independent — no shared state
Parallel.ForEach(imagePaths, Sub(imagePath)
    Dim ocr As New IronTesseract()
    Dim result = ocr.Read(imagePath)
    extractedTexts(imagePath) = result.Text
End Sub)

For Each kvp In extractedTexts
    Dim path = kvp.Key
    Dim text = kvp.Value
    Console.WriteLine($"{System.IO.Path.GetFileName(path)}: {text.Length} characters")
Next
$vbLabelText   $csharpLabel

IronOCR no requiere ninguna secuencia de inicialización antes de que comience el trabajo en paralelo. Una asignación IronOcr.License.LicenseKey en el inicio de la aplicación activa todas las instancias. No se requiere recargar la plantilla por hilo ni validación JSON por instancia. Para escenarios no bloqueantes del lado del servidor, la guía de OCR asíncrono cubre patrones basados en await para ASP.NET Core y Azure Functions.

Salida PDF con capacidad de búsqueda a partir de archivos de etiquetas escaneadas

Dynamsoft no tiene capacidad de generar archivos PDF de ningún tipo. Los archivos de etiquetas escaneadas procesados ​​a través de Dynamsoft permanecen como archivos de solo imagen: no se pueden buscar, no son indexables por los sistemas de gestión de documentos y no cumplen con los estándares de archivo PDF/A.IronOCR agrega salida de PDF buscable como una sola llamada de método en el objeto OcrResult.

Enfoque de Dynamsoft:

using Dynamsoft.LabelRecognizer;

// Process scanned label — extract text into a string
LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();
recognizer.AppendSettingsFromString(labelTemplate);

var results = recognizer.RecognizeFile("scanned-labels.jpg");
var extractedText = new StringBuilder();
foreach (var r in results)
    foreach (var line in r.LineResults)
        extractedText.AppendLine(line.Text);

// Cannot create a searchable PDF — save text to a sidecar .txt file instead
System.IO.File.WriteAllText("scanned-labels.txt", extractedText.ToString());

// The original image remains unsearchable
Console.WriteLine("No PDF output available in Dynamsoft Label Recognizer.");
recognizer.Dispose();
using Dynamsoft.LabelRecognizer;

// Process scanned label — extract text into a string
LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();
recognizer.AppendSettingsFromString(labelTemplate);

var results = recognizer.RecognizeFile("scanned-labels.jpg");
var extractedText = new StringBuilder();
foreach (var r in results)
    foreach (var line in r.LineResults)
        extractedText.AppendLine(line.Text);

// Cannot create a searchable PDF — save text to a sidecar .txt file instead
System.IO.File.WriteAllText("scanned-labels.txt", extractedText.ToString());

// The original image remains unsearchable
Console.WriteLine("No PDF output available in Dynamsoft Label Recognizer.");
recognizer.Dispose();
Imports Dynamsoft.LabelRecognizer
Imports System.IO
Imports System.Text

' Process scanned label — extract text into a string
LabelRecognizer.InitLicense(licenseKey)
Dim recognizer = New LabelRecognizer()
recognizer.AppendSettingsFromString(labelTemplate)

Dim results = recognizer.RecognizeFile("scanned-labels.jpg")
Dim extractedText = New StringBuilder()
For Each r In results
    For Each line In r.LineResults
        extractedText.AppendLine(line.Text)
    Next
Next

' Cannot create a searchable PDF — save text to a sidecar .txt file instead
File.WriteAllText("scanned-labels.txt", extractedText.ToString())

' The original image remains unsearchable
Console.WriteLine("No PDF output available in Dynamsoft Label Recognizer.")
recognizer.Dispose()
$vbLabelText   $csharpLabel

Enfoque IronOCR:

using IronOcr;

// Read, preprocess, and produce a searchable PDF archive in one pipeline
using var input = new OcrInput();
input.LoadImage("scanned-labels.jpg");
input.Deskew();
input.Contrast();

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

// Searchable PDF: original image layer preserved, invisible text layer added
result.SaveAsSearchablePdf("scanned-labels-searchable.pdf");

Console.WriteLine($"Searchable PDF created. Confidence: {result.Confidence}%");
Console.WriteLine($"Extracted text preview: {result.Text.Substring(0, 100)}");
using IronOcr;

// Read, preprocess, and produce a searchable PDF archive in one pipeline
using var input = new OcrInput();
input.LoadImage("scanned-labels.jpg");
input.Deskew();
input.Contrast();

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

// Searchable PDF: original image layer preserved, invisible text layer added
result.SaveAsSearchablePdf("scanned-labels-searchable.pdf");

Console.WriteLine($"Searchable PDF created. Confidence: {result.Confidence}%");
Console.WriteLine($"Extracted text preview: {result.Text.Substring(0, 100)}");
Imports IronOcr

' Read, preprocess, and produce a searchable PDF archive in one pipeline
Using input As New OcrInput()
    input.LoadImage("scanned-labels.jpg")
    input.Deskew()
    input.Contrast()

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

    ' Searchable PDF: original image layer preserved, invisible text layer added
    result.SaveAsSearchablePdf("scanned-labels-searchable.pdf")

    Console.WriteLine($"Searchable PDF created. Confidence: {result.Confidence}%")
    Console.WriteLine($"Extracted text preview: {result.Text.Substring(0, 100)}")
End Using
$vbLabelText   $csharpLabel

El archivo PDF con capacidad de búsqueda conserva la imagen escaneada original a resolución completa y añade una capa de texto OCR invisible. Los sistemas de gestión documental, los índices de búsqueda y los visores de PDF ahora pueden buscar en el contenido de las etiquetas. El manual en formato PDF con función de búsqueda y la guía de procesamiento de documentos escaneados abarcan archivos de varias páginas y flujos de trabajo de conversión por lotes.

Referencia de mapeo de la API OCR de Dynamsoft a IronOCR

Reconocedor de etiquetas Dynamsoft Equivalente a IronOCR
LabelRecognizer.InitLicense(key) IronOcr.License.LicenseKey = key
new LabelRecognizer() new IronTesseract()
recognizer.AppendSettingsFromString(json) No es necesario: se configura automáticamente.
recognizer.RecognizeFile(path) ocr.Read(path)
recognizer.RecognizeByFile(path, templateName) ocr.Read(path)
recognizer.RecognizeBuffer(buffer, width, height, ...) input.LoadImage(imageBytes) y luego ocr.Read(input)
DLRResult[] (arreglo de resultados) OcrResult (objeto de resultado único)
DLRResult.LineResults OcrResult.Lines
DLRLineResult.Text OcrResult.Line.Text
DLRLineResult.Confidence OcrResult.Line.Words[i].Confidence
DLRLineResult.Location.Points[i] OcrResult.Line.X, .Y, .Width, .Height
ReferenceRegionArray (JSON) new CropRectangle(x, y, w, h)
LabelRecognizerParameterArray (JSON) No es necesario
CharacterModelName en plantilla No es necesario
recognizer.Dispose() using var ocr = new IronTesseract()
No admite entrada de archivos PDF. input.LoadPdf(path)
No se genera un archivo PDF con capacidad de búsqueda. result.SaveAsSearchablePdf(path)
No se admite entrada TIFF de varias páginas. input.LoadImageFrames(path)
Lectura de códigos de barras (producto aparte) ocr.Configuration.ReadBarCodes = true
Múltiples llamadas InitLicense (por producto) Asignación simple IronOcr.License.LicenseKey

Problemas comunes de migración y soluciones

Problema 1: AppendSettingsFromString devuelve resultados vacíos después de la migración.

Dynamsoft: El reconocimiento devuelve en silencio arreglos DLRResult vacíos cuando AppendSettingsFromString recibe una cadena JSON malformada. No se produce ninguna excepción. Los equipos que migran desde Dynamsoft a veces añaden comprobaciones de nulidad defensivas en todo su código de análisis de resultados para protegerse contra este fallo silencioso.

Solución: Retire AppendSettingsFromString completamente.IronOCR no requiere configuración de plantilla. Si se necesita un enfoque específico de región, reemplace el JSON ReferenceRegionArray con un CropRectangle:

// Remove all AppendSettingsFromString calls
// Replace region JSON with a CropRectangle constructor
var region = new CropRectangle(x: 0, y: 400, width: 1200, height: 300);
using var input = new OcrInput();
input.LoadImage(imagePath, region);
var result = new IronTesseract().Read(input);
// Remove all AppendSettingsFromString calls
// Replace region JSON with a CropRectangle constructor
var region = new CropRectangle(x: 0, y: 400, width: 1200, height: 300);
using var input = new OcrInput();
input.LoadImage(imagePath, region);
var result = new IronTesseract().Read(input);
Imports System

' Remove all AppendSettingsFromString calls
' Replace region JSON with a CropRectangle constructor
Dim region As New CropRectangle(x:=0, y:=400, width:=1200, height:=300)
Using input As New OcrInput()
    input.LoadImage(imagePath, region)
    Dim result = New IronTesseract().Read(input)
End Using
$vbLabelText   $csharpLabel

La guía de configuración de IronTesseract documenta todas las opciones de configuración del motor que se pueden establecer como propiedades en lugar de cadenas JSON.

Problema 2: Múltiples llamadas a InitLicense al iniciar la aplicación

Dynamsoft: Una aplicación que usa reconocedor de etiquetas, lector de códigos de barras y normalizador de documentos llama a tres métodos InitLicense separados en el inicio, cada uno requiriendo su propia clave de licencia. Los equipos almacenan tres variables de entorno, rotan tres claves de forma independiente y depuran tres fallos de inicialización distintos.

Solución: Retire todas las llamadas InitLicense de Dynamsoft. Reemplazar con una línea de IronOCR:

// Remove:
// LabelRecognizer.InitLicense(mrzKey);
// BarcodeReader.InitLicense(barcodeKey);
// DocumentNormalizer.InitLicense(docKey);

// Add once, at application startup:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
// Remove:
// LabelRecognizer.InitLicense(mrzKey);
// BarcodeReader.InitLicense(barcodeKey);
// DocumentNormalizer.InitLicense(docKey);

// Add once, at application startup:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
' Remove:
' LabelRecognizer.InitLicense(mrzKey)
' BarcodeReader.InitLicense(barcodeKey)
' DocumentNormalizer.InitLicense(docKey)

' Add once, at application startup:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
$vbLabelText   $csharpLabel

Una única variable de entorno activa todas las funciones de IronOCR(OCR, códigos de barras, procesamiento de PDF y más de 125 idiomas) sin necesidad de inicialización para cada función.

Problema 3: La agregación de texto de LineResult produce una salida ilegible.

Dynamsoft: RecognizeFile devuelve una DLRResult por región de etiqueta detectada. Las aplicaciones que concatenan lineResult.Text en todos los resultados con separadores \n producen salidas que mezclan líneas de diferentes zonas de etiqueta en la página — porque el orden de los objetos DLRResult es el orden de detección de región, no el orden de lectura.

Solución: La jerarquía de resultados de IronOCR organiza la salida en orden de lectura. Use la estructura PagesParagraphsLines para acceder al texto en el orden natural del documento:

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

// Reading-order text — no manual aggregation
Console.WriteLine(result.Text);

// Or access paragraph-level groupings
foreach (var paragraph in result.Pages[0].Paragraphs)
    Console.WriteLine(paragraph.Text);
var result = new IronTesseract().Read(imagePath);

// Reading-order text — no manual aggregation
Console.WriteLine(result.Text);

// Or access paragraph-level groupings
foreach (var paragraph in result.Pages[0].Paragraphs)
    Console.WriteLine(paragraph.Text);
Imports IronOcr

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

' Reading-order text — no manual aggregation
Console.WriteLine(result.Text)

' Or access paragraph-level groupings
For Each paragraph In result.Pages(0).Paragraphs
    Console.WriteLine(paragraph.Text)
Next
$vbLabelText   $csharpLabel

Problema 4: Faltan archivos JSON de plantilla en el servidor de implementación.

Dynamsoft: Los archivos JSON de plantilla son artefactos de implementación independientes. Cuando falta un archivo de plantilla o tiene una ruta incorrecta en un nuevo servidor, AppendSettingsFromString falla — a veces de manera silenciosa, a veces con una excepción de tiempo de ejecución — y la implementación rompe el reconocimiento sin tocar el código de la aplicación.

Solución:IronOCR no tiene archivos de plantilla. Tras reemplazar el paquete NuGet y actualizar los espacios de nombres, la lista de artefactos de implementación se reduce a los binarios de la aplicación y una variable de entorno. Si es necesario, agregue una comprobación de validación de inicio:

// Validate license at startup — no template files to check
if (!IronOcr.License.IsValidLicense)
    throw new InvalidOperationException("IronOCR license key is invalid or missing.");

var ocr = new IronTesseract();
// Ready to process — no file dependencies
// Validate license at startup — no template files to check
if (!IronOcr.License.IsValidLicense)
    throw new InvalidOperationException("IronOCR license key is invalid or missing.");

var ocr = new IronTesseract();
// Ready to process — no file dependencies
Imports IronOcr

' Validate license at startup — no template files to check
If Not IronOcr.License.IsValidLicense Then
    Throw New InvalidOperationException("IronOCR license key is invalid or missing.")
End If

Dim ocr As New IronTesseract()
' Ready to process — no file dependencies
$vbLabelText   $csharpLabel

Número 5: Gestión de la cadena de eliminación en múltiples productos

Dynamsoft: Cada instancia de producto de Dynamsoft (LabelRecognizer, BarcodeReader, DocumentNormalizer) implementa IDisposable. Las clases de servicio que agregan múltiples productos llevan implementaciones de Dispose de múltiples niveles, y la falta de una llamada Dispose en cualquier instancia de producto causa fugas de recursos nativos que se manifiestan como crecimiento de memoria bajo carga.

Solución: Tanto el IronTesseract como el OcrInput de IronOCR implementan IDisposable, pero el modelo de disposición es más simple — una clase, un patrón. Registre una instancia IronTesseract única en el contenedor DI como singleton, o use bloques using para instancias de corta duración:

// Short-lived pattern
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = new IronTesseract().Read(input);

// Long-lived singleton (register in Program.cs)
builder.Services.AddSingleton<IronTesseract>();
// Short-lived pattern
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = new IronTesseract().Read(input);

// Long-lived singleton (register in Program.cs)
builder.Services.AddSingleton<IronTesseract>();
Imports IronOcr

' Short-lived pattern
Using input As New OcrInput()
    input.LoadImage(imagePath)
    Dim result = (New IronTesseract()).Read(input)
End Using

' Long-lived singleton (register in Program.vb)
builder.Services.AddSingleton(Of IronTesseract)()
$vbLabelText   $csharpLabel

La guía de seguimiento del progreso abarca la gestión del ciclo de vida de los trabajos por lotes de larga duración, y la guía de OCR asíncrono muestra los patrones singleton en ASP.NET Core.

Problema 6: El modelo de caracteres no está disponible para el formato de etiqueta personalizado.

Dynamsoft: El campo CharacterModelName en una plantilla referencia un archivo de modelo que debe existir en el directorio de instalación del SDK de Dynamsoft. Los modelos de personajes personalizados requieren la descarga de archivos de modelo adicionales desde el portal de Dynamsoft y su colocación en la ruta SDK correcta. La ausencia de un modelo provoca un fallo en el reconocimiento y un error de ejecución críptico.

Solución:IronOCR no utiliza archivos de modelos de personajes. Para fuentes personalizadas o especializadas, utilice la formación de idiomas personalizada:

// No character model files needed
// For specialized fonts, use a custom language pack
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.English;  // or load a custom trained pack

using var input = new OcrInput();
input.LoadImage(specializedFontImage);
input.Binarize();  // helps with specialized font clarity

var result = ocr.Read(input);
// No character model files needed
// For specialized fonts, use a custom language pack
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.English;  // or load a custom trained pack

using var input = new OcrInput();
input.LoadImage(specializedFontImage);
input.Binarize();  // helps with specialized font clarity

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

' No character model files needed
' For specialized fonts, use a custom language pack
Dim ocr As New IronTesseract()
ocr.Language = OcrLanguage.English  ' or load a custom trained pack

Using input As New OcrInput()
    input.LoadImage(specializedFontImage)
    input.Binarize()  ' helps with specialized font clarity

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

La guía de capacitación sobre fuentes personalizadas abarca la capacitación en un paquete de idioma personalizado para fuentes de etiquetas propietarias sin necesidad de configurar la ruta del archivo.

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

Pre-Migración

Revisa el código fuente en busca de todas las referencias a Dynamsoft:

# Find all Dynamsoft using statements
grep -r "using Dynamsoft" --include="*.cs" .

# Find InitLicense calls (one per product)
grep -r "InitLicense" --include="*.cs" .

# Find AppendSettingsFromString calls (template loads)
grep -r "AppendSettingsFromString" --include="*.cs" .

# Find template JSON strings (inline or file references)
grep -r "LabelRecognizerParameterArray\|ReferenceRegionArray\|CharacterModelName" --include="*.cs" .

# Find RecognizeFile and RecognizeByFile calls
grep -r "RecognizeFile\|RecognizeByFile\|RecognizeBuffer" --include="*.cs" .

# Find LineResult result parsing patterns
grep -r "LineResults\|DLRResult\|DLRLineResult" --include="*.cs" .

# Identify any JSON template files in the project
find . -name "*.json" | xargs grep -l "LabelRecognizerParameterArray" 2>/dev/null
# Find all Dynamsoft using statements
grep -r "using Dynamsoft" --include="*.cs" .

# Find InitLicense calls (one per product)
grep -r "InitLicense" --include="*.cs" .

# Find AppendSettingsFromString calls (template loads)
grep -r "AppendSettingsFromString" --include="*.cs" .

# Find template JSON strings (inline or file references)
grep -r "LabelRecognizerParameterArray\|ReferenceRegionArray\|CharacterModelName" --include="*.cs" .

# Find RecognizeFile and RecognizeByFile calls
grep -r "RecognizeFile\|RecognizeByFile\|RecognizeBuffer" --include="*.cs" .

# Find LineResult result parsing patterns
grep -r "LineResults\|DLRResult\|DLRLineResult" --include="*.cs" .

# Identify any JSON template files in the project
find . -name "*.json" | xargs grep -l "LabelRecognizerParameterArray" 2>/dev/null
SHELL

Resultados del inventario:

  • Cuente los sitios de llamada InitLicense (uno por producto de Dynamsoft en uso)
  • Cuente los sitios de llamada AppendSettingsFromString (uno por plantilla de reconocimiento)
  • Enumerar todos los archivos de plantilla JSON que se eliminarán después de la migración.
  • Identifique cualquier definición ReferenceRegionArray (convertir a CropRectangle)
  • Tenga en cuenta qué funciones están cubiertas por productos separados (códigos de barras, normalización de documentos).

Migración de código

  1. Elimine el paquete NuGet Dynamsoft.LabelRecognizer (y cualquier otro paquete de Dynamsoft)
  2. Instale el paquete NuGet IronOcr (dotnet add package IronOcr)
  3. Reemplace todos los using Dynamsoft.LabelRecognizer; y using Dynamsoft.Core; con using IronOcr;
  4. Reemplace todas las llamadas LabelRecognizer.InitLicense(key) con una sola IronOcr.License.LicenseKey = key en el inicio de la aplicación
  5. Elimine todas las llamadas AppendSettingsFromString(json) — no se requiere equivalente
  6. Reemplace las coordenadas JSON ReferenceRegionArray con constructores new CropRectangle(x, y, width, height)
  7. Reemplace la instanciación new LabelRecognizer() con new IronTesseract()
  8. Reemplace recognizer.RecognizeFile(path) con ocr.Read(path)
  9. Reemplace la iteración de resultados DLRLineResult con la jerarquía .Words
  10. Reemplace la agregación manual de cadenas lineResult.Text con result.Text o enumeración de página estructurada
  11. Reemplace BarcodeReader.InitLicense + BarcodeReader.DecodeFile con ocr.Configuration.ReadBarCodes = true
  12. Elimine las cadenas Dispose de múltiples productos y reemplácelas con using var ocr = new IronTesseract()
  13. Eliminar los archivos de plantilla JSON de los artefactos del proyecto y de la implementación.
  14. Reemplace los bucles de iteración de cuadros sobre archivos TIFF con input.LoadImageFrames(path)
  15. Agregue result.SaveAsSearchablePdf(outputPath) donde sea necesario archivar salidas escaneadas

Posmigración

  • Verifique que IronOcr.License.IsValidLicense devuelva true al inicio de la aplicación
  • Confirme que todos los resultados de reconocimiento devuelvan result.Text no vacíos para las entradas que funcionaban anteriormente
  • Verifique result.Confidence en muestras representativas — valores por encima del 80% indican buena calidad de reconocimiento
  • Pruebe la precisión de la orientación a regiones comparando las salidas CropRectangle con los resultados de región anteriores de Dynamsoft
  • Valide la lectura de códigos de barras ejecutando ocr.Configuration.ReadBarCodes = true en entradas que anteriormente requerían el lector de códigos de barras de Dynamsoft
  • Verifique que el procesamiento de archivos TIFF de múltiples páginas produzca un OcrResult.Page por cuadro
  • Confirme que la salida PDF con capacidad de búsqueda permite realizar búsquedas de texto en un visor de PDF.
  • Ejecute pruebas de procesamiento en paralelo con Parallel.ForEach y verifique que no haya contención de hilos
  • Pruebe todos los destinos de implementación (Linux, Docker, Azure) para confirmar que la implementación de un solo paquete funciona sin archivos de plantilla JSON.
  • Confirme que no queden variables de entorno de clave de licencia de Dynamsoft en la configuración de implementación.

Principales ventajas de migrar a IronOCR

Un paquete elimina el impuesto de coordinación de múltiples productos. Cada capacidad — OCR general, extracción de MRZ, lectura de códigos de barras, entrada de PDF nativo, salida de PDF buscable, preprocesamiento y más de 125 idiomas — se envía como un solo paquete NuGet IronOcr. Las actualizaciones de versión afectan una entrada de paquete en un archivo .csproj. La rotación de la clave de licencia es una variable de entorno. El inventario de artefactos de despliegue se reduce a binarios de la aplicación, sin archivos de plantilla JSON ni directorios de modelos de caracteres.

Sin dependencias de archivos de configuración. El sistema de plantillas Dynamsoft requiere que los archivos JSON estén presentes y correctamente ubicados en la ruta de ejecución.IronOCR no tiene dependencias de archivos en tiempo de ejecución más allá del propio paquete NuGet . Las imágenes de Docker se vuelven deterministas: lo que pasa la integración continua es exactamente lo que se ejecuta en producción. La guía de despliegue en Docker muestra un archivo Docker completo que requiere solo una línea apt-get install para Linux.

La salida estructurada reduce el código de integración. Dynamsoft devuelve cadenas de texto sin procesar LineResult. La extracción de datos estructurados (posiciones de campo, límites de palabras, nivel de confianza por token) requiere un procesamiento posterior que su código posee y prueba. La jerarquía de resultados de IronOCR expone páginas, párrafos, líneas, palabras y caracteres como colecciones escritas con coordenadas y puntuaciones de confianza en cada nivel. Los equipos que migran desde Dynamsoft suelen eliminar entre el 30 % y el 50 % de su código de procesamiento de resultados porque IronOCR les proporciona la estructura que estaban creando manualmente. La página de resultados del OCR documenta el modelo de salida completo.

Las licencias perpetuas eliminan la incertidumbre del presupuesto anual. Reconocedor de etiquetas Dynamsoft no ofrece la opción de licencia perpetua. Un clúster de procesamiento que funcione durante cinco años cuesta más de 2995 dólares al año solo para el reconocimiento de etiquetas, antes de añadir productos de normalización de códigos de barras o documentos. La licencia Lite de $999 de IronOCR es una compra única que cubre todas las características indefinidamente. Para implementaciones gubernamentales, Enterprise y de larga duración, eliminar la dependencia de la renovación anual de un componente central de procesamiento de documentos reduce la complejidad de las adquisiciones y elimina el riesgo de una interrupción de la suscripción a mitad del proyecto. La página de licencias de IronOCR enumera todos los niveles y lo que cubre cada uno.

Multiplataforma sin configuración específica de plataforma.IronOCR resuelve el binario de tiempo de ejecución correcto para Windows x64, Linux x64, macOS x64 y macOS ARM a través del gráfico de identificadores de tiempo de ejecución de NuGet. Sin bloques <PackageReference> condicionales, sin código de detección de plataforma, sin configuración de ruta de binarios nativos. El mismo código using IronOcr; compila y se ejecuta en Windows, Linux, AWS Lambda, y Azure App Service sin modificaciones.

La cobertura lingüística se adapta a las necesidades de la aplicación. Reconocedor de etiquetas Dynamsoft está diseñado para formatos MRZ con caracteres latinos. Los alfabetos no latinos —árabe, chino, japonés, coreano, hebreo— quedan fuera de su ámbito de diseño.IronOCR admite más de 125 idiomas instalados como paquetes NuGet individuales: dotnet add package IronOcr.Languages.Arabic añade soporte para árabe sin cambio de código en la tubería de reconocimiento. El índice de idiomas lista cada paquete de idioma disponible. Para las aplicaciones que procesan documentos de identidad internacionales, etiquetas de envío multilingües o facturas transfronterizas, esta amplitud significa que una sola biblioteca cubre todos los tipos de documentos con los que se encuentra una aplicación en constante evolución.

Por favor notaDynamsoft y Tesseract son marcas registradas de sus respectivos propietarios. Este sitio no está afiliado con, respaldado por, o patrocinado por Dynamsoft o Google. Todos los nombres de producto, logotipos y marcas son propiedad de sus respectivos dueños. Las comparaciones son solo para fines informativos y reflejan información públicamente disponible en el momento de la redacción.

Preguntas Frecuentes

¿Por qué debería migrar de Dynamsoft OCR SDK 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 Dynamsoft OCR SDK a IronOCR?

Sustituya las secuencias de inicialización de Dynamsoft OCR 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 iguala la precisión de OCR de Dynamsoft OCR SDK 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 maneja IronOCR los datos de idioma que Dynamsoft OCR SDK 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 Dynamsoft OCR SDK a IronOCR requiere cambios en la infraestructura de implementación?

IronOCR requiere menos cambios de infraestructura que Dynamsoft OCR SDK. 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 de la misma manera que lo hace Dynamsoft OCR?

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 Dynamsoft OCR SDK 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 Dynamsoft OCR SDK a IronOCR?

Las pruebas que validan el contenido de texto extraído deben seguir superándose tras la migración. Las pruebas que validan patrones de llamada a API o el ciclo de vida de objetos COM deberán actualizarse para reflejar el modelo de inicialización y resultados más sencillo de IronOCR.

Kannaopat Udonpant
Ingeniero de Software
Antes de convertirse en Ingeniero de Software, Kannapat completó un doctorado en Recursos Ambientales de la Universidad de Hokkaido en Japón. Mientras perseguía su grado, Kannapat también se convirtió en miembro del Laboratorio de Robótica de Vehículos, que es parte del Departamento de Ingeniería ...
Leer más

Equipo de soporte de Iron

Estamos disponibles online las 24 horas, 5 días a la semana.
Chat
Email
Llámame