Comment effectuer la reconnaissance optique de caractères sur les plaques d'immatriculation en C#
Ce guide s'adresse aux développeurs .NET qui migrent leurs charges de travail OCR du SDK Kofax OmniPageCapture (désormais commercialisé sous le nom de Tungsten Automation) vers IronOCR . Il couvre l'intégralité du processus de migration : suppression de la dépendance à l'installateur du SDK, élimination de la cérémonie de cycle de vie du moteur, remplacement des modèles de reconnaissance basés sur les zones et modernisation de la gestion des erreurs. Aucune lecture préalable de l'article comparatif n'est requise.
Pourquoi migrer depuis Kofax OmniPage(Tungstène) ?
Le kit de développement logiciel OmniPage Capture a été conçu pour les infrastructures de gestion de documents Enterprise à une époque antérieure à NuGet, Docker et aux pipelines CI/CD. Chacun de ses choix architecturaux qui paraissait judicieux en 2005 crée des frictions en 2026.
L'installateur du SDK n'a pas sa place dans l'environnement NuGet . Chaque machine de développement, agent de compilation et serveur de production exécutant du code OmniPage requiert l'exécution de l'installateur du SDK Tungsten avant la compilation. Il n'y a pas de référence de package .csproj à restaurer. La mise à niveau de la version du SDK implique la réexécution du programme d'installation sur l'ensemble du parc. Un nouveau développeur ne peut pas exécuter le projet sans contacter la personne qui gère la licence du SDK et sans attendre l'accès à l'installateur.
Le fichier de licence est une responsabilité opérationnelle. Un fichier .lic doit être présent à un chemin spécifique sur chaque machine qui appelle engine.Initialize(). Déployez le fichier sur un nouveau serveur et oubliez-le : chaque appel OCR échouera avec une exception de licence, et non une erreur OCR. Utiliser des licences flottantes et une partition réseau entre votre serveur d'application et le serveur de licences signifie que chaque appel Initialize() échoue jusqu'à ce que la connectivité soit rétablie, que cette machine ait réussi à valider sa licence hier ou non.
La gestion du cycle de vie du moteur fuit des ressources sur les chemins d'erreur. OmniPageEngine.Shutdown() doit être appelé dans chaque chemin de code possible — y compris les gestionnaires d'exceptions, les retours anticipés, et les dépassements de temps — sinon un siège de licence flottante reste verrouillé jusqu'à l'expiration du délai d'attente du serveur de licences. Écrire de manière défensive autour de cette contrainte signifie envelopper chaque point d'intégration dans des modèles IDisposable qui existent uniquement pour se protéger contre le saut de l'arrêt du moteur.
L'identification matérielle est incompatible avec les déploiements modernes. L'activation d'OmniPage est liée au matériel. Les redémarrages de conteneurs, les migrations de machines virtuelles et la mise à l'échelle automatique du cloud impliquent tous des changements d'identité matérielle qui déclenchent des exigences de réactivation. Un modèle de déploiement incompatible avec la mise à l'échelle automatique est un modèle de déploiement incompatible avec l'infrastructure cloud.
La tarification à la page est imprévisible à grande échelle. Un pic de traitement de documents (un nouveau client, un lot de documents en attente soumis) génère une facture de dépassement pour un quota non prévu au budget. IronOCR est perpétuel et illimité dans le cadre de la licence : pas de facturation par page, pas de quota, pas de factures de dépassement.
Le processus d'approvisionnement bloque l'expédition. Le kit de développement logiciel OmniPage est soumis à conditions de vente. L'accès à l'évaluation, la tarification et les conditions contractuelles nécessitent tous un engagement commercial d'une durée de 4 à 12 semaines. Les équipes qui développent des fonctionnalités OCR dans le respect d'une date limite de livraison produit ne peuvent pas attendre le cycle d'approvisionnement de Enterprise . dotnet add package IronOcr se résout en 30 secondes. Voir la page de licence IronOCR pour des tarifs publiés en libre-service — $999 Lite jusqu'à $2,999 Enterprise, tous perpétuels.
Le problème fondamental
OmniPage nécessite une cérémonie d'initialisation complète avant la lecture du premier octet et une cérémonie d'arrêt après le dernier ; chaque site d'appel doit gérer les deux :
// OmniPage: Ceremony required on EVERY entry point — init, process, shutdown
using var engine = new OmniPageEngine();
engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic"); // File must exist here
engine.Initialize(); // Network call to license server — can fail for license reasons, not OCR reasons
var document = engine.CreateDocument();
document.AddPage("invoice.jpg");
document.Recognize(new RecognitionSettings { Language = "English" });
string text = document.GetText();
document.Dispose(); // Must not be skipped
engine.Shutdown(); // Must not be skipped — omitting this locks a floating seat
// OmniPage: Ceremony required on EVERY entry point — init, process, shutdown
using var engine = new OmniPageEngine();
engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic"); // File must exist here
engine.Initialize(); // Network call to license server — can fail for license reasons, not OCR reasons
var document = engine.CreateDocument();
document.AddPage("invoice.jpg");
document.Recognize(new RecognitionSettings { Language = "English" });
string text = document.GetText();
document.Dispose(); // Must not be skipped
engine.Shutdown(); // Must not be skipped — omitting this locks a floating seat
Imports OmniPage
' OmniPage: Ceremony required on EVERY entry point — init, process, shutdown
Using engine As New OmniPageEngine()
engine.SetLicenseFile("C:\Program Files\OmniPage\license.lic") ' File must exist here
engine.Initialize() ' Network call to license server — can fail for license reasons, not OCR reasons
Dim document = engine.CreateDocument()
document.AddPage("invoice.jpg")
document.Recognize(New RecognitionSettings With {.Language = "English"})
Dim text As String = document.GetText()
document.Dispose() ' Must not be skipped
engine.Shutdown() ' Must not be skipped — omitting this locks a floating seat
End Using
// IronOCR: One NuGet package, one line at startup, one call to read
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; // At app startup — once, ever
var text = new IronTesseract().Read("invoice.jpg").Text;
// IronOCR: One NuGet package, one line at startup, one call to read
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; // At app startup — once, ever
var text = new IronTesseract().Read("invoice.jpg").Text;
Imports IronOcr
' IronOCR: One NuGet package, one line at startup, one call to read
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY" ' At app startup — once, ever
Dim text = New IronTesseract().Read("invoice.jpg").Text
La version OmniPage comporte huit étapes distinctes, deux appels de nettoyage obligatoires, une dépendance réseau et une dépendance au système de fichiers. La version IronOCR en possède deux.
Comparaison des fonctionnalités IronOCR et de Kofax OmniPage(Tungstène)
Le tableau ci-dessous récapitule les fonctionnalités les plus pertinentes pour les équipes évaluant cette migration.
| Fonction | Kit de développement logiciel Kofax OmniPage | IronOCR |
|---|---|---|
| Méthode d'installation | Programme d'installation personnalisé du SDK (sans NuGet) | dotnet add package IronOcr |
| Délai avant le premier appel OCR | 4 à 12 semaines (approvisionnement) + heures (installation) | 30 secondes |
| mécanisme de licence | Fichier .lic sur disque + serveur de licences optionnel |
Clé de chaîne au démarrage de l'application |
| empreinte numérique du matériel | Oui (réactivation lors de la migration de la VM) | Non |
| Serveur de licences requis | Oui (pour les licences flottantes) | Non |
| Facturation au temps d'exécution par page | Disponible (variable) | Non |
| Prix publiés | Non (contacter les ventes) | Oui ($999 / $1,499 / $2,999 perpétuel) |
| frais d'entretien annuels | 18 à 25 % du coût de la licence | En option |
| Gestion du cycle de vie du moteur | Requis (Initialize / Shutdown) |
Non requis |
| <Windows x64 | Oui | Oui |
| Linux | Oui (ajouté en janvier 2026) | Oui (toutes versions) |
| macOS | Non | Oui |
| Docker / Kubernetes | Difficile (programme d'installation + fichier de licence dans l'image) | Simple (package NuGet uniquement) |
| Azure / AWS Lambda | Complexe (accessibilité du serveur de licence) | Simplicité |
| Formats d'entrée d'image | Oui | JPG, PNG, BMP, TIFF, GIF et plus encore |
| Entrée PDF native | Oui (peut nécessiter une licence de module) | Oui (intégré, aucune licence supplémentaire) |
| TIFF multipage | Oui | Oui |
| Sortie PDF consultable | Oui | Oui (result.SaveAsSearchablePdf()) |
| prétraitement automatique | Configuration de l'objet Paramètres | API de pipeline intégrée et explicite |
| Langues prises en charge | 120+ | 125+ (packages NuGet distincts) |
| Sortie de données structurées | Oui (coordonnées du mot) | Pages, paragraphes, lignes, mots, caractères avec leurs coordonnées |
| Score de confiance | Oui | Oui (result.Confidence, par mot) |
| Lecture de codes-barres | Module complémentaire (licence séparée) | Intégré (ocr.Configuration.ReadBarCodes = true) |
| Sécurité des threads | Complexe (contraintes de partage de moteur) | Complet (un IronTesseract par thread) |
| Support du pipeline CI/CD | Nécessite un programme d'installation sur chaque agent | dotnet restore Standard |
Démarrage rapide : Migration de Kofax OmniPagevers IronOCR
Étape 1 : Remplacez le SDK par un package NuGet
OmniPage ne possède aucun package NuGet à supprimer. Supprimez les références manuelles de DLL du fichier .csproj et désinstallez le SDK des machines de développement lorsque la migration est terminée. Installez ensuite IronOCR:
dotnet add package IronOcr
Le package NuGet IronOCR regroupe le moteur OCR, les filtres de prétraitement et les composants d'exécution. Pas de programme d'installation séparé, pas de gestion native des DLL, pas d'enregistrement de composants.
Étape 2 : Mise à jour des espaces de noms
// Before (Kofax OmniPage)
using Kofax.OmniPage.CSDK;
using Kofax.OmniPageCSDK;
using CSDK;
// After (IronOCR)
using IronOcr;
// Before (Kofax OmniPage)
using Kofax.OmniPage.CSDK;
using Kofax.OmniPageCSDK;
using CSDK;
// After (IronOCR)
using IronOcr;
Imports IronOcr
Étape 3 : initialisation de la licence
Ajoutez une ligne au démarrage de l'application. Cela remplace le chemin de fichier .lic, la configuration du serveur de licences, et l'appel engine.Initialize():
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Stockez la clé dans une variable d'environnement (IRONOCR_LICENSE_KEY) ou appsettings.json plutôt que dans le code source. La clé est lue hors ligne — aucun appel réseau n'a lieu lors de l'exécution.
Exemples de migration de code
Remplacement du chemin d'erreur d'initialisation du moteur
L'aspect le plus dangereux du modèle de cycle de vie d'OmniPage n'est pas le chemin optimal, mais le chemin d'erreur. Toute exception lancée entre engine.Initialize() et engine.Shutdown() peut laisser un siège de licence flottant verrouillé si Shutdown() n'est pas atteint. Le code de production nécessite des blocs try/finally autour de chaque appel de traitement de document :
Approche Kofax OmniPage :
// OmniPage: try/finally required everywhere to protect license seat release
public string ProcessInvoice(string imagePath)
{
var engine = new OmniPageEngine();
engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic");
try
{
engine.Initialize(); // Contacts license server — failure here locks nothing
var document = engine.CreateDocument();
try
{
document.AddPage(imagePath);
document.Recognize(new RecognitionSettings { Language = "English" });
return document.GetText();
}
catch (RecognitionException ex)
{
// Log, rethrow — but Shutdown must still be called
throw new OcrProcessingException("Recognition failed", ex);
}
finally
{
document.Dispose(); // Inner finally: release document
}
}
catch (LicenseValidationException ex)
{
throw new OcrProcessingException("License validation failed", ex);
}
finally
{
engine.Shutdown(); // Outer finally: release license seat — MUST execute
}
}
// OmniPage: try/finally required everywhere to protect license seat release
public string ProcessInvoice(string imagePath)
{
var engine = new OmniPageEngine();
engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic");
try
{
engine.Initialize(); // Contacts license server — failure here locks nothing
var document = engine.CreateDocument();
try
{
document.AddPage(imagePath);
document.Recognize(new RecognitionSettings { Language = "English" });
return document.GetText();
}
catch (RecognitionException ex)
{
// Log, rethrow — but Shutdown must still be called
throw new OcrProcessingException("Recognition failed", ex);
}
finally
{
document.Dispose(); // Inner finally: release document
}
}
catch (LicenseValidationException ex)
{
throw new OcrProcessingException("License validation failed", ex);
}
finally
{
engine.Shutdown(); // Outer finally: release license seat — MUST execute
}
}
Imports System
' OmniPage: try/finally required everywhere to protect license seat release
Public Function ProcessInvoice(imagePath As String) As String
Dim engine As New OmniPageEngine()
engine.SetLicenseFile("C:\Program Files\OmniPage\license.lic")
Try
engine.Initialize() ' Contacts license server — failure here locks nothing
Dim document = engine.CreateDocument()
Try
document.AddPage(imagePath)
document.Recognize(New RecognitionSettings With {.Language = "English"})
Return document.GetText()
Catch ex As RecognitionException
' Log, rethrow — but Shutdown must still be called
Throw New OcrProcessingException("Recognition failed", ex)
Finally
document.Dispose() ' Inner finally: release document
End Try
Catch ex As LicenseValidationException
Throw New OcrProcessingException("License validation failed", ex)
Finally
engine.Shutdown() ' Outer finally: release license seat — MUST execute
End Try
End Function
Approche IronOCR :
// IronOCR: Non license seat to release, no engine to shut down
public string ProcessInvoice(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
var ocr = new IronTesseract();
var result = ocr.Read(input);
return result.Text;
// OcrInput disposal is automatic — no license implications on any code path
}
// IronOCR: Non license seat to release, no engine to shut down
public string ProcessInvoice(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
var ocr = new IronTesseract();
var result = ocr.Read(input);
return result.Text;
// OcrInput disposal is automatic — no license implications on any code path
}
Imports IronOcr
Public Function ProcessInvoice(imagePath As String) As String
Using input As New OcrInput()
input.LoadImage(imagePath)
Dim ocr As New IronTesseract()
Dim result = ocr.Read(input)
Return result.Text
End Using
' OcrInput disposal is automatic — no license implications on any code path
End Function
Le bloc using sur OcrInput gère le nettoyage de la mémoire pour les données d'image chargées. Aucun try/finally n'existe pour protéger un siège de licence. Une exception survenant n'importe où dans cette méthode n'a aucun effet secondaire en dehors de la portée de la méthode. Consultez le guide d'installation d'IronTesseract pour connaître les options de configuration, notamment les instances locales au thread.
Migration de la reconnaissance basée sur les zones
Le principal mécanisme d'extraction structurée d'OmniPage est la définition de zones : les régions du document sont déclarées avec des limites de coordonnées et un type de reconnaissance par zone (OCR, ICR, OMR, code-barres). Les développeurs définissent des objets FormZone par modèle de document et les transmettent au moteur de reconnaissance. IronOCR utilise CropRectangle pour obtenir la même extraction ciblée sans gestion des modèles ni déclarations de type de zone:
Approche Kofax OmniPage :
// OmniPage: Zone-based form extraction with template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
// Define zones per document template
var zones = new List<FormZone>
{
new FormZone { Name = "InvoiceNumber", Type = "OCR",
X = 520, Y = 80, Width = 200, Height = 30 },
new FormZone { Name = "InvoiceDate", Type = "OCR",
X = 520, Y = 120, Width = 200, Height = 30 },
new FormZone { Name = "TotalAmount", Type = "OCR",
X = 520, Y = 580, Width = 200, Height = 30 },
new FormZone { Name = "VendorName", Type = "OCR",
X = 50, Y = 80, Width = 300, Height = 40 }
};
var template = new FormTemplate { Name = "StandardInvoice", Zones = zones };
var settings = new RecognitionSettings { Language = "English" };
var document = engine.CreateDocument();
document.AddPage(invoicePath);
document.ApplyTemplate(template);
document.Recognize(settings);
var fields = new Dictionary<string, string>();
foreach (var zone in zones)
fields[zone.Name] = document.GetZoneText(zone.Name);
document.Dispose();
engine.Shutdown();
return fields;
}
// OmniPage: Zone-based form extraction with template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
// Define zones per document template
var zones = new List<FormZone>
{
new FormZone { Name = "InvoiceNumber", Type = "OCR",
X = 520, Y = 80, Width = 200, Height = 30 },
new FormZone { Name = "InvoiceDate", Type = "OCR",
X = 520, Y = 120, Width = 200, Height = 30 },
new FormZone { Name = "TotalAmount", Type = "OCR",
X = 520, Y = 580, Width = 200, Height = 30 },
new FormZone { Name = "VendorName", Type = "OCR",
X = 50, Y = 80, Width = 300, Height = 40 }
};
var template = new FormTemplate { Name = "StandardInvoice", Zones = zones };
var settings = new RecognitionSettings { Language = "English" };
var document = engine.CreateDocument();
document.AddPage(invoicePath);
document.ApplyTemplate(template);
document.Recognize(settings);
var fields = new Dictionary<string, string>();
foreach (var zone in zones)
fields[zone.Name] = document.GetZoneText(zone.Name);
document.Dispose();
engine.Shutdown();
return fields;
}
Imports System
Imports System.Collections.Generic
' OmniPage: Zone-based form extraction with template management
Public Function ExtractInvoiceFields(invoicePath As String) As Dictionary(Of String, String)
Using engine As New OmniPageEngine()
engine.SetLicenseFile(licensePath)
engine.Initialize()
' Define zones per document template
Dim zones As New List(Of FormZone) From {
New FormZone With {.Name = "InvoiceNumber", .Type = "OCR", .X = 520, .Y = 80, .Width = 200, .Height = 30},
New FormZone With {.Name = "InvoiceDate", .Type = "OCR", .X = 520, .Y = 120, .Width = 200, .Height = 30},
New FormZone With {.Name = "TotalAmount", .Type = "OCR", .X = 520, .Y = 580, .Width = 200, .Height = 30},
New FormZone With {.Name = "VendorName", .Type = "OCR", .X = 50, .Y = 80, .Width = 300, .Height = 40}
}
Dim template As New FormTemplate With {.Name = "StandardInvoice", .Zones = zones}
Dim settings As New RecognitionSettings With {.Language = "English"}
Dim document = engine.CreateDocument()
document.AddPage(invoicePath)
document.ApplyTemplate(template)
document.Recognize(settings)
Dim fields As New Dictionary(Of String, String)()
For Each zone In zones
fields(zone.Name) = document.GetZoneText(zone.Name)
Next
document.Dispose()
engine.Shutdown()
Return fields
End Using
End Function
Approche IronOCR :
// IronOCR: CropRectangle targets specific regions — no template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
var fields = new Dictionary<string, string>();
var ocr = new IronTesseract();
// Extract each field by reading only the target region
var fieldRegions = new Dictionary<string, CropRectangle>
{
["InvoiceNumber"] = new CropRectangle(520, 80, 200, 30),
["InvoiceDate"] = new CropRectangle(520, 120, 200, 30),
["TotalAmount"] = new CropRectangle(520, 580, 200, 30),
["VendorName"] = new CropRectangle(50, 80, 300, 40)
};
foreach (var (fieldName, region) in fieldRegions)
{
using var input = new OcrInput();
input.LoadImage(invoicePath, region);
fields[fieldName] = ocr.Read(input).Text.Trim();
}
return fields;
}
// IronOCR: CropRectangle targets specific regions — no template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
var fields = new Dictionary<string, string>();
var ocr = new IronTesseract();
// Extract each field by reading only the target region
var fieldRegions = new Dictionary<string, CropRectangle>
{
["InvoiceNumber"] = new CropRectangle(520, 80, 200, 30),
["InvoiceDate"] = new CropRectangle(520, 120, 200, 30),
["TotalAmount"] = new CropRectangle(520, 580, 200, 30),
["VendorName"] = new CropRectangle(50, 80, 300, 40)
};
foreach (var (fieldName, region) in fieldRegions)
{
using var input = new OcrInput();
input.LoadImage(invoicePath, region);
fields[fieldName] = ocr.Read(input).Text.Trim();
}
return fields;
}
Imports System.Collections.Generic
' IronOCR: CropRectangle targets specific regions — no template management
Public Function ExtractInvoiceFields(invoicePath As String) As Dictionary(Of String, String)
Dim fields As New Dictionary(Of String, String)()
Dim ocr As New IronTesseract()
' Extract each field by reading only the target region
Dim fieldRegions As New Dictionary(Of String, CropRectangle) From {
{"InvoiceNumber", New CropRectangle(520, 80, 200, 30)},
{"InvoiceDate", New CropRectangle(520, 120, 200, 30)},
{"TotalAmount", New CropRectangle(520, 580, 200, 30)},
{"VendorName", New CropRectangle(50, 80, 300, 40)}
}
For Each kvp In fieldRegions
Dim fieldName = kvp.Key
Dim region = kvp.Value
Using input As New OcrInput()
input.LoadImage(invoicePath, region)
fields(fieldName) = ocr.Read(input).Text.Trim()
End Using
Next
Return fields
End Function
Chaque CropRectangle spécifie (x, y, width, height) en pixels. Le moteur OCR traite uniquement la région désignée, et non la page entière — le même gain d'efficacité que celui offert par le traitement par zone dans OmniPage. Le guide OCR basé sur la région couvre les modèles de sélection de coordonnées, y compris comment extraire plusieurs régions à partir d'un seul chargement d'image en utilisant l'API multi-région de OcrInput.
Migration des données structurées en sortie
OmniPage expose la structure du document via un pipeline d'exportation propriétaire : les paramètres de format sont appliqués à un document reconnu, et la sortie est écrite dans un fichier dans un format spécifié (RTF, XML, CSV, PDF consultable). L'accès aux coordonnées au niveau du mot nécessite l'itération d'un itérateur de résultats avec des requêtes de position explicites. IronOCR expose les mêmes données structurées directement dans l'objet résultat, sans étape d'exportation secondaire :
Approche Kofax OmniPage :
// OmniPage: Structured output requires format configuration and file export
public void ExtractStructuredContent(string imagePath, string outputDir)
{
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
var settings = new RecognitionSettings { Language = "English" };
var document = engine.CreateDocument();
document.AddPage(imagePath);
document.Recognize(settings);
// Word-level coordinates through result iterator
var iterator = document.GetResultIterator(ResultIteratorLevel.Word);
while (iterator.MoveNext())
{
string word = iterator.GetText();
var bounds = iterator.GetBoundingBox();
double confidence = iterator.GetConfidence();
Console.WriteLine($"Word: {word} at ({bounds.X},{bounds.Y}) conf:{confidence:F1}%");
}
// Structured export requires separate output format configuration
var outputSettings = new OutputSettings
{
Format = OutputFormat.XML,
IncludeCoordinates = true,
IncludeConfidence = true
};
document.SaveAs(Path.Combine(outputDir, "output.xml"), outputSettings);
document.Dispose();
engine.Shutdown();
}
// OmniPage: Structured output requires format configuration and file export
public void ExtractStructuredContent(string imagePath, string outputDir)
{
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
var settings = new RecognitionSettings { Language = "English" };
var document = engine.CreateDocument();
document.AddPage(imagePath);
document.Recognize(settings);
// Word-level coordinates through result iterator
var iterator = document.GetResultIterator(ResultIteratorLevel.Word);
while (iterator.MoveNext())
{
string word = iterator.GetText();
var bounds = iterator.GetBoundingBox();
double confidence = iterator.GetConfidence();
Console.WriteLine($"Word: {word} at ({bounds.X},{bounds.Y}) conf:{confidence:F1}%");
}
// Structured export requires separate output format configuration
var outputSettings = new OutputSettings
{
Format = OutputFormat.XML,
IncludeCoordinates = true,
IncludeConfidence = true
};
document.SaveAs(Path.Combine(outputDir, "output.xml"), outputSettings);
document.Dispose();
engine.Shutdown();
}
Imports System
Imports System.IO
Public Sub ExtractStructuredContent(imagePath As String, outputDir As String)
Using engine As New OmniPageEngine()
engine.SetLicenseFile(licensePath)
engine.Initialize()
Dim settings As New RecognitionSettings With {.Language = "English"}
Dim document = engine.CreateDocument()
document.AddPage(imagePath)
document.Recognize(settings)
' Word-level coordinates through result iterator
Dim iterator = document.GetResultIterator(ResultIteratorLevel.Word)
While iterator.MoveNext()
Dim word As String = iterator.GetText()
Dim bounds = iterator.GetBoundingBox()
Dim confidence As Double = iterator.GetConfidence()
Console.WriteLine($"Word: {word} at ({bounds.X},{bounds.Y}) conf:{confidence:F1}%")
End While
' Structured export requires separate output format configuration
Dim outputSettings As New OutputSettings With {
.Format = OutputFormat.XML,
.IncludeCoordinates = True,
.IncludeConfidence = True
}
document.SaveAs(Path.Combine(outputDir, "output.xml"), outputSettings)
document.Dispose()
engine.Shutdown()
End Using
End Sub
Approche IronOCR :
// IronOCR: Structured data directly on OcrResult — no export step
public void ExtractStructuredContent(string imagePath)
{
var result = new IronTesseract().Read(imagePath);
Console.WriteLine($"Confidence: {result.Confidence:F1}%");
Console.WriteLine($"Pages: {result.Pages.Length}");
foreach (var page in result.Pages)
{
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($"[Paragraph at ({paragraph.X},{paragraph.Y})]");
Console.WriteLine(paragraph.Text);
foreach (var word in paragraph.Words)
{
// Per-word confidence and coordinates — no iterator needed
Console.WriteLine(
$" Word: '{word.Text}' " +
$"at ({word.X},{word.Y}) " +
$"conf: {word.Confidence:F1}%");
}
}
}
}
// IronOCR: Structured data directly on OcrResult — no export step
public void ExtractStructuredContent(string imagePath)
{
var result = new IronTesseract().Read(imagePath);
Console.WriteLine($"Confidence: {result.Confidence:F1}%");
Console.WriteLine($"Pages: {result.Pages.Length}");
foreach (var page in result.Pages)
{
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($"[Paragraph at ({paragraph.X},{paragraph.Y})]");
Console.WriteLine(paragraph.Text);
foreach (var word in paragraph.Words)
{
// Per-word confidence and coordinates — no iterator needed
Console.WriteLine(
$" Word: '{word.Text}' " +
$"at ({word.X},{word.Y}) " +
$"conf: {word.Confidence:F1}%");
}
}
}
}
Public Sub ExtractStructuredContent(imagePath As String)
Dim result = New IronTesseract().Read(imagePath)
Console.WriteLine($"Confidence: {result.Confidence:F1}%")
Console.WriteLine($"Pages: {result.Pages.Length}")
For Each page In result.Pages
For Each paragraph In page.Paragraphs
Console.WriteLine($"[Paragraph at ({paragraph.X},{paragraph.Y})]")
Console.WriteLine(paragraph.Text)
For Each word In paragraph.Words
' Per-word confidence and coordinates — no iterator needed
Console.WriteLine(
$" Word: '{word.Text}' " &
$"at ({word.X},{word.Y}) " &
$"conf: {word.Confidence:F1}%")
Next
Next
Next
End Sub
La hiérarchie de l'objet OcrResult — Pages, Paragraphes, Lignes, Mots, Caractères — fournit les mêmes données positionnelles que l'itérateur de résultat d'OmniPage expose, mais sous forme de graphe d'objets navigable plutôt que de curseur séquentiel. Les scores de confiance sont disponibles au niveau du résultat, de la page, du paragraphe et du mot sans configuration supplémentaire. Le guide des résultats de lecture couvre toutes les propriétés des données structurées, et le guide des scores de confiance couvre les modèles de validation par mot.
Migration du traitement TIFF multipage
Le flux de travail TIFF multipage d'OmniPage nécessite une extraction page par page du conteneur TIFF, avec un appel de reconnaissance et une suppression de document distincts pour chaque page. Cela crée à la fois du code standard et une surface de gestion des ressources proportionnelle au nombre de pages. IronOCR charge les fichiers TIFF multi-images en un seul appel :
Approche Kofax OmniPage :
// OmniPage: Page-by-page TIFF extraction with per-page lifecycle
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
var pageTexts = new List<string>();
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
// Open TIFF and iterate frames
var tiffContainer = engine.OpenTiff(tiffPath);
int pageCount = tiffContainer.GetPageCount();
var settings = new RecognitionSettings { Language = "English" };
for (int i = 0; i < pageCount; i++)
{
var page = tiffContainer.GetPage(i); // Extract frame
var document = engine.CreateDocument();
try
{
document.AddPage(page);
document.Recognize(settings);
pageTexts.Add(document.GetText());
}
finally
{
document.Dispose(); // Dispose per page to manage memory
}
}
tiffContainer.Dispose();
engine.Shutdown();
return pageTexts;
}
// OmniPage: Page-by-page TIFF extraction with per-page lifecycle
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
var pageTexts = new List<string>();
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
// Open TIFF and iterate frames
var tiffContainer = engine.OpenTiff(tiffPath);
int pageCount = tiffContainer.GetPageCount();
var settings = new RecognitionSettings { Language = "English" };
for (int i = 0; i < pageCount; i++)
{
var page = tiffContainer.GetPage(i); // Extract frame
var document = engine.CreateDocument();
try
{
document.AddPage(page);
document.Recognize(settings);
pageTexts.Add(document.GetText());
}
finally
{
document.Dispose(); // Dispose per page to manage memory
}
}
tiffContainer.Dispose();
engine.Shutdown();
return pageTexts;
}
Imports System.Collections.Generic
' OmniPage: Page-by-page TIFF extraction with per-page lifecycle
Public Function ExtractFromMultiPageTiff(tiffPath As String) As List(Of String)
Dim pageTexts As New List(Of String)()
Using engine As New OmniPageEngine()
engine.SetLicenseFile(licensePath)
engine.Initialize()
' Open TIFF and iterate frames
Dim tiffContainer = engine.OpenTiff(tiffPath)
Dim pageCount As Integer = tiffContainer.GetPageCount()
Dim settings As New RecognitionSettings With {.Language = "English"}
For i As Integer = 0 To pageCount - 1
Dim page = tiffContainer.GetPage(i) ' Extract frame
Dim document = engine.CreateDocument()
Try
document.AddPage(page)
document.Recognize(settings)
pageTexts.Add(document.GetText())
Finally
document.Dispose() ' Dispose per page to manage memory
End Try
Next
tiffContainer.Dispose()
engine.Shutdown()
End Using
Return pageTexts
End Function
Approche IronOCR :
// IronOCR: Multi-frame TIFF loaded in one call — all pages in one result
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // All frames loaded automatically
var result = new IronTesseract().Read(input);
// Each TIFF frame becomes a page in the result
return result.Pages.Select(page => page.Text).ToList();
}
// IronOCR: Multi-frame TIFF loaded in one call — all pages in one result
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // All frames loaded automatically
var result = new IronTesseract().Read(input);
// Each TIFF frame becomes a page in the result
return result.Pages.Select(page => page.Text).ToList();
}
Imports System.Collections.Generic
Imports IronOcr
' IronOCR: Multi-frame TIFF loaded in one call — all pages in one result
Public Function ExtractFromMultiPageTiff(tiffPath As String) As List(Of String)
Using input As New OcrInput()
input.LoadImageFrames(tiffPath) ' All frames loaded automatically
Dim result = New IronTesseract().Read(input)
' Each TIFF frame becomes a page in the result
Return result.Pages.Select(Function(page) page.Text).ToList()
End Using
End Function
LoadImageFrames lit chaque cadre du conteneur TIFF en un seul appel. Le résultat expose un OcrResult.Page par cadre, préservant la structure page par page sans nécessiter une boucle d'itération manuelle ni un nettoyage par page. Pour les flux de travail en lot de production TIFF, voir le guide d'entrée TIFF et GIF.
Migration du traitement parallèle sécurisé entre threads
Le moteur d'OmniPage est une ressource partagée et à état. Partager une instance OmniPageEngine entre les threads nécessite une synchronisation externe car plusieurs threads appelant CreateDocument() de manière concurrente peuvent produire un état entrelacé. Le modèle sûr — une instance de moteur par thread — entre en conflit avec le coût d'initialisation lourd du moteur. Les instances d'IronOCR ne transportent aucun état partagé : créez un IronTesseract par thread avec zéro surcharge de coordination :
Approche Kofax OmniPage :
// OmniPage: Shared engine with locking to serialize document operations
public ConcurrentDictionary<string, string> ProcessBatchWithEngine(
string[] imagePaths, string licensePath)
{
var results = new ConcurrentDictionary<string, string>();
var engineLock = new object();
// One engine — document operations must be serialized
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
var settings = new RecognitionSettings { Language = "English" };
Parallel.ForEach(imagePaths, imagePath =>
{
lock (engineLock) // Serialize: one recognition at a time
{
var document = engine.CreateDocument();
try
{
document.AddPage(imagePath);
document.Recognize(settings);
results[imagePath] = document.GetText();
}
finally
{
document.Dispose();
}
}
});
engine.Shutdown();
return results;
}
// OmniPage: Shared engine with locking to serialize document operations
public ConcurrentDictionary<string, string> ProcessBatchWithEngine(
string[] imagePaths, string licensePath)
{
var results = new ConcurrentDictionary<string, string>();
var engineLock = new object();
// One engine — document operations must be serialized
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
var settings = new RecognitionSettings { Language = "English" };
Parallel.ForEach(imagePaths, imagePath =>
{
lock (engineLock) // Serialize: one recognition at a time
{
var document = engine.CreateDocument();
try
{
document.AddPage(imagePath);
document.Recognize(settings);
results[imagePath] = document.GetText();
}
finally
{
document.Dispose();
}
}
});
engine.Shutdown();
return results;
}
Imports System.Collections.Concurrent
Imports System.Threading.Tasks
' OmniPage: Shared engine with locking to serialize document operations
Public Function ProcessBatchWithEngine(imagePaths As String(), licensePath As String) As ConcurrentDictionary(Of String, String)
Dim results As New ConcurrentDictionary(Of String, String)()
Dim engineLock As New Object()
' One engine — document operations must be serialized
Using engine As New OmniPageEngine()
engine.SetLicenseFile(licensePath)
engine.Initialize()
Dim settings As New RecognitionSettings With {.Language = "English"}
Parallel.ForEach(imagePaths, Sub(imagePath)
SyncLock engineLock ' Serialize: one recognition at a time
Dim document = engine.CreateDocument()
Try
document.AddPage(imagePath)
document.Recognize(settings)
results(imagePath) = document.GetText()
Finally
document.Dispose()
End Try
End SyncLock
End Sub)
engine.Shutdown()
End Using
Return results
End Function
Approche IronOCR :
// IronOCR: One IronTesseract per thread — no locking, genuine parallelism
public ConcurrentDictionary<string, string> ProcessBatchInParallel(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
Parallel.ForEach(imagePaths, imagePath =>
{
// Each thread creates its own instance — no shared state, no locks
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = ocr.Read(input);
results[imagePath] = result.Text;
});
return results;
}
// IronOCR: One IronTesseract per thread — no locking, genuine parallelism
public ConcurrentDictionary<string, string> ProcessBatchInParallel(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
Parallel.ForEach(imagePaths, imagePath =>
{
// Each thread creates its own instance — no shared state, no locks
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = ocr.Read(input);
results[imagePath] = result.Text;
});
return results;
}
Imports System.Collections.Concurrent
Imports System.Threading.Tasks
Imports IronOcr
' IronOCR: One IronTesseract per thread — no locking, genuine parallelism
Public Function ProcessBatchInParallel(imagePaths As String()) As ConcurrentDictionary(Of String, String)
Dim results As New ConcurrentDictionary(Of String, String)()
Parallel.ForEach(imagePaths, Sub(imagePath)
' Each thread creates its own instance — no shared state, no locks
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadImage(imagePath)
Dim result = ocr.Read(input)
results(imagePath) = result.Text
End Using
End Sub)
Return results
End Function
La version OmniPage sérialise toute la reconnaissance via un verrou, annulant ainsi le parallélisme. La version IronOCR traite les documents simultanément, sans coordination. Pour les charges de travail par lots à haut débit, consultez l' exemple de multithreading et le guide d'optimisation de la vitesse .
Génération de PDF consultables à partir d'archives numérisées
La sortie PDF consultable d'OmniPage nécessite l'assemblage d'un pipeline de reconnaissance avec une configuration explicite OutputSettings et OutputFormat avant de sauvegarder. IronOCR génère des PDF consultables à partir d'un simple appel de méthode sur l'objet résultat :
Approche Kofax OmniPage :
// OmniPage: Searchable PDF requires OutputSettings configuration before save
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
var recognitionSettings = new RecognitionSettings
{
Language = "English",
AccuracyMode = "Maximum",
PreserveLayout = true
};
var outputSettings = new OutputSettings
{
Format = OutputFormat.SearchablePDF,
Compression = PDFCompression.Standard,
ImageQuality = 85,
EmbedFonts = true
};
foreach (var pdfPath in scannedPdfPaths)
{
var document = engine.OpenPDF(pdfPath);
document.RecognizeAll(recognitionSettings);
string outputPath = Path.Combine(
outputDirectory,
Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");
document.SaveAs(outputPath, outputSettings);
document.Dispose();
}
engine.Shutdown();
}
// OmniPage: Searchable PDF requires OutputSettings configuration before save
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
var recognitionSettings = new RecognitionSettings
{
Language = "English",
AccuracyMode = "Maximum",
PreserveLayout = true
};
var outputSettings = new OutputSettings
{
Format = OutputFormat.SearchablePDF,
Compression = PDFCompression.Standard,
ImageQuality = 85,
EmbedFonts = true
};
foreach (var pdfPath in scannedPdfPaths)
{
var document = engine.OpenPDF(pdfPath);
document.RecognizeAll(recognitionSettings);
string outputPath = Path.Combine(
outputDirectory,
Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");
document.SaveAs(outputPath, outputSettings);
document.Dispose();
}
engine.Shutdown();
}
Imports System.IO
' OmniPage: Searchable PDF requires OutputSettings configuration before save
Public Sub ConvertArchiveToSearchable(scannedPdfPaths As String(), outputDirectory As String)
Using engine As New OmniPageEngine()
engine.SetLicenseFile(licensePath)
engine.Initialize()
Dim recognitionSettings As New RecognitionSettings With {
.Language = "English",
.AccuracyMode = "Maximum",
.PreserveLayout = True
}
Dim outputSettings As New OutputSettings With {
.Format = OutputFormat.SearchablePDF,
.Compression = PDFCompression.Standard,
.ImageQuality = 85,
.EmbedFonts = True
}
For Each pdfPath As String In scannedPdfPaths
Dim document = engine.OpenPDF(pdfPath)
document.RecognizeAll(recognitionSettings)
Dim outputPath As String = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(pdfPath) & "_searchable.pdf")
document.SaveAs(outputPath, outputSettings)
document.Dispose()
Next
End Using
engine.Shutdown()
End Sub
Approche IronOCR :
// IronOCR: Searchable PDF output is one method call on the result
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
var ocr = new IronTesseract();
foreach (var pdfPath in scannedPdfPaths)
{
using var input = new OcrInput();
input.LoadPdf(pdfPath); // All pages loaded automatically
var result = ocr.Read(input);
string outputPath = Path.Combine(
outputDirectory,
Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");
result.SaveAsSearchablePdf(outputPath);
Console.WriteLine($"Processed: {Path.GetFileName(pdfPath)} ({result.Pages.Length} pages)");
}
}
// IronOCR: Searchable PDF output is one method call on the result
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
var ocr = new IronTesseract();
foreach (var pdfPath in scannedPdfPaths)
{
using var input = new OcrInput();
input.LoadPdf(pdfPath); // All pages loaded automatically
var result = ocr.Read(input);
string outputPath = Path.Combine(
outputDirectory,
Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");
result.SaveAsSearchablePdf(outputPath);
Console.WriteLine($"Processed: {Path.GetFileName(pdfPath)} ({result.Pages.Length} pages)");
}
}
Imports System.IO
Imports IronOcr
Public Sub ConvertArchiveToSearchable(scannedPdfPaths As String(), outputDirectory As String)
Dim ocr As New IronTesseract()
For Each pdfPath As String In scannedPdfPaths
Using input As New OcrInput()
input.LoadPdf(pdfPath) ' All pages loaded automatically
Dim result = ocr.Read(input)
Dim outputPath As String = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(pdfPath) & "_searchable.pdf")
result.SaveAsSearchablePdf(outputPath)
Console.WriteLine($"Processed: {Path.GetFileName(pdfPath)} ({result.Pages.Length} pages)")
End Using
Next
End Sub
SaveAsSearchablePdf intègre une couche de texte dans le PDF, le rendant indexable dans les systèmes de gestion de documents sans altérer l'apparence visuelle de la numérisation originale. Le guide PDF consultable couvre les options de la couche texte et l' exemple de reconnaissance optique de caractères (OCR) PDF démontre le traitement de bout en bout des PDF numérisés.
Référence de mappage de l'API Kofax OmniPagevers IronOCR
| Kofax OmniPage | Équivalent d'IronOCR |
|---|---|
using Kofax.OmniPage.CSDK; |
using IronOcr; |
using Kofax.OmniPageCSDK; |
using IronOcr; |
using CSDK; |
using IronOcr; |
new OmniPageEngine() |
Non requis — aucun objet moteur |
engine.SetLicenseFile(path) |
IronOcr.License.LicenseKey = "key"; |
engine.Initialize() |
Non requis |
engine.Shutdown() |
Non requis |
engine.CreateDocument() |
new OcrInput() |
document.AddPage(imagePath) |
input.LoadImage(imagePath) |
engine.OpenPDF(pdfPath) |
input.LoadPdf(pdfPath) |
engine.OpenTiff(tiffPath) |
input.LoadImageFrames(tiffPath) |
document.Recognize(settings) |
new IronTesseract().Read(input) |
document.RecognizeAll(settings) |
new IronTesseract().Read(input) (toutes les pages à la fois) |
document.GetText() |
result.Text |
document.GetZoneText(zoneName) |
input.LoadImage(path, cropRectangle) + result.Text |
document.Dispose() |
using var input = new OcrInput() (automatique) |
iterator.GetText() |
result.Pages[n].Words[m].Text |
iterator.GetBoundingBox() |
result.Pages[n].Words[m].X / Y / Width / Height |
iterator.GetConfidence() |
result.Pages[n].Words[m].Confidence |
engine.LoadLanguageDictionary("German") |
dotnet add package IronOcr.Languages.German |
settings.PrimaryLanguage = "English" |
ocr.Language = OcrLanguage.English; |
settings.SecondaryLanguages = new[] {"German"} |
ocr.Language = OcrLanguage.English + OcrLanguage.German; |
settings.DeskewImage = true |
input.Deskew(); |
settings.DespeckleLevel = 2 |
input.DeNoise(); |
settings.ContrastEnhancement = true |
input.Contrast(); |
settings.AutoRotate = true |
input.Deskew(); (inclut la correction de rotation) |
document.SaveAs(path, outputSettings) |
result.SaveAsSearchablePdf(path) |
OutputFormat.SearchablePDF |
result.SaveAsSearchablePdf(path) |
OutputFormat.XML (avec coordonnées) |
result.Pages / .Paragraphs / .Words graphe d'objets |
FormZone avec limites de coordonnées |
new CropRectangle(x, y, width, height) |
| Comptage des licences par page | Sans objet |
.lic déploiement de fichier |
Sans objet |
| configuration du serveur de licences | Sans objet |
Problèmes de migration courants et solutions
Problème 1 : Exceptions " Fichier de licence introuvable " en production
Kofax OmniPage : Chaque déploiement nécessite que le fichier .lic existe à un chemin spécifique accessible au processus d'application. Un pipeline de déploiement qui ne copie pas explicitement le fichier de licence sur le serveur cible cause FileNotFoundException ou LicenseException lors de l'initialisation du moteur. Les équipes de sécurité signalent souvent l'incorporation de fichiers .lic dans les images de conteneur ou leur engagement dans le contrôle de source.
Solution : IronOCR lit sa licence à partir d'une chaîne de caractères. Stockez la clé comme variable d'environnement et lisez-la au démarrage — aucun fichier à déployer, aucun chemin à configurer :
// At application startup — reads IRONOCR_LICENSE_KEY from environment
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
?? throw new InvalidOperationException("IronOCR license key not configured.");
// At application startup — reads IRONOCR_LICENSE_KEY from environment
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
?? throw new InvalidOperationException("IronOCR license key not configured.");
Imports System
' At application startup — reads IRONOCR_LICENSE_KEY from environment
IronOcr.License.LicenseKey = If(Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY"), Throw New InvalidOperationException("IronOCR license key not configured."))
Dans Docker, passez --env IRONOCR_LICENSE_KEY=YOUR-KEY. Dans Azure App Service, configurez-le en tant que paramètre d'application. Dans Kubernetes, injectez-le via un secret. Aucun montage de fichiers, aucune configuration de chemin, aucun contrôle de sécurité pour les fichiers de licence intégrés.
Problème n° 2 : Blocage des licences flottantes après un plantage du processus
Kofax OmniPage : Lorsqu'un processus d'application plante, est tué par une surveillance, ou se termine via Environment.FailFast, engine.Shutdown() ne s'exécute pas. Les licences flottantes restent utilisées jusqu'à l'expiration du délai d'expiration du serveur de licences, généralement de 30 à 60 minutes. Dans un environnement conteneurisé où les pods redémarrent fréquemment, ce comportement épuise le pool de licences.
Solution : IronOCR n'a pas la notion de poste de licence emprunté. Un plantage de processus ne libère aucune ressource et ne verrouille aucun système externe. La prochaine étape commence immédiatement, sans attente pour la disponibilité des places :
// IronOCR: Process crash has no license implications whatsoever
// Start processing immediately after any failure
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("document.jpg"); // Non seat to check out
// IronOCR: Process crash has no license implications whatsoever
// Start processing immediately after any failure
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("document.jpg"); // Non seat to check out
' IronOCR: Process crash has no license implications whatsoever
' Start processing immediately after any failure
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim result = New IronTesseract().Read("document.jpg") ' Non seat to check out
Pour des services ASP.NET Core résilients, consultez le guide OCR asynchrone pour des modèles qui s'intègrent parfaitement aux services hébergés et à l'arrêt en douceur.
Problème 3 : Échec de la création de l'image Docker — L'installateur ne peut pas s'exécuter en mode non interactif
Kofax OmniPage : L'installateur SDK OmniPage est un installateur GUI ou semi-interactif qui ne s'exécute pas proprement dans un contexte docker build. Les équipes qui tentent d'intégrer le SDK dans une image conteneur rencontrent des problèmes lors de l'accord de licence ou des étapes de sélection des composants de l'installateur. Les solutions de contournement (options d'installation silencieuse, copies de DLL pré-extraites) ne sont pas documentées et sont spécifiques à chaque version.
Solution : Dockerfile d'IronOCR est trois lignes :
FROM mcr.microsoft.com/dotnet/aspnet:8.0
RUN apt-get update && apt-get install -y libgdiplus # Single Linux dependency
COPY --from=build /app/publish /app
WORKDIR /app
ENTRYPOINT ["dotnet", "YourApp.dll"]
libgdiplus est la seule dépendance système. Le package NuGet IronOCR est restauré par dotnet restore à l'intérieur de l'étape de construction comme toute autre référence de package. Le guide de déploiement Docker couvre les images de base Debian et Alpine, et le guide de déploiement Linux couvre les noms de paquets spécifiques à chaque distribution.
Problème 4 : Réactivation requise après la migration de la machine virtuelle ou la mise à l'échelle automatique dans le cloud
Kofax OmniPage : L'activation d'OmniPage est liée à l'identité du matériel. Les environnements cloud qui migrent des machines virtuelles entre hôtes physiques, effectuent une mise à l'échelle automatique vers de nouvelles instances ou remplacent les conteneurs par de nouvelles images déclenchent des changements d'identité matérielle qui nécessitent une réactivation. Contacter le support de Tungsten pour une réactivation en cours d'incident ajoute un risque opérationnel.
Solution : La clé de licence d'IronOCR est indépendante du matériel. La même clé fonctionne sur n'importe quelle machine, n'importe quel conteneur, n'importe quelle région cloud, n'importe quel nombre d'instances à mise à l'échelle automatique au sein du niveau de licence. Aucune réactivation, aucun contact avec le support, aucune interruption de service :
// Identical startup code on any hardware topology
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
bool valid = IronOcr.License.IsLicensed; // Verify without a network call
// Identical startup code on any hardware topology
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
bool valid = IronOcr.License.IsLicensed; // Verify without a network call
' Identical startup code on any hardware topology
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim valid As Boolean = IronOcr.License.IsLicensed ' Verify without a network call
Problème 5 : L'objet RecognitionSettings n'a pas d'équivalent IronOCR
Kofax OmniPage : OmniPage utilise un objet RecognitionSettings (ou PreprocessingSettings selon la version du SDK) pour configurer le comportement du moteur par document. Les équipes en cours de migration s'attendent à un objet de configuration parallèle dans IronOCR.
Solution : IronOCR divise la configuration entre deux surfaces : opérations de prétraitement sur OcrInput et paramètres du moteur sur IronTesseract. Il n'existe aucun objet de paramètres à instancier :
// OmniPage settings object → IronOCR method calls on OcrInput
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.English; // RecognitionSettings.Language
// ocr.Configuration.TesseractVersion = ... // Engine version already optimized
using var input = new OcrInput();
input.LoadImage(imagePath);
input.Deskew(); // settings.DeskewImage = true
input.DeNoise(); // settings.DespeckleLevel = 2
input.Contrast(); // settings.ContrastEnhancement = true
var result = ocr.Read(input);
// OmniPage settings object → IronOCR method calls on OcrInput
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.English; // RecognitionSettings.Language
// ocr.Configuration.TesseractVersion = ... // Engine version already optimized
using var input = new OcrInput();
input.LoadImage(imagePath);
input.Deskew(); // settings.DeskewImage = true
input.DeNoise(); // settings.DespeckleLevel = 2
input.Contrast(); // settings.ContrastEnhancement = true
var result = ocr.Read(input);
Imports IronOcr
Dim ocr As New IronTesseract()
ocr.Language = OcrLanguage.English
Using input As New OcrInput()
input.LoadImage(imagePath)
input.Deskew()
input.DeNoise()
input.Contrast()
Dim result = ocr.Read(input)
End Using
Le guide de correction de la qualité d'image répertorie toutes les méthodes de prétraitement disponibles et indique quels filtres s'appliquent à quels profils de qualité de document.
Problème n° 6 : Les fichiers de modèles de zone ne peuvent pas être portés directement.
Kofax OmniPage : Les modèles de formulaire OmniPage stockent les définitions de zones dans des fichiers de modèles propriétaires .fdt ou .fpf qui définissent les positions des champs, les types de reconnaissance, et les règles de validation par classe de document. Ces fichiers ne sont pas importables par IronOCR.
Solution : Extrayez les données de coordonnées des fichiers de modèles (ils sont basés sur XML) et convertissez chaque zone en un CropRectangle. Les noms des champs deviennent des clés du dictionnaire ; Les coordonnées de zone se mappent directement aux paramètres (x, y, width, height) :
// Convert OmniPage zone definition to IronOCR CropRectangle
// OmniPage zone: Name="TotalDue" X="490" Y="612" Width="180" Height="28"
// IronOCR equivalent:
var totalDueRegion = new CropRectangle(490, 612, 180, 28);
using var input = new OcrInput();
input.LoadImage(invoicePath, totalDueRegion);
string totalDue = new IronTesseract().Read(input).Text.Trim();
// Convert OmniPage zone definition to IronOCR CropRectangle
// OmniPage zone: Name="TotalDue" X="490" Y="612" Width="180" Height="28"
// IronOCR equivalent:
var totalDueRegion = new CropRectangle(490, 612, 180, 28);
using var input = new OcrInput();
input.LoadImage(invoicePath, totalDueRegion);
string totalDue = new IronTesseract().Read(input).Text.Trim();
Imports IronOcr
' Convert OmniPage zone definition to IronOCR CropRectangle
' OmniPage zone: Name="TotalDue" X="490" Y="612" Width="180" Height="28"
' IronOCR equivalent:
Dim totalDueRegion As New CropRectangle(490, 612, 180, 28)
Using input As New OcrInput()
input.LoadImage(invoicePath, totalDueRegion)
Dim totalDue As String = New IronTesseract().Read(input).Text.Trim()
End Using
Pour les documents où les zones varient par page, chargez la même image avec différentes valeurs CropRectangle par région plutôt que de recharger le fichier. L' exemple de recadrage de région démontre la lecture de plusieurs régions à partir d'une seule source d'image.
Liste de contrôle pour la migration Kofax OmniPage
Pré-migration
Identifiez tous les points d'intégration d'OmniPage dans le code source :
# Find all OmniPage namespace references
grep -r "Kofax\|OmniPage\|CSDK" --include="*.cs" --include="*.csproj" .
# Find engine lifecycle calls
grep -r "Initialize\|Shutdown\|SetLicenseFile" --include="*.cs" .
# Find document and zone creation patterns
grep -r "CreateDocument\|AddPage\|FormZone\|RecognitionSettings\|PreprocessingSettings" --include="*.cs" .
# Find output format configuration
grep -r "OutputSettings\|OutputFormat\|SaveAs\|GetText" --include="*.cs" .
# Find license file path references
grep -r "\.lic\|license\.lic\|licensePath" --include="*.cs" --include="*.config" --include="*.json" .
# Find all OmniPage namespace references
grep -r "Kofax\|OmniPage\|CSDK" --include="*.cs" --include="*.csproj" .
# Find engine lifecycle calls
grep -r "Initialize\|Shutdown\|SetLicenseFile" --include="*.cs" .
# Find document and zone creation patterns
grep -r "CreateDocument\|AddPage\|FormZone\|RecognitionSettings\|PreprocessingSettings" --include="*.cs" .
# Find output format configuration
grep -r "OutputSettings\|OutputFormat\|SaveAs\|GetText" --include="*.cs" .
# Find license file path references
grep -r "\.lic\|license\.lic\|licensePath" --include="*.cs" --include="*.config" --include="*.json" .
Inventaire avant de commencer :
- Compter les fichiers avec des importations d'espace de noms OmniPage
- Listez toutes les définitions
FormTemplateetFormZoneet extrayez leurs données de coordonnées. - Identifiez quels dictionnaires linguistiques sont chargés et mappés aux packages NuGet
IronOcr.Languages.*. - Notez les formats de sortie utilisés (PDF consultable, texte brut, XML) et leurs équivalents IronOCR.
- Localisez toutes les références de fichiers
.licdans les scripts de déploiement et la configuration.
Migration de code
- Supprimez les références de DLL OmniPage de tous les fichiers
.csproj. - Exécutez
dotnet add package IronOcrdans chaque projet effectuant de l'OCR. - Ajoutez des packs linguistiques pour chaque langue utilisée :
dotnet add package IronOcr.Languages.[Language]. - Ajoutez
IronOcr.License.LicenseKey = ...;à chaque point d'entrée de l'application (Program.cs, Startup.cs, ou hôte de service). - Remplacez toutes les
using Kofax.OmniPage.CSDK;,using CSDK;, et importations de namespace connexes parusing IronOcr;. - Supprimez toutes les constructions
OmniPageEngine,SetLicenseFile, et les appelsInitialize. - Supprimez tous les appels
engine.Shutdown()et les implémentationsIDisposablequi existent uniquement pour assurer l'arrêt. - Remplacez
engine.CreateDocument()+document.AddPage()parnew OcrInput()+input.LoadImage(). - Remplacez
engine.OpenPDF()+ itération par page parinput.LoadPdf()(toutes les pages en un appel). - Remplacez
engine.OpenTiff()+ boucles par cadre parinput.LoadImageFrames(). - Remplacez
document.Recognize(settings)parnew IronTesseract().Read(input). - Convertissez les données de coordonnées
FormZoneen instancesCropRectangle(x, y, width, height). - Remplacez
document.GetZoneText()parinput.LoadImage(path, cropRectangle)+result.Textpar région. - Remplacez
document.SaveAs(path, outputSettings)pour PDF consultable parresult.SaveAsSearchablePdf(path). - Remplacez les modèles d'itérateur de résultat par une navigation des propriétés
result.Pages/.Paragraphs/.Words. - Supprimez les objets
PreprocessingSettingset remplacez les indicateurs booléens par des appels de méthode explicitesinput.Deskew(),input.DeNoise(),input.Contrast(). - Supprimez les chemins d'accès aux fichiers de licence des scripts de déploiement, des fichiers de configuration et des modèles d'infrastructure en tant que code.
Après la migration
- Vérifier la précision de la reconnaissance optique de caractères (OCR) sur un échantillon représentatif de plus de 100 documents issus du corpus réel : comparer les résultats avec la sortie OmniPage ligne par ligne pour les factures, les contrats et les formulaires.
- Confirmez que l'extraction de zone basée sur
CropRectangleretourne du texte correspondant à la sortie OmniPageGetZoneText()pour chaque champ mappé. - Tester le traitement des PDF multipages : vérifier le nombre de pages, leur ordre et la continuité du texte
- Tester le traitement TIFF multi-images : vérifier que toutes les images sont traitées et que la séquence d'images est préservée.
- Vérifier que le fichier PDF consultable est indexable dans le système de gestion documentaire utilisé (SharePoint, OpenText, etc.).
- Effectuer un test de traitement parallèle avec plus de 50 documents simultanés et confirmer l'absence de problèmes de sécurité des threads
- Testez la couverture des packs de langues : chargez chaque dictionnaire de langue précédemment utilisé via
IronOcr.Languages.*et vérifiez la qualité de reconnaissance. - Confirmez que les plantages de processus et les redémarrages de conteneurs ne nécessitent aucune action de récupération de licence.
- Exécutez la construction Docker sur un agent CI — vérifiez que
dotnet restorerésout IronOCR sans étapes d'installation. - Vérifier que les scores de confiance sont disponibles par mot et correspondent aux exigences de qualité des données de tout flux de validation en aval.
- Vérifiez que l'application démarre proprement sans le fichier
.licprésent dans n'importe quel chemin.
Principaux avantages de la migration vers IronOCR
La complexité du déploiement passe de semaines à minutes. Un projet qui nécessitait auparavant l'accès aux installateurs SDK, au déploiement de fichiers .lic, à la configuration du pare-feu pour le serveur de licences, et à la coordination de l'équipe d'infrastructures, se déploie maintenant via dotnet restore. Un nouveau développeur clone le dépôt et exécute le projet. Un nouveau serveur de production est provisionné par le pipeline CI/CD. Les images conteneurisées sont créées sans étape d'installation.
Le risque de licence disparaît entièrement. Il n'y a pas de sièges flottants à épuiser, pas de délais d'attente à supporter, pas d'empreintes matérielles à réactiver, et aucun fichier .lic à protéger dans les pipelines de déploiement. Un plantage d'application à 3 heures du matin ne libère ni ne verrouille rien. L'ingénieur d'astreinte relance le processus ; La reconnaissance optique de caractères (OCR) reprend immédiatement. La page produit IronOCR couvre tous les niveaux de licence.
Le déploiement multiplateforme devient une cible standard de NuGet . Les machines de développement macOS fonctionnent sans exception de plateforme. Les serveurs de production Linux ne nécessitent pas une version du SDK de janvier 2026. Les conteneurs Docker construisent à partir d'une image de base mcr.microsoft.com/dotnet/aspnet standard avec une dépendance système. La même référence de package IronOcr dans .csproj produit une construction fonctionnelle sur Windows, Linux, et macOS. Consultez le guide de déploiement Azure et le guide de déploiement AWS pour la configuration spécifique au cloud.
Le débit parallèle est proportionnel au matériel. L'architecture de moteur partagé d'OmniPage nécessite un verrouillage qui sérialise la reconnaissance simultanée. Les instances IronTesseract d'IronOCR sans état se mettent à l'échelle de manière linéaire avec les cœurs de CPU disponibles. Doubler le nombre de cœurs d'un serveur de traitement par lots double le débit sans modification de configuration ni licence supplémentaire.
L'accès aux données structurées est immédiat. OcrResult.Pages, Paragraphs, Lines, Words, et Characters exposent la structure de document complète en tant que graphe d'objet .NET navigable. La confiance et les coordonnées par mot sont des propriétés de chaque objet mot. Il n'y a pas de pipeline d'exportation secondaire, pas de configuration de format et aucune sortie de fichier requise pour accéder aux données positionnelles.
Le coût est fixe et prévisible. La combinaison de la licence SDK, de la maintenance annuelle et des frais d'exécution par page proposée par OmniPage engendre un coût qui évolue en fonction de l'utilisation et qui est facturé annuellement. IronOCR à $999–$2,999 perpétuel est un achat unique. Un flux de travail de 500 000 pages par an, générant des frais par page, permet de récupérer le coût de la licence IronOCR en quelques semaines. La IronOCR et les tutoriels d'IronOCR sont disponibles sans contrat d'assistance.
Questions Fréquemment Posées
Pourquoi devrais-je migrer de Kofax OmniPage vers IronOCR ?
Parmi les motivations communes, citons l'élimination de la complexité de l'interopérabilité COM, le remplacement de la gestion des licences basée sur les fichiers, l'absence de facturation à la page, l'activation du déploiement Docker/conteneur et l'adoption d'un flux de travail NuGet-natif qui s'intègre à l'outillage .NET standard.
Quels sont les principaux changements de code lors de la migration de Kofax OmniPage vers IronOcr ?
Remplacer les séquences d'initialisation de Kofax OmniPage par l'instanciation d'IronTesseract, supprimer la gestion du cycle de vie de COM (modèles explicites Create/Load/Close) et mettre à jour les noms des propriétés des résultats. Le résultat est une réduction significative du nombre de lignes de code.
Comment installer IronOCR pour commencer la migration ?
Exécutez "Install-Package IronOcr" dans la console du Package Manager ou "dotnet add package IronOcr" dans le CLI. Les packs de langues sont des paquets distincts : 'dotnet add package IronOcr.Languages.French' pour le français, par exemple.
IronOCR offre-t-il la même précision d'OCR que Kofax OmniPage pour les documents commerciaux standard ?
IronOcr atteint un niveau de précision élevé pour les contenus commerciaux standard, notamment les factures, les contrats, les reçus et les formulaires dactylographiés. Les filtres de prétraitement d'image (désalignement, suppression du bruit, amélioration du contraste) améliorent encore la reconnaissance sur des données dégradées.
Comment IronOCR gère-t-il les données linguistiques que Kofax OmniPage installe séparément ?
Les données linguistiques de l'IronOcr sont distribuées sous forme de packages NuGet. 'dotnet add package IronOcr.Languages.German' installe la prise en charge de l'allemand. Il n'y a pas de placement manuel de fichiers ou de chemins d'accès aux répertoires.
La migration de Kofax OmniPage vers IronOCR nécessite-t-elle de modifier l'infrastructure de déploiement ?
IronOcr nécessite moins de changements d'infrastructure que Kofax OmniPage. Il n'y a pas de chemins binaires SDK, de placements de fichiers de licence ou de configurations de serveurs de licence. Le package NuGet contient le moteur d'OCR complet, et la clé de licence est une chaîne de caractères définie dans le code de l'application.
Comment configurer les licences IronOCR après la migration ?
Attribuer IronOcr.License.LicenseKey = "YOUR-KEY" dans le code de démarrage de l'application. Dans Docker ou Kubernetes, stockez la clé dans une variable d'environnement et lisez-la au démarrage. Utilisez License.IsValidLicense pour valider avant d'accepter le trafic.
IronOCR peut-il traiter les PDF de la même manière que Kofax OmniPage ?
Oui, IronOCR lit aussi bien les PDF natifs que les PDF numérisés. Instanciez IronTesseract, appelez ocr.Read(input) où l'input est un chemin PDF ou OcrPdfInput, et itérez les pages OcrResult. Aucun pipeline de rendu PDF séparé n'est nécessaire.
Comment IronOcr gère-t-il le threading dans le cadre d'un traitement à haut volume ?
IronTesseract est sûr pour l'instanciation par thread. Créez une instance par thread dans un Parallel.ForEach ou un Task pool, exécutez l'OCR simultanément et disposez de chaque instance lorsque vous avez terminé. Aucun état global ou verrouillage n'est nécessaire.
Quels formats de sortie IronOCR prend-il en charge après l'extraction du texte ?
IronOCR renvoie des résultats structurés comprenant le texte, les coordonnées des mots, les scores de confiance et la structure des pages. Les options d'exportation comprennent le texte brut, le PDF interrogeable et les objets de résultats structurés pour le traitement en aval.
La tarification d'IronOcr est-elle plus prévisible que celle de Kofax OmniPage pour la mise à l'échelle des charges de travail ?
IronOCR utilise des licences perpétuelles forfaitaires sans frais par page ou par volume. Que vous traitiez 10 000 ou 10 millions de pages, le coût de la licence reste constant. Les options de licence en volume et en équipe sont disponibles sur la page de tarification d'IronOcr.
Qu'advient-il de mes tests existants après la migration de Kofax OmniPage vers IronOcr ?
Les tests qui vérifient le contenu du texte extrait doivent continuer à passer après la migration. Les tests qui valident les modèles d'appel d'API ou le cycle de vie des objets COM devront être mis à jour pour refléter le modèle d'initialisation et de résultat plus simple d'IronOcr.

