Passer au contenu du pied de page
VIDéOS

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
$vbLabelText   $csharpLabel
// 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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

É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"
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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."))
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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" .
SHELL

Inventaire avant de commencer :

  • Compter les fichiers avec des importations d'espace de noms OmniPage
  • Listez toutes les définitions FormTemplate et FormZone et 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 .lic dans les scripts de déploiement et la configuration.

Migration de code

  1. Supprimez les références de DLL OmniPage de tous les fichiers .csproj.
  2. Exécutez dotnet add package IronOcr dans chaque projet effectuant de l'OCR.
  3. Ajoutez des packs linguistiques pour chaque langue utilisée : dotnet add package IronOcr.Languages.[Language].
  4. Ajoutez IronOcr.License.LicenseKey = ...; à chaque point d'entrée de l'application (Program.cs, Startup.cs, ou hôte de service).
  5. Remplacez toutes les using Kofax.OmniPage.CSDK;, using CSDK;, et importations de namespace connexes par using IronOcr;.
  6. Supprimez toutes les constructions OmniPageEngine, SetLicenseFile, et les appels Initialize.
  7. Supprimez tous les appels engine.Shutdown() et les implémentations IDisposable qui existent uniquement pour assurer l'arrêt.
  8. Remplacez engine.CreateDocument() + document.AddPage() par new OcrInput() + input.LoadImage().
  9. Remplacez engine.OpenPDF() + itération par page par input.LoadPdf() (toutes les pages en un appel).
  10. Remplacez engine.OpenTiff() + boucles par cadre par input.LoadImageFrames().
  11. Remplacez document.Recognize(settings) par new IronTesseract().Read(input).
  12. Convertissez les données de coordonnées FormZone en instances CropRectangle(x, y, width, height).
  13. Remplacez document.GetZoneText() parinput.LoadImage(path, cropRectangle) + result.Textpar région.
  14. Remplacez document.SaveAs(path, outputSettings) pour PDF consultable par result.SaveAsSearchablePdf(path).
  15. Remplacez les modèles d'itérateur de résultat par une navigation des propriétés result.Pages / .Paragraphs / .Words.
  16. Supprimez les objets PreprocessingSettings et remplacez les indicateurs booléens par des appels de méthode explicites input.Deskew(), input.DeNoise(), input.Contrast().
  17. 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 CropRectangle retourne du texte correspondant à la sortie OmniPage GetZoneText() 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 restore ré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 .lic pré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.

Veuillez noterKofax OmniPage, OpenPDF, et Tesseract sont des marques déposées de leurs propriétaires respectifs. Ce site n'est pas affilié à, approuvé par ou sponsorisé par Google, Kofax, ou LibrePDF. Tous les noms de produits, logos et marques sont la propriété de leurs propriétaires respectifs. Les comparaisons sont à titre informatif uniquement et reflètent les informations publiquement disponibles au moment de l'écriture.

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.

Kannaopat Udonpant
Ingénieur logiciel
Avant de devenir ingénieur logiciel, Kannapat a obtenu un doctorat en ressources environnementales à l'université d'Hokkaido au Japon. Pendant qu'il poursuivait son diplôme, Kannapat est également devenu membre du laboratoire de robotique de véhicules, qui fait partie du département de bioproduction. En 2022, il a utilisé ses compé...
Lire la suite

Équipe de soutien Iron

Nous sommes en ligne 24 heures sur 24, 5 jours sur 7.
Chat
Email
Appelez-moi