Migration d'ABBYY FineReader vers IronOcr
Ce guide accompagne les développeurs .NET à chaque étape du remplacement du kit de développement logiciel Moteur ABBYY FineReader par IronOCR . Il décrit les étapes mécaniques de la suppression des dépendances COM et des artefacts d'installation du SDK, établit une correspondance entre l'API d'ABBYY et ses équivalents IronOCR , et fournit des exemples de code avant/après pour les modèles les plus fréquemment rencontrés dans les intégrations ABBYY en production. Les cibles de migration sont les équipes qui ont décidé que le coût d'entreprise et la complexité de déploiement d'ABBYY ne s'alignent plus avec leurs exigences de projet.
Pourquoi migrer depuis ABBYY FineReader ?
ABBYY FineReader Engine est une plateforme OCR performante, mais son architecture a été conçue pour les Enterprise Windows disposant d'équipes d'infrastructure dédiées. Lorsque la charge de travail réelle d'une équipe .NET consiste à traiter des factures, à numériser des contrats ou à extraire des formulaires scannés, cette architecture devient un handicap plutôt qu'un atout.
La dette d'interopérabilité COM s'accumule avec le temps. Chaque intégration ABBYY dans .NET passe par une couche d'interopérabilité COM. Les objets COM nécessitent une gestion explicite du cycle de vie : créer, initialiser, traiter, puis fermer dans un bloc finally ou le processus fuit de la mémoire. Chaque chemin de code qui touche ABBYY présente ce modèle. Sur deux ou trois ans d'ajouts de fonctionnalités, cette cérémonie de cycle de vie se propage à travers les classes de services, les processus en arrière-plan et les gestionnaires de requêtes. Le résultat est de 30 à 50 % de code standard dans chaque classe liée à l'OCR qui disparaît complètement lorsque vous passez à IronTesseract.
Le programme d'installation du SDK bloque les modèles de déploiement modernes. ABBYY déploie ses produits via un programme d'installation de SDK Windows qui place les fichiers binaires, les données de langue, les fichiers d'exécution et les fichiers de licence dans des chemins d'accès codés en dur. La containerisation d'un service qui utilise ABBYY nécessite soit de créer une image de base personnalisée de plus de 300 Mo à partir de la sortie de cet installateur, soit de monter des volumes avec des fichiers de licence au démarrage. Aucune des approches ne convient à un pipeline Kubernetes standard ou cloud-native. IronOCR est un package NuGet : le même dotnet restore qui télécharge toutes les autres dépendances télécharge le moteur OCR complet.
La facturation à la page transforme le volume en centre de coûts. Les modèles de licences d'ABBYY basés sur le volume facturent chaque page traitée au-delà des seuils inclus. Une application qui traite 50 000 documents par mois à son lancement et qui atteint 500 000 deux ans plus tard voit ses coûts OCR augmenter proportionnellement à son succès. IronOCR facture un tarif fixe pour la licence : une équipe traitant deux millions de pages par mois paie exactement le même coût de licence qu'une équipe en traitant deux mille.
Les données linguistiques nécessitent une coordination manuelle du déploiement. Les modules linguistiques ABBYY sont stockés sous forme de fichiers dans le répertoire d'exécution du SDK. L'ajout d'une langue implique d'identifier les fichiers de données appropriés, de les copier au bon emplacement sur chaque cible de déploiement et de mettre à jour les scripts CI/CD pour les inclure. Sur IronOCR, ajouter le français est dotnet add package IronOcr.Languages.French — le gestionnaire de packages s'occupe du reste.
Les échecs des fichiers de licence touchent la production sans avertissement. Les licences ABBYY existent sous forme de fichiers .lic et .key qui doivent être présents à des chemins de disque spécifiques lorsque loader.GetEngineObject() s'exécute. Si ces fichiers sont manquants sur un nouveau serveur de production (script de déploiement erroné, copie de fichiers ayant échoué, problème d'autorisations), l'appel génère une erreur au démarrage. Une licence expirée provoque la même erreur. La licence de IronOCR est une clé de chaîne attribuée dans le code au démarrage, stockable dans n'importe quel gestionnaire de secrets, avec une vérification effectuée par IronOcr.License.IsValidLicense avant que l'application n'accepte le trafic.
La sécurité des threads nécessite une instance unique de moteur partagé. Le moteur d'ABBYY n'est pas trivialement sécurisé pour les appels CreateFRDocument concurrents depuis plusieurs threads. Les implémentations en production utilisent des stratégies de verrouillage ou des pools de processeurs. Le IronTesseract de IronOCR est sans état : lancez une instance par thread, exécutez la reconnaissance simultanément sans verrous, disposez lorsque c'est terminé.
Le problème fondamental
// ABBYY: COM loader + license path validation + profile load — before a single pixel of OCR runs
var loader = new EngineLoader();
var engine = loader.GetEngineObject(
@"C:\Program Files\ABBYY SDK\FineReader Engine\Bin", // Breaks on every new machine
@"C:\Program Files\ABBYY SDK\License" // Fails if .lic file is missing
);
engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
var langParams = engine.CreateLanguageParams();
langParams.Languages.Add("English");
// ABBYY: COM loader + license path validation + profile load — before a single pixel of OCR runs
var loader = new EngineLoader();
var engine = loader.GetEngineObject(
@"C:\Program Files\ABBYY SDK\FineReader Engine\Bin", // Breaks on every new machine
@"C:\Program Files\ABBYY SDK\License" // Fails if .lic file is missing
);
engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
var langParams = engine.CreateLanguageParams();
langParams.Languages.Add("English");
' ABBYY: COM loader + license path validation + profile load — before a single pixel of OCR runs
Dim loader As New EngineLoader()
Dim engine = loader.GetEngineObject(
"C:\Program Files\ABBYY SDK\FineReader Engine\Bin", ' Breaks on every new machine
"C:\Program Files\ABBYY SDK\License" ' Fails if .lic file is missing
)
engine.LoadPredefinedProfile("DocumentConversion_Accuracy")
Dim langParams = engine.CreateLanguageParams()
langParams.Languages.Add("English")
// IronOCR: the entire initialization
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var ocr = new IronTesseract();
// IronOCR: the entire initialization
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var ocr = new IronTesseract();
Imports IronOcr
' IronOCR: the entire initialization
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim ocr As New IronTesseract()
Comparaison des fonctionnalités IronOCR et d'ABBYY FineReader
Le tableau suivant récapitule les fonctionnalités pertinentes pour les équipes évaluant cette migration.
| Fonction | Moteur ABBYY FineReader | IronOCR |
|---|---|---|
| Installation | Programme d'installation du SDK (Windows) | dotnet add package IronOcr |
| Acquisition | Contactez le service commercial (4 à 12 semaines) | NuGet en libre-service |
| Modèle de licence | Enterprise, par serveur ou par page | Perpétuelle, $999-2,999 une fois |
| Gestion des licences | .lic + fichiers .key sur le disque |
Clé de chaîne dans le code ou variable d'environnement |
| Intégration .NET | Interopérabilité COM | Natif .NET |
| Dépendance COM | Oui | Non |
| Sécurité des threads | Nécessite une stratégie de verrouillage | Complet (un IronTesseract par thread) |
| Langues prises en charge | 190+ | 125+ |
| Installation de la langue | Fichiers de données d'exécution au chemin du SDK | packages de langage NuGet |
| Entrée PDF | Oui (via CreatePDFFile) |
Oui (natif, input.LoadPdf()) |
| Sortie PDF consultable | Oui (pipeline d'exportation) | Oui (result.SaveAsSearchablePdf()) |
| Prétraitement automatique | Basé sur le profil | Intégré (Correction de l'inclinaison du bureau, Réduction du bruit, Contraste, Binarisation, Netteté) |
| OCR basé sur la région | Objets de zone (CreateZone, SetBounds) |
Paramètre CropRectangle |
| Lecture de codes-barres | Oui | Oui (ocr.Configuration.ReadBarCodes = true) |
| Cross-Platform | Windows, Linux, macOS | Windows, Linux, macOS, Docker, Azure, AWS |
| Déploiement Docker | Image de base personnalisée requise | Image de base standard .NET + libgdiplus |
| Évaluation de la confiance | Oui | Oui (result.Confidence) |
| Délai d'obtention du premier résultat OCR | 4 à 12 semaines (passation de commande) | Le même jour |
Démarrage rapide : Migration d'ABBYY FineReader vers IronOCR
Étape 1 : Remplacer le package NuGet
ABBYY FineReader Engine ne possède pas de package NuGet . Supprimez-le en désinstallant le SDK et en supprimant la référence d'assemblage manuel de votre fichier projet :
<Reference Include="FREngine">
<HintPath>C:\Program Files\ABBYY SDK\FineReader Engine\Bin\FREngine.dll</HintPath>
</Reference>
<Reference Include="FREngine">
<HintPath>C:\Program Files\ABBYY SDK\FineReader Engine\Bin\FREngine.dll</HintPath>
</Reference>
Ensuite, supprimez la référence d'interopérabilité COM FREngine.dll du nœud des Références de Visual Studio, ou supprimez l'entrée correspondante directement de votre fichier de projet. Installez IronOCR depuis NuGet :
dotnet add package IronOcr
Étape 2 : Mise à jour des espaces de noms
// Before (ABBYY)
using FREngine;
using ABBYY.FineReader;
// After (IronOCR)
using IronOcr;
// Before (ABBYY)
using FREngine;
using ABBYY.FineReader;
// After (IronOCR)
using IronOcr;
Imports IronOcr
Étape 3 : initialisation de la licence
Ajoutez ceci une seule fois au démarrage de l'application, avant tout appel OCR :
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 ou un gestionnaire de secrets pour les déploiements en production :
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY");
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY");
Imports System
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
Exemples de migration de code
Cycle de vie du moteur dans un service Windows vs IronTesseract sans état
La cérémonie d'initialisation du moteur d'ABBYY appartient à un wrapper de service parce que les objets EngineLoader et IEngine sont coûteux à créer. La plupart des intégrations en production encapsulent le moteur dans un service singleton avec des méthodes de démarrage et de destruction explicites.
Approche ABBYY FineReader :
using FREngine;
public class DocumentOcrService : IHostedService, IDisposable
{
private IEngine _engine;
public Task StartAsync(CancellationToken cancellationToken)
{
// Step 1: Create loader — requires Interopérabilité COM registration
var loader = new EngineLoader();
// Step 2: Load engine from SDK path — throws if license files are missing
_engine = loader.GetEngineObject(
@"C:\Program Files\ABBYY SDK\FineReader Engine\Bin",
@"C:\Program Files\ABBYY SDK\License"
);
// Step 3: Load profile before any recognition work
_engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
return Task.CompletedTask;
}
public string ProcessDocument(string imagePath)
{
// Document must be created and destroyed per call
var document = _engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
return document.PlainText.Text;
}
finally
{
document.Close(); // Memory leaks if omitted
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_engine = null; // COM cleanup
return Task.CompletedTask;
}
public void Dispose() => _engine = null;
}
using FREngine;
public class DocumentOcrService : IHostedService, IDisposable
{
private IEngine _engine;
public Task StartAsync(CancellationToken cancellationToken)
{
// Step 1: Create loader — requires Interopérabilité COM registration
var loader = new EngineLoader();
// Step 2: Load engine from SDK path — throws if license files are missing
_engine = loader.GetEngineObject(
@"C:\Program Files\ABBYY SDK\FineReader Engine\Bin",
@"C:\Program Files\ABBYY SDK\License"
);
// Step 3: Load profile before any recognition work
_engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
return Task.CompletedTask;
}
public string ProcessDocument(string imagePath)
{
// Document must be created and destroyed per call
var document = _engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
return document.PlainText.Text;
}
finally
{
document.Close(); // Memory leaks if omitted
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_engine = null; // COM cleanup
return Task.CompletedTask;
}
public void Dispose() => _engine = null;
}
Imports FREngine
Imports System.Threading
Imports System.Threading.Tasks
Public Class DocumentOcrService
Implements IHostedService, IDisposable
Private _engine As IEngine
Public Function StartAsync(cancellationToken As CancellationToken) As Task Implements IHostedService.StartAsync
' Step 1: Create loader — requires Interopérabilité COM registration
Dim loader As New EngineLoader()
' Step 2: Load engine from SDK path — throws if license files are missing
_engine = loader.GetEngineObject(
"C:\Program Files\ABBYY SDK\FineReader Engine\Bin",
"C:\Program Files\ABBYY SDK\License"
)
' Step 3: Load profile before any recognition work
_engine.LoadPredefinedProfile("DocumentConversion_Accuracy")
Return Task.CompletedTask
End Function
Public Function ProcessDocument(imagePath As String) As String
' Document must be created and destroyed per call
Dim document = _engine.CreateFRDocument()
Try
document.AddImageFile(imagePath, Nothing, Nothing)
document.Process(Nothing)
Return document.PlainText.Text
Finally
document.Close() ' Memory leaks if omitted
End Try
End Function
Public Function StopAsync(cancellationToken As CancellationToken) As Task Implements IHostedService.StopAsync
_engine = Nothing ' COM cleanup
Return Task.CompletedTask
End Function
Public Sub Dispose() Implements IDisposable.Dispose
_engine = Nothing
End Sub
End Class
Approche IronOCR :
using IronOcr;
public class DocumentOcrService
{
// Non startup, no shutdown, no COM lifecycle
// IronTesseract is stateless — create per call or reuse per thread
public string ProcessDocument(string imagePath)
{
return new IronTesseract().Read(imagePath).Text;
}
}
using IronOcr;
public class DocumentOcrService
{
// Non startup, no shutdown, no COM lifecycle
// IronTesseract is stateless — create per call or reuse per thread
public string ProcessDocument(string imagePath)
{
return new IronTesseract().Read(imagePath).Text;
}
}
Imports IronOcr
Public Class DocumentOcrService
' Non startup, no shutdown, no COM lifecycle
' IronTesseract is stateless — create per call or reuse per thread
Public Function ProcessDocument(imagePath As String) As String
Return (New IronTesseract()).Read(imagePath).Text
End Function
End Class
IronTesseract n'a pas de cycle de vie du moteur. Il s'initialise en interne lors de sa première utilisation et ne nécessite aucun arrêt explicite. Le wrapper de service hébergé, le champ IEngine et les méthodes StopAsync disparaissent tous. Si l'application traite des documents simultanément, chaque thread crée sa propre instance IronTesseract — aucun verrouillage requis. Le guide d'installation IronTesseract couvre les options de configuration y compris les propriétés TesseractVersion et Configuration.
Configuration de la langue de reconnaissance
La configuration linguistique ABBYY implique la création d'un objet LanguageParams, l'ajout de chaînes de noms de langage qui doivent correspondre aux fichiers de données installés, et l'association de ces paramètres au moteur avant que tout document ne soit traité. Chaque langue supplémentaire nécessite les fichiers de données correspondants déployés dans le chemin d'exécution.
Approche ABBYY FineReader :
using FREngine;
// Engine must already be initialized with sdkPath and licensePath
private void ConfigureLanguages(IEngine engine, string[] languageCodes)
{
// Create language parameters object
var langParams = engine.CreateLanguageParams();
// Add each language — string names must match installed data file names
// Missing data file causes runtime failure
foreach (var lang in languageCodes)
{
langParams.Languages.Add(lang); // e.g., "English", "French", "German"
}
// Language params are associated at the profile level, not per-document
// Changing languages requires reloading profile or reinitializing engine
engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
}
public string RecognizeFrenchDocument(IEngine engine, string imagePath)
{
var langParams = engine.CreateLanguageParams();
langParams.Languages.Add("French"); // Requires FrenchLanguage data files at runtime path
var document = engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
return document.PlainText.Text;
}
finally
{
document.Close();
}
}
using FREngine;
// Engine must already be initialized with sdkPath and licensePath
private void ConfigureLanguages(IEngine engine, string[] languageCodes)
{
// Create language parameters object
var langParams = engine.CreateLanguageParams();
// Add each language — string names must match installed data file names
// Missing data file causes runtime failure
foreach (var lang in languageCodes)
{
langParams.Languages.Add(lang); // e.g., "English", "French", "German"
}
// Language params are associated at the profile level, not per-document
// Changing languages requires reloading profile or reinitializing engine
engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
}
public string RecognizeFrenchDocument(IEngine engine, string imagePath)
{
var langParams = engine.CreateLanguageParams();
langParams.Languages.Add("French"); // Requires FrenchLanguage data files at runtime path
var document = engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
return document.PlainText.Text;
}
finally
{
document.Close();
}
}
Imports FREngine
' Engine must already be initialized with sdkPath and licensePath
Private Sub ConfigureLanguages(engine As IEngine, languageCodes As String())
' Create language parameters object
Dim langParams = engine.CreateLanguageParams()
' Add each language — string names must match installed data file names
' Missing data file causes runtime failure
For Each lang In languageCodes
langParams.Languages.Add(lang) ' e.g., "English", "French", "German"
Next
' Language params are associated at the profile level, not per-document
' Changing languages requires reloading profile or reinitializing engine
engine.LoadPredefinedProfile("DocumentConversion_Accuracy")
End Sub
Public Function RecognizeFrenchDocument(engine As IEngine, imagePath As String) As String
Dim langParams = engine.CreateLanguageParams()
langParams.Languages.Add("French") ' Requires FrenchLanguage data files at runtime path
Dim document = engine.CreateFRDocument()
Try
document.AddImageFile(imagePath, Nothing, Nothing)
document.Process(Nothing)
Return document.PlainText.Text
Finally
document.Close()
End Try
End Function
Approche IronOCR :
using IronOcr;
// Single language — install IronOcr.Languages.French via NuGet first
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.French;
var result = ocr.Read("french-document.jpg");
Console.WriteLine(result.Text);
// Multiple simultaneous languages — operator overload, no data file management
var multiOcr = new IronTesseract();
multiOcr.Language = OcrLanguage.French + OcrLanguage.German + OcrLanguage.English;
var multiResult = multiOcr.Read("multilingual-contract.jpg");
Console.WriteLine(multiResult.Text);
using IronOcr;
// Single language — install IronOcr.Languages.French via NuGet first
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.French;
var result = ocr.Read("french-document.jpg");
Console.WriteLine(result.Text);
// Multiple simultaneous languages — operator overload, no data file management
var multiOcr = new IronTesseract();
multiOcr.Language = OcrLanguage.French + OcrLanguage.German + OcrLanguage.English;
var multiResult = multiOcr.Read("multilingual-contract.jpg");
Console.WriteLine(multiResult.Text);
Imports IronOcr
' Single language — install IronOcr.Languages.French via NuGet first
Dim ocr As New IronTesseract()
ocr.Language = OcrLanguage.French
Dim result = ocr.Read("french-document.jpg")
Console.WriteLine(result.Text)
' Multiple simultaneous languages — operator overload, no data file management
Dim multiOcr As New IronTesseract()
multiOcr.Language = OcrLanguage.French + OcrLanguage.German + OcrLanguage.English
Dim multiResult = multiOcr.Read("multilingual-contract.jpg")
Console.WriteLine(multiResult.Text)
Les packs de langues s'installent comme des packages NuGet standard (dotnet add package IronOcr.Languages.French). Aucun fichier de données à déployer manuellement, aucune configuration de chemin, aucune réinitialisation du moteur lors du changement de langue. Le guide multilingue explique comment combiner les langues, et l' index des langues répertorie tous les plus de 125 packs disponibles.
Traitement TIFF multi-images
ABBYY traite les fichiers TIFF multipages en itérant sur les images et en ajoutant chaque image comme une page de document distincte. Le nombre d'images doit être récupéré à partir de l'objet TIFF, puis chaque image est ajoutée individuellement au conteneur du document.
Approche ABBYY FineReader :
using FREngine;
public string ProcessMultiFrameTiff(IEngine engine, string tiffPath)
{
var document = engine.CreateFRDocument();
try
{
// Must add each frame individually — no automatic multi-frame handling
// Page count requires reading the TIFF metadata before processing
var imageInfo = engine.CreateImageInfo();
imageInfo.LoadImageFile(tiffPath);
int frameCount = imageInfo.FrameCount;
for (int i = 0; i < frameCount; i++)
{
// Each frame added with its frame index via image processing params
var imgParams = engine.CreateImageProcessingParams();
imgParams.FrameIndex = i;
document.AddImageFile(tiffPath, imgParams, null);
}
document.Process(null);
return document.PlainText.Text;
}
finally
{
document.Close();
}
}
using FREngine;
public string ProcessMultiFrameTiff(IEngine engine, string tiffPath)
{
var document = engine.CreateFRDocument();
try
{
// Must add each frame individually — no automatic multi-frame handling
// Page count requires reading the TIFF metadata before processing
var imageInfo = engine.CreateImageInfo();
imageInfo.LoadImageFile(tiffPath);
int frameCount = imageInfo.FrameCount;
for (int i = 0; i < frameCount; i++)
{
// Each frame added with its frame index via image processing params
var imgParams = engine.CreateImageProcessingParams();
imgParams.FrameIndex = i;
document.AddImageFile(tiffPath, imgParams, null);
}
document.Process(null);
return document.PlainText.Text;
}
finally
{
document.Close();
}
}
Imports FREngine
Public Function ProcessMultiFrameTiff(engine As IEngine, tiffPath As String) As String
Dim document = engine.CreateFRDocument()
Try
' Must add each frame individually — no automatic multi-frame handling
' Page count requires reading the TIFF metadata before processing
Dim imageInfo = engine.CreateImageInfo()
imageInfo.LoadImageFile(tiffPath)
Dim frameCount As Integer = imageInfo.FrameCount
For i As Integer = 0 To frameCount - 1
' Each frame added with its frame index via image processing params
Dim imgParams = engine.CreateImageProcessingParams()
imgParams.FrameIndex = i
document.AddImageFile(tiffPath, imgParams, Nothing)
Next
document.Process(Nothing)
Return document.PlainText.Text
Finally
document.Close()
End Try
End Function
Approche IronOCR :
using IronOcr;
// LoadImageFrames handles multi-frame TIFF automatically
using var input = new OcrInput();
input.LoadImageFrames("scanned-batch.tiff");
var ocr = new IronTesseract();
var result = ocr.Read(input);
// Per-page results accessible directly
foreach (var page in result.Pages)
{
Console.WriteLine($"Frame {page.PageNumber}: {page.Text.Length} characters");
Console.WriteLine(page.Text);
}
using IronOcr;
// LoadImageFrames handles multi-frame TIFF automatically
using var input = new OcrInput();
input.LoadImageFrames("scanned-batch.tiff");
var ocr = new IronTesseract();
var result = ocr.Read(input);
// Per-page results accessible directly
foreach (var page in result.Pages)
{
Console.WriteLine($"Frame {page.PageNumber}: {page.Text.Length} characters");
Console.WriteLine(page.Text);
}
Imports IronOcr
' LoadImageFrames handles multi-frame TIFF automatically
Using input As New OcrInput()
input.LoadImageFrames("scanned-batch.tiff")
Dim ocr As New IronTesseract()
Dim result = ocr.Read(input)
' Per-page results accessible directly
For Each page In result.Pages
Console.WriteLine($"Frame {page.PageNumber}: {page.Text.Length} characters")
Console.WriteLine(page.Text)
Next
End Using
OcrInput.LoadImageFrames lit chaque trame dans un TIFF multi-page sans itération manuelle. Le résultat offre un accès par page via result.Pages, y compris le texte, les données de coordonnées et la confiance par trame. Le guide d'entrée TIFF couvre à la fois la gestion des fichiers TIFF multi-images et des GIF animés.
Traitement par lots parallèle
Le moteur basé sur COM d'ABBYY n'est pas sûr à appeler CreateFRDocument de façon concurrente à partir de plusieurs threads sans stratégie de synchronisation. Les processeurs de traitement par lots en production maintiennent généralement un pool d'instances de moteur ou sérialisent l'accès via un verrou. Chacune de ces approches ajoute une infrastructure IronOCR élimine.
Approche ABBYY FineReader :
using FREngine;
using System.Collections.Concurrent;
using System.Threading;
public class AbbyyBatchProcessor
{
// Pool required because engine is not safely concurrent
private readonly SemaphoreSlim _engineLock = new SemaphoreSlim(1, 1);
private IEngine _engine;
public async Task<Dictionary<string, string>> ProcessBatchAsync(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// Must serialize — one document at a time through single engine
foreach (var imagePath in imagePaths)
{
await _engineLock.WaitAsync();
try
{
var document = _engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
results[imagePath] = document.PlainText.Text;
}
finally
{
document.Close();
}
}
finally
{
_engineLock.Release();
}
}
return new Dictionary<string, string>(results);
}
}
using FREngine;
using System.Collections.Concurrent;
using System.Threading;
public class AbbyyBatchProcessor
{
// Pool required because engine is not safely concurrent
private readonly SemaphoreSlim _engineLock = new SemaphoreSlim(1, 1);
private IEngine _engine;
public async Task<Dictionary<string, string>> ProcessBatchAsync(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// Must serialize — one document at a time through single engine
foreach (var imagePath in imagePaths)
{
await _engineLock.WaitAsync();
try
{
var document = _engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
results[imagePath] = document.PlainText.Text;
}
finally
{
document.Close();
}
}
finally
{
_engineLock.Release();
}
}
return new Dictionary<string, string>(results);
}
}
Imports FREngine
Imports System.Collections.Concurrent
Imports System.Threading
Public Class AbbyyBatchProcessor
' Pool required because engine is not safely concurrent
Private ReadOnly _engineLock As New SemaphoreSlim(1, 1)
Private _engine As IEngine
Public Async Function ProcessBatchAsync(imagePaths As String()) As Task(Of Dictionary(Of String, String))
Dim results As New ConcurrentDictionary(Of String, String)()
' Must serialize — one document at a time through single engine
For Each imagePath In imagePaths
Await _engineLock.WaitAsync()
Try
Dim document = _engine.CreateFRDocument()
Try
document.AddImageFile(imagePath, Nothing, Nothing)
document.Process(Nothing)
results(imagePath) = document.PlainText.Text
Finally
document.Close()
End Try
Finally
_engineLock.Release()
End Try
Next
Return New Dictionary(Of String, String)(results)
End Function
End Class
Approche IronOCR :
using IronOcr;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class OcrBatchProcessor
{
public Dictionary<string, string> ProcessBatch(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// IronTesseract is thread-safe — one instance per thread, fully parallel
Parallel.ForEach(imagePaths, imagePath =>
{
var ocr = new IronTesseract(); // Each thread owns its instance
var result = ocr.Read(imagePath);
results[imagePath] = result.Text;
});
return new Dictionary<string, string>(results);
}
}
using IronOcr;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class OcrBatchProcessor
{
public Dictionary<string, string> ProcessBatch(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// IronTesseract is thread-safe — one instance per thread, fully parallel
Parallel.ForEach(imagePaths, imagePath =>
{
var ocr = new IronTesseract(); // Each thread owns its instance
var result = ocr.Read(imagePath);
results[imagePath] = result.Text;
});
return new Dictionary<string, string>(results);
}
}
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Threading.Tasks
Public Class OcrBatchProcessor
Public Function ProcessBatch(imagePaths As String()) As Dictionary(Of String, String)
Dim results = New ConcurrentDictionary(Of String, String)()
' IronTesseract is thread-safe — one instance per thread, fully parallel
Parallel.ForEach(imagePaths, Sub(imagePath)
Dim ocr = New IronTesseract() ' Each thread owns its instance
Dim result = ocr.Read(imagePath)
results(imagePath) = result.Text
End Sub)
Return New Dictionary(Of String, String)(results)
End Function
End Class
Chaque instance IronTesseract est indépendante. Parallel.ForEach sature les cœurs CPU disponibles sans aucun état partagé, verroux ou sérialisation. La version ABBYY traite les documents de manière séquentielle malgré le wrapper asynchrone ; La version IronOCR les traite véritablement en parallèle. L' exemple de multithreading illustre ce modèle par des comparaisons de temps d'exécution. Pour un contrôle du débit de niveau supérieur, consultez le guide d'optimisation de la vitesse .
Pipeline d'exportation de documents
ABBYY prend en charge plusieurs formats d'exportation via sa méthode Export avec des valeurs FileExportFormatEnum. L'exportation vers DOCX, RTF ou texte brut nécessite la création d'objets de paramètres d'exportation spécifiques au format, puis l'appel de document.Export avec la valeur enum appropriée et l'objet de paramètres.
Approche ABBYY FineReader :
using FREngine;
public class AbbyyExporter
{
private IEngine _engine;
public void ExportToMultipleFormats(string imagePath, string outputDir)
{
var document = _engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
string baseName = Path.GetFileNameWithoutExtension(imagePath);
// Export as plain text
document.Export(
Path.Combine(outputDir, baseName + ".txt"),
FileExportFormatEnum.FEF_TextUnicodeDefaults,
null
);
// Export as searchable PDF (requires PDF export params)
var pdfParams = _engine.CreatePDFExportParams();
pdfParams.Scenario = PDFExportScenarioEnum.PDES_Balanced;
pdfParams.UseOriginalPaperSize = true;
document.Export(
Path.Combine(outputDir, baseName + ".pdf"),
FileExportFormatEnum.FEF_PDF,
pdfParams
);
// Export as DOCX
var docxParams = _engine.CreateDOCXExportParams();
document.Export(
Path.Combine(outputDir, baseName + ".docx"),
FileExportFormatEnum.FEF_DOCX,
docxParams
);
}
finally
{
document.Close();
}
}
}
using FREngine;
public class AbbyyExporter
{
private IEngine _engine;
public void ExportToMultipleFormats(string imagePath, string outputDir)
{
var document = _engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
string baseName = Path.GetFileNameWithoutExtension(imagePath);
// Export as plain text
document.Export(
Path.Combine(outputDir, baseName + ".txt"),
FileExportFormatEnum.FEF_TextUnicodeDefaults,
null
);
// Export as searchable PDF (requires PDF export params)
var pdfParams = _engine.CreatePDFExportParams();
pdfParams.Scenario = PDFExportScenarioEnum.PDES_Balanced;
pdfParams.UseOriginalPaperSize = true;
document.Export(
Path.Combine(outputDir, baseName + ".pdf"),
FileExportFormatEnum.FEF_PDF,
pdfParams
);
// Export as DOCX
var docxParams = _engine.CreateDOCXExportParams();
document.Export(
Path.Combine(outputDir, baseName + ".docx"),
FileExportFormatEnum.FEF_DOCX,
docxParams
);
}
finally
{
document.Close();
}
}
}
Imports FREngine
Public Class AbbyyExporter
Private _engine As IEngine
Public Sub ExportToMultipleFormats(imagePath As String, outputDir As String)
Dim document = _engine.CreateFRDocument()
Try
document.AddImageFile(imagePath, Nothing, Nothing)
document.Process(Nothing)
Dim baseName As String = Path.GetFileNameWithoutExtension(imagePath)
' Export as plain text
document.Export(
Path.Combine(outputDir, baseName & ".txt"),
FileExportFormatEnum.FEF_TextUnicodeDefaults,
Nothing
)
' Export as searchable PDF (requires PDF export params)
Dim pdfParams = _engine.CreatePDFExportParams()
pdfParams.Scenario = PDFExportScenarioEnum.PDES_Balanced
pdfParams.UseOriginalPaperSize = True
document.Export(
Path.Combine(outputDir, baseName & ".pdf"),
FileExportFormatEnum.FEF_PDF,
pdfParams
)
' Export as DOCX
Dim docxParams = _engine.CreateDOCXExportParams()
document.Export(
Path.Combine(outputDir, baseName & ".docx"),
FileExportFormatEnum.FEF_DOCX,
docxParams
)
Finally
document.Close()
End Try
End Sub
End Class
Approche IronOCR :
using IronOcr;
public class OcrExporter
{
public void ExportToMultipleFormats(string imagePath, string outputDir)
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
string baseName = Path.GetFileNameWithoutExtension(imagePath);
// Plain text — direct property access
File.WriteAllText(
Path.Combine(outputDir, baseName + ".txt"),
result.Text
);
// Searchable PDF — one method call, no parameter objects
result.SaveAsSearchablePdf(
Path.Combine(outputDir, baseName + ".pdf")
);
// hOCR format — for document management systems
result.SaveAsHocrFile(
Path.Combine(outputDir, baseName + ".hocr")
);
}
}
using IronOcr;
public class OcrExporter
{
public void ExportToMultipleFormats(string imagePath, string outputDir)
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
string baseName = Path.GetFileNameWithoutExtension(imagePath);
// Plain text — direct property access
File.WriteAllText(
Path.Combine(outputDir, baseName + ".txt"),
result.Text
);
// Searchable PDF — one method call, no parameter objects
result.SaveAsSearchablePdf(
Path.Combine(outputDir, baseName + ".pdf")
);
// hOCR format — for document management systems
result.SaveAsHocrFile(
Path.Combine(outputDir, baseName + ".hocr")
);
}
}
Imports IronOcr
Imports System.IO
Public Class OcrExporter
Public Sub ExportToMultipleFormats(imagePath As String, outputDir As String)
Dim ocr As New IronTesseract()
Dim result = ocr.Read(imagePath)
Dim baseName As String = Path.GetFileNameWithoutExtension(imagePath)
' Plain text — direct property access
File.WriteAllText(
Path.Combine(outputDir, baseName & ".txt"),
result.Text
)
' Searchable PDF — one method call, no parameter objects
result.SaveAsSearchablePdf(
Path.Combine(outputDir, baseName & ".pdf")
)
' hOCR format — for document management systems
result.SaveAsHocrFile(
Path.Combine(outputDir, baseName & ".hocr")
)
End Sub
End Class
Le OcrResult d'IronOCR expose directement .Text et fournit des méthodes de sortie sans objets de paramètres ni enums de format. L'appel SaveAsSearchablePdf gère l'exportation PDF en une seule ligne contre la séquence de trois étapes de paramètres/exportation d'ABBYY. Le guide PDF consultable couvre les options de plage de pages et les paramètres de compression. Le guide d'exportation hOCR couvre le format HOCR pour les systèmes qui consomment une sortie OCR prenant en compte la position.
Référence de mappage de l'API ABBYY FineReader vers IronOCR
| Moteur ABBYY FineReader | Équivalent d'IronOCR |
|---|---|
new EngineLoader() |
Non requis |
loader.GetEngineObject(sdkPath, licensePath) |
new IronTesseract() |
engine.LoadPredefinedProfile("...") |
Non requis (traité en interne) |
engine.CreateLanguageParams() |
Non requis |
langParams.Languages.Add("French") |
ocr.Language = OcrLanguage.French |
langParams.Languages.Add("English") + langParams.Languages.Add("German") |
ocr.Language = OcrLanguage.English + OcrLanguage.German |
engine.CreateFRDocument() |
new OcrInput() |
engine.CreateFRDocumentFromImage(path, null) |
ocr.Read(path) |
document.AddImageFile(path, null, null) |
input.LoadImage(path) |
imageInfo.LoadImageFile(tiff) + boucle frameCount |
input.LoadImageFrames(tiff) |
engine.CreatePDFFile() puis pdfFile.Open(path, null, null) |
input.LoadPdf(path) |
document.Process(null) |
ocr.Read(input) |
document.PlainText.Text |
result.Text |
frDocument.Pages[i].PlainText.Text |
result.Pages[i].Text |
page.Layout.Blocks + contrôle BlockTypeEnum.BT_Table |
result.Pages + données de coordonnées de mots |
block.GetAsTableBlock() |
result.Pages[i].Lines (avec coordonnées) |
engine.CreatePDFExportParams() |
Non requis |
document.Export(path, FEF_PDF, params) |
result.SaveAsSearchablePdf(path) |
document.Export(path, FEF_TextUnicodeDefaults, null) |
File.WriteAllText(path, result.Text) |
engine.CreateDOCXExportParams() + Exportation |
Non directement pris en charge |
document.Close() |
Géré par using sur OcrInput |
_engine.GetLicenseInfo().ExpirationDate |
IronOcr.License.IsValidLicense |
Fichiers de licence (ABBYY.lic, ABBYY.key) |
IronOcr.License.LicenseKey = "key" |
engine.CreateZone() + zone.SetBounds(x, y, w, h) |
new CropRectangle(x, y, width, height) |
Problèmes de migration courants et solutions
Problème 1 : Erreurs d'enregistrement COM après la suppression du SDK
ABBYY : Après avoir retiré FREngine.dll des références du projet, la construction peut toujours échouer avec Could not load type 'FREngine.EngineLoader' ou des erreurs d'interopérabilité COM provenant des classes qui ont conservé l'ancien espace de noms.
Solution : Recherchez toutes les utilisations de FREngine et ABBYY.FineReader avant de retirer la référence. Toute classe qui implémente IDisposable spécifiquement pour annuler un champ IEngine a besoin de remplacer sa logique de disposition par des blocs using sur OcrInput :
// Before: explicit Close in finally
var document = _engine.CreateFRDocument();
try { document.Process(null); }
finally { document.Close(); }
// After: using pattern on OcrInput
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = new IronTesseract().Read(input);
// Before: explicit Close in finally
var document = _engine.CreateFRDocument();
try { document.Process(null); }
finally { document.Close(); }
// After: using pattern on OcrInput
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = new IronTesseract().Read(input);
Option Strict On
' Before: explicit Close in finally
Dim document = _engine.CreateFRDocument()
Try
document.Process(Nothing)
Finally
document.Close()
End Try
' After: using pattern on OcrInput
Using input As New OcrInput()
input.LoadImage(imagePath)
Dim result = New IronTesseract().Read(input)
End Using
Problème n° 2 : Le profil de reconnaissance n'a pas d'équivalent
ABBYY : Le code qui appelle engine.LoadPredefinedProfile("DocumentConversion_Speed") ou engine.LoadPredefinedProfile("FieldLevelRecognition") utilise des profils spécifiques à ABBYY pour équilibrer la précision contre le débit. Il n'existe pas de propriété équivalente dans IronOCR nommée Profile.
Solution : IronOCR expose les mêmes compromis via IronTesseract.Configuration. Pour optimiser la vitesse, définissez ocr.Configuration.TesseractVersion = TesseractVersion.Tesseract5 (défaut) et réduisez les filtres de prétraitement. Pour une précision maximale, ajoutez le pipeline de prétraitement complet :
// Speed-optimized
var ocr = new IronTesseract();
// Non preprocessing — fastest path
var result = ocr.Read("clean-document.jpg");
// Accuracy-optimized for difficult inputs
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("degraded-scan.jpg");
input.Deskew();
input.DeNoise();
input.Contrast();
input.Binarize();
var result = ocr.Read(input);
// Speed-optimized
var ocr = new IronTesseract();
// Non preprocessing — fastest path
var result = ocr.Read("clean-document.jpg");
// Accuracy-optimized for difficult inputs
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("degraded-scan.jpg");
input.Deskew();
input.DeNoise();
input.Contrast();
input.Binarize();
var result = ocr.Read(input);
Imports IronTesseract
' Speed-optimized
Dim ocr As New IronTesseract()
' Non preprocessing — fastest path
Dim result = ocr.Read("clean-document.jpg")
' Accuracy-optimized for difficult inputs
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadImage("degraded-scan.jpg")
input.Deskew()
input.DeNoise()
input.Contrast()
input.Binarize()
Dim result = ocr.Read(input)
End Using
Le guide de correction de la qualité d'image explique quels filtres permettent de résoudre quels problèmes de qualité d'image. Le guide d'optimisation de la vitesse décrit les propriétés de configuration qui réduisent le temps de traitement des documents propres.
Problème 3 : L'étape de déploiement du fichier de licence reste dans l'intégration continue/le déploiement continu (CI/CD).
ABBYY : Les pipelines de construction contiennent généralement une étape qui copie ABBYY.lic et ABBYY.key d'un magasin sécurisé vers la cible de déploiement. Après la migration, il arrive que les équipes oublient de supprimer cette étape, laissant ainsi du code de déploiement inactif qui référence des chemins d'accès qui n'existent plus.
Solution : Supprimez complètement l'étape de copie du fichier de licence. Remplacez-la par une étape d'injection de variable d'environnement :
# Remove these CI/CD steps after migration:
# - name: Copy ABBYY license files
# run: |
# cp $SECRETS_PATH/ABBYY.lic $DEPLOY_PATH/License/
# cp $SECRETS_PATH/ABBYY.key $DEPLOY_PATH/License/
# Add this instead (environment variable injection):
# - name: Set IronOCR license
# env:
# IRONOCR_LICENSE_KEY: ${{secrets.IRONOCR_LICENSE}}
# Remove these CI/CD steps after migration:
# - name: Copy ABBYY license files
# run: |
# cp $SECRETS_PATH/ABBYY.lic $DEPLOY_PATH/License/
# cp $SECRETS_PATH/ABBYY.key $DEPLOY_PATH/License/
# Add this instead (environment variable injection):
# - name: Set IronOCR license
# env:
# IRONOCR_LICENSE_KEY: ${{secrets.IRONOCR_LICENSE}}
Et au démarrage de l'application :
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
?? throw new InvalidOperationException("IRONOCR_LICENSE_KEY not set");
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
?? throw new InvalidOperationException("IRONOCR_LICENSE_KEY not set");
Imports System
IronOcr.License.LicenseKey = If(Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY"), Throw New InvalidOperationException("IRONOCR_LICENSE_KEY not set"))
Problème 4 : Moteur non sécurisé pour les threads — Code de verrouillage existant
ABBYY : Les applications qui appellent ABBYY depuis plusieurs threads contiennent généralement SemaphoreSlim, des instructions lock, ou des instances de moteur locales au thread pour éviter les problèmes de threading COM. Ce code de synchronisation est spécifique au modèle de threads d'ABBYY.
Solution : Supprimez tout le code de synchronisation encapsulant les appels ABBYY. Le IronTesseract d'IronOCR est sûr à instancier par thread :
// Remove all of this:
// private readonly SemaphoreSlim _engineLock = new SemaphoreSlim(1, 1);
// await _engineLock.WaitAsync();
// try { ... } finally { _engineLock.Release(); }
// Replace with:
Parallel.ForEach(documents, doc =>
{
var ocr = new IronTesseract(); // One per thread — no lock needed
results[doc.Id] = ocr.Read(doc.Path).Text;
});
// Remove all of this:
// private readonly SemaphoreSlim _engineLock = new SemaphoreSlim(1, 1);
// await _engineLock.WaitAsync();
// try { ... } finally { _engineLock.Release(); }
// Replace with:
Parallel.ForEach(documents, doc =>
{
var ocr = new IronTesseract(); // One per thread — no lock needed
results[doc.Id] = ocr.Read(doc.Path).Text;
});
Imports System.Threading.Tasks
Parallel.ForEach(documents, Sub(doc)
Dim ocr = New IronTesseract() ' One per thread — no lock needed
results(doc.Id) = ocr.Read(doc.Path).Text
End Sub)
Problème 5 : Modèle CreateImageInfo / FrameCount pour TIFF
ABBYY : Le code qui lit les nombres de trames à partir de fichiers TIFF en utilisant engine.CreateImageInfo() et imageInfo.LoadImageFile() avant de boucler à travers les trames n'a pas d'équivalent direct dans IronOCR car OcrInput.LoadImageFrames gère l'énumération des trames en interne.
Solution : Supprimer complètement la boucle de comptage d'images :
// Remove:
// var imageInfo = engine.CreateImageInfo();
// imageInfo.LoadImageFile(tiffPath);
// for (int i = 0; i < imageInfo.FrameCount; i++) { document.AddImageFile(...) }
// Replace with:
using var input = new OcrInput();
input.LoadImageFrames("multi-page-scan.tiff");
var result = new IronTesseract().Read(input);
// result.Pages contains one entry per TIFF frame
// Remove:
// var imageInfo = engine.CreateImageInfo();
// imageInfo.LoadImageFile(tiffPath);
// for (int i = 0; i < imageInfo.FrameCount; i++) { document.AddImageFile(...) }
// Replace with:
using var input = new OcrInput();
input.LoadImageFrames("multi-page-scan.tiff");
var result = new IronTesseract().Read(input);
// result.Pages contains one entry per TIFF frame
Imports IronOcr
' Remove:
' Dim imageInfo = engine.CreateImageInfo()
' imageInfo.LoadImageFile(tiffPath)
' For i As Integer = 0 To imageInfo.FrameCount - 1
' document.AddImageFile(...)
' Next
' Replace with:
Using input As New OcrInput()
input.LoadImageFrames("multi-page-scan.tiff")
Dim result = New IronTesseract().Read(input)
' result.Pages contains one entry per TIFF frame
End Using
Problème n°6 : L'exportation DOCX n'a pas d'équivalent direct
ABBYY : document.Export(path, FileExportFormatEnum.FEF_DOCX, docxParams) produit un document Word. IronOCR ne produit pas directement de fichiers DOCX.
Solution : IronOCR produit des PDF consultables et des données textuelles structurées. Pour les flux de travail nécessitant une sortie DOCX, la voie de migration pratique consiste à produire un PDF consultable et à le convertir en aval, ou à extraire le texte structuré et à l'écrire dans un fichier DOCX à l'aide d'une bibliothèque telle que Open XML SDK :
// IronOCR to searchable PDF (closest equivalent)
var result = new IronTesseract().Read(inputPath);
result.SaveAsSearchablePdf(outputPath.Replace(".docx", ".pdf"));
// Or extract structured text for downstream DOCX generation
foreach (var paragraph in result.Paragraphs)
{
Console.WriteLine(paragraph.Text);
// Write to DOCX via Open XML SDK or similar
}
// IronOCR to searchable PDF (closest equivalent)
var result = new IronTesseract().Read(inputPath);
result.SaveAsSearchablePdf(outputPath.Replace(".docx", ".pdf"));
// Or extract structured text for downstream DOCX generation
foreach (var paragraph in result.Paragraphs)
{
Console.WriteLine(paragraph.Text);
// Write to DOCX via Open XML SDK or similar
}
Imports IronOcr
' IronOCR to searchable PDF (closest equivalent)
Dim result = New IronTesseract().Read(inputPath)
result.SaveAsSearchablePdf(outputPath.Replace(".docx", ".pdf"))
' Or extract structured text for downstream DOCX generation
For Each paragraph In result.Paragraphs
Console.WriteLine(paragraph.Text)
' Write to DOCX via Open XML SDK or similar
Next
Le guide des résultats de lecture explique comment accéder aux paragraphes, aux lignes, aux mots et aux données de coordonnées au niveau des caractères pour un traitement ultérieur.
Liste de vérification pour la migration d'ABBYY FineReader
Tâches préalables à la migration
Vérifiez le code source avant d'apporter toute modification :
# Find all files using ABBYY namespaces
grep -r "using FREngine" --include="*.cs" .
grep -r "using ABBYY" --include="*.cs" .
# Find engine lifecycle patterns
grep -r "EngineLoader\|GetEngineObject\|LoadPredefinedProfile" --include="*.cs" .
# Find document lifecycle patterns
grep -r "CreateFRDocument\|document\.Close\|AddImageFile\|document\.Process" --include="*.cs" .
# Find language configuration
grep -r "CreateLanguageParams\|langParams\.Languages" --include="*.cs" .
# Find export calls
grep -r "FileExportFormatEnum\|CreatePDFExportParams\|document\.Export" --include="*.cs" .
# Find license file references in CI/CD and deployment scripts
grep -r "ABBYY\.lic\|ABBYY\.key" .
# Find all files using ABBYY namespaces
grep -r "using FREngine" --include="*.cs" .
grep -r "using ABBYY" --include="*.cs" .
# Find engine lifecycle patterns
grep -r "EngineLoader\|GetEngineObject\|LoadPredefinedProfile" --include="*.cs" .
# Find document lifecycle patterns
grep -r "CreateFRDocument\|document\.Close\|AddImageFile\|document\.Process" --include="*.cs" .
# Find language configuration
grep -r "CreateLanguageParams\|langParams\.Languages" --include="*.cs" .
# Find export calls
grep -r "FileExportFormatEnum\|CreatePDFExportParams\|document\.Export" --include="*.cs" .
# Find license file references in CI/CD and deployment scripts
grep -r "ABBYY\.lic\|ABBYY\.key" .
Documentez chaque classe qui contient un champ IEngine ou IFRDocument. Notez les formats d'exportation utilisés : la sortie DOCX nécessite une approche alternative (voir le problème 6 ci-dessus).
Tâches de mise à jour du code
- Retirez la référence
FREngine.dllde tous les fichiers.csproj - Exécutez
dotnet add package IronOcrdans chaque projet qui utilisait ABBYY - Ajoutez
IronOcr.License.LicenseKey = ...au démarrage de l'application (Program.csou classe de démarrage) - Installez les packages de langue NuGet pour chaque langue non anglaise (
dotnet add package IronOcr.Languages.French, etc.) - Supprimez tous les appels
EngineLoader,GetEngineObject, etLoadPredefinedProfile - Supprimez tous les appels
CreateLanguageParamsetlangParams.Languages.Add - Remplacez
engine.CreateFRDocument()+document.AddImageFile()+document.Process()parnew IronTesseract().Read(path) - Remplacez les boucles TIFF multi-trames par
input.LoadImageFrames(tiffPath) - Remplacez
document.PlainText.Textparresult.Text - Remplacez
frDocument.Pages[i].PlainText.Textparresult.Pages[i].Text - Remplacez
document.Export(..., FEF_PDF, pdfParams)parresult.SaveAsSearchablePdf(path) - Remplacez tous les appels
document.Close()par des blocsusingsurOcrInput - Supprimez
SemaphoreSlimet le code de verrouillage qui sérialisait l'accès au moteur ABBYY - Remplacez
engine.CreateZone()/zone.SetBounds()/page.Zones.Add()parnew CropRectangle(x, y, width, height)passé àinput.LoadImage() - Supprimer les étapes de copie des fichiers de licence des pipelines CI/CD
- Mettez à jour les images Docker — supprimez la couche d'installation SDK, ajoutez
libgdipluspour les cibles Linux
Test de post-migration
- Vérifier le résultat de l'extraction de texte sur un échantillon représentatif de chaque type de document (factures, contrats, formulaires numérisés).
- Vérifier que le traitement TIFF multipage renvoie le même nombre de pages que les images produites par ABBYY
- Tester les documents multilingues avec les mêmes données d'entrée que celles utilisées pour la comparaison de référence ABBYY.
- Vérifier que le fichier PDF interrogeable est bien interrogeable dans Adobe Reader et les visionneuses PDF des navigateurs.
- Exécutez le processeur de traitement par lots parallèle avec le niveau de concurrence de production et vérifiez qu'aucune exception n'est constatée.
- Vérifiez
result.Confidencesur des documents de bonne qualité connus pour établir un seuil de référence pour les portes de qualité - Tester l'initialisation de la clé de licence à partir d'une variable d'environnement dans l'environnement de déploiement de préproduction
- Vérifier que l'image Docker se construit et exécute l'OCR sans le montage de volume du SDK ABBYY
- Vérifier que le pipeline CI/CD s'exécute correctement sans l'étape de copie du fichier de licence.
- Exécutez un profileur de mémoire sur le processeur en lot pour confirmer qu'aucun objet
OcrInputn'est en fuite (vérifiez le placementusing)
Principaux avantages de la migration vers IronOCR
La complexité du déploiement est considérablement réduite. Chaque déploiement ABBYY nécessitait l'installation du SDK, le placement du fichier de licence, la configuration du chemin d'exécution et la vérification que les fichiers se trouvaient aux bons emplacements avant le démarrage de l'application. IronOCR se déploie en tant que dépendance NuGet . dotnet publish produit un artefact autonome avec le moteur OCR inclus. Le guide de déploiement Docker et le guide d'installation Azure présentent la configuration complète ; les deux tiennent sur une seule page.
L'interopérabilité COM a disparu. Le retrait de la couche COM élimine une catégorie entière de défaillances au moment de l'exécution : erreurs d'enregistrement COM sur les nouvelles machines, incompatibilités de threading par appartement, bogues de cycle de vie RCW, et les 15 à 25 lignes de boilerplate try/finally que chaque appel de traitement de document ABBYY nécessitait. La base de code se réduit. La surface d'erreur se réduit en conséquence.
L'augmentation du volume de documents ne justifie plus de révision budgétaire. La licence perpétuelle d'IronOCR couvre un volume de documents illimité. Une application traitant 10 000 documents par mois la première année et 2 000 000 par mois la troisième année conserve le même coût de licence OCR. Il n'y a ni facturation au nombre de pages traitées, ni facturation supplémentaire, ni renégociation des paliers de volume. La page des licences affiche tous les niveaux — la licence Professional à 2 999 $ couvre dix développeurs traitant n'importe quel volume sur n'importe quel nombre de cibles de déploiement.
Le déploiement multiplateforme ouvre de nouvelles perspectives en matière d'infrastructure. La couche COM d'ABBYY nécessite Windows. Les équipes qui souhaitaient transférer le traitement des documents vers des conteneurs Linux pour des raisons de coût ou de densité ont été bloquées. IronOCR fonctionne de manière identique sous Windows, Linux et macOS à partir du même package NuGet . La migration depuis ABBYY supprime la contrainte Windows de la couche OCR de la pile applicative. Le guide de déploiement Linux et le guide de déploiement AWS couvrent la configuration complète de chaque environnement.
Le débit parallèle est disponible sans travaux d'infrastructure. Les stratégies de verrouillage qui sérialisaient l'accès au moteur ABBYY ont disparu. Les instances IronTesseract sont indépendantes : lancez-en une par thread, exécutez Parallel.ForEach sur un lot de documents, obtenez les résultats. Le débit s'adapte au nombre de cœurs de processeur disponibles sans aucun code supplémentaire. L' exemple de multithreading démontre les améliorations du temps d'exécution sur un matériel multicœur.
La configuration linguistique est une référence de package. L'ajout de la prise en charge de la reconnaissance optique de caractères (OCR) en allemand ou en japonais à une intégration ABBYY impliquait d'identifier les fichiers de données, de les déployer dans les répertoires d'exécution sur chaque machine cible et de gérer les erreurs en cas de fichiers manquants. Avec IronOCR, dotnet add package IronOcr.Languages.German ajoute le pack de langue comme une dépendance NuGet versionnée et reproductible. Le Package Manager garantit que les données sont présentes dans chaque compilation. Le guide des packs de langue personnalisés couvre la formation et le déploiement de modèles de langue personnalisés pour des domaines spécialisés.
Questions Fréquemment Posées
Pourquoi devrais-je migrer d'ABBYY FineReader Engine 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 d'ABBYY FineReader Engine vers IronOcr ?
Remplacer les séquences d'initialisation d'ABBYY FineReader 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 qu'ABBYY FineReader Engine pour les documents professionnels 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 qu'ABBYY FineReader Engine 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 d'ABBYY FineReader Engine vers IronOCR nécessite-t-elle des modifications de l'infrastructure de déploiement ?
IronOCR nécessite moins de changements d'infrastructure qu'ABBYY FineReader Engine. 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 OCR complet et la clé de licence est une chaîne 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 qu'ABBYY FineReader ?
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 d'ABBYY FineReader Engine 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 d'ABBYY FineReader Engine 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.

