Migracja z ABBYY FineReader do IronOCR
Ten przewodnik przeprowadza programistów .NET przez każdy etap zastępowania Silnik ABBYY FineReader SDK przez IronOCR. Obejmuje on mechaniczne kroki usuwania zależności COM i artefaktów instalatora SDK, mapuje API ABBYY na odpowiedniki IronOCR oraz zawiera przykłady kodu "przed" i "po" dla wzorców najczęściej spotykanych w produkcyjnych integracjach ABBYY. Migracja jest skierowana do zespołów, które uznały, że koszty Enterprise i złożoność wdrożenia ABBYY już nie odpowiadają ich wymaganiom projektowym.
Dlaczego warto przejść z ABBYY FineReader
ABBYY FineReader Engine to wydajna platforma OCR, ale jej architektura została zaprojektowana z myślą o korporacyjnych środowiskach Windows z dedykowanymi zespołami infrastrukturalnymi. Gdy rzeczywistym obciążeniem zespołu .NET jest przetwarzanie faktur, digitalizacja umów lub ekstrakcja zeskanowanych formularzy, architektura ta staje się odpowiedzialnością, a nie atutem.
Zadłużenie związane z interoperacyjnością COM narasta z czasem. Każda integracja ABBYY w środowisku .NET przebiega przez warstwę interoperacyjności COM. Obiekty COM wymagają jawnego zarządzania cyklem życia: tworzenie, inicjowanie, przetwarzanie, a następnie zamknięcie w bloku finally, w przeciwnym razie proces powoduje wyciek pamięci. Każda ścieżka kodu, która ma związek z ABBYY, charakteryzuje się tym schematem. W ciągu dwóch lub trzech lat dodawania nowych funkcji ten cykl życia rozprzestrzenia się na klasy usług, procesy działające w tle i moduły obsługi żądań. Rezultatem jest 30-50% powtarzalnego kodu w każdej klasie związanej z OCR, który całkowicie znika, gdy przełączysz się na IronTesseract.
Instalator SDK blokuje nowoczesne wzorce wdrażania. Firma ABBYY wdraża oprogramowanie za pomocą instalatora Windows SDK, który umieszcza pliki binarne, dane językowe, pliki uruchomieniowe i pliki licencyjne w stałych ścieżkach. Konteneryzowanie usługi korzystającej z ABBYY wymaga albo utworzenia niestandardowego obrazu bazowego o rozmiarze ponad 300 MB z wyników instalatora, albo montowania woluminów z plikami licencyjnymi przy uruchamianiu. Żadne z tych podejść nie pasuje do standardowej linii produkcyjnej Kubernetes ani architektury cloud-native.IronOCR jest pakietem NuGet: ten sam dotnet restore, który pobiera każde inne zależności, pobiera pełny silnik OCR.
Licencjonowanie na stronę zamienia wolumen w źródło kosztów. Modele licencjonowania ABBYY oparte na wolumenie pobierają opłaty za każdą stronę przetworzoną powyżej określonych progów. W przypadku aplikacji, która w momencie uruchomienia przetwarza 50 000 dokumentów miesięcznie, a dwa lata później osiąga poziom 500 000, koszty OCR rosną wprost proporcjonalnie do jej sukcesu.IronOCR pobiera stałą opłatę za licencję — zespół przetwarzający dwa miliony stron miesięcznie płaci dokładnie tyle samo za licencję, co zespół przetwarzający dwa tysiące.
Dane językowe wymagają ręcznej koordynacji wdrożenia. Pakiety językowe ABBYY znajdują się w postaci plików w katalogu uruchomieniowym SDK. Dodanie języka oznacza zidentyfikowanie odpowiednich plików danych, skopiowanie ich do właściwej ścieżki w każdym środowisku docelowym oraz aktualizację skryptów CI/CD w celu uwzględnienia ich. W IronOCR dodanie języka francuskiego to dotnet add package IronOcr.Languages.French — resztę obsługuje menedżer pakietów.
Błędy pliku licencji pojawiają się w produkcji bez ostrzeżenia. Licencje ABBYY istnieją jako pliki .lic i .key, które muszą być obecne w określonych ścieżkach dyskowych, gdy działa loader.GetEngineObject(). Jeśli tych plików brakuje na nowym serwerze produkcyjnym — z powodu nieprawidłowego skryptu wdrożeniowego, nieudanego kopiowania plików lub problemu z uprawnieniami — podczas uruchamiania pojawia się błąd. W ten sam sposób kończy się nieudanie w przypadku wygasłej licencji. Licencjonowanie dla IronOCR to klucz tekstowy przypisywany w kodzie uruchomieniowym, który można przechowywać w dowolnym menedżerze tajemnic, z walidacją sprawdzaną przez IronOcr.License.IsValidLicense przed akceptowaniem ruchu przez aplikację.
Bezpieczeństwo wątków wymaga pojedynczej współdzielonej instancji silnika. Silnik ABBYY nie jest trywialnie bezpieczny dla wątków przy równoczesnych wywołaniach CreateFRDocument z wielu wątków. Wdrożenia produkcyjne wykorzystują strategie blokowania lub pule procesorów.IronOCR jest bezstanowy: uruchom jedna instancje na watek, uruchamiaj rozpoznawanie rownolegle bez zamkow, wyrzuc po zakonczeniu.
Podstawowy problem
// 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()
IronOCR a ABBYY FineReader: porównanie funkcji
Poniższa tabela przedstawia możliwości istotne dla zespołów oceniających tę migrację.
| Funkcja | Silnik ABBYY FineReader | IronOCR |
|---|---|---|
| Instalacja | Instalator SDK (Windows) | dotnet add package IronOcr |
| Pozyskanie | Skontaktuj się z działem sprzedaży (4–12 tygodni) | NuGet w trybie samoobsługowym |
| Model licencyjny | Enterprise, na serwer lub na stronę | Wieczysta, $999-$2,999 jednorazowo |
| Zarządzanie licencjami | .lic + .key pliki na dysku |
Klucz ciągu znaków w kodzie lub zmiennej środowiskowej |
| Integracja z platformą .NET | Współpraca z COM | Natywny .NET |
| Zależność COM | Tak | Nie |
| Bezpieczeństwo wątków | Wymagana strategia blokowania | Pełny (jeden IronTesseract na wątek) |
| Obsługiwane języki | 190+ | 125+ |
| Instalacja języka | Pliki danych środowiska uruchomieniowego w ścieżce SDK | Pakiety językowe NuGet |
| Plik wejściowy PDF | Tak (przez CreatePDFFile) |
Tak (natywny, input.LoadPdf()) |
| Wyjście w formacie PDF z możliwością wyszukiwania | Tak (potok eksportu) | Tak (result.SaveAsSearchablePdf()) |
| Automatyczne przetwarzanie wstępne | Oparte na profilu | Wbudowane (Deskew, DeNoise, Contrast, Binarize, Sharpen) |
| OCR oparte na regionie | Obiekty stref (CreateZone, SetBounds) |
CropRectangle parametr |
| Odczytywanie BarCode | Tak | Tak (ocr.Configuration.ReadBarCodes = true) |
| Wielopłatformowe | Windows, Linux, macOS | Windows, Linux, macOS, Docker, Azure, AWS |
| Wdrażanie Docker | Wymagany niestandardowy obraz bazowy | Standardowy obraz bazowy .NET + libgdiplus |
| Ocena pewności | Tak | Tak (result.Confidence) |
| Czas do uzyskania pierwszego wyniku OCR | 4–12 tygodni (zamówienie) | Tego samego dnia |
Szybki start: Migracja z ABBYY FineReader do IronOCR
Krok 1: Zastąp pakiet NuGet
Silnik ABBYY FineReader nie posiada pakietu NuGet. Usuń go, odinstalowując SDK i usuwając ręczne odwołanie do zestawu z pliku projektu:
<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>
Nastepnie usun odniesienie FREngine.dll Współpraca z COM z wezla References w Visual Studio lub bezposrednio usun odpowiedni wpis z pliku projektu. Zainstaluj IronOCR z NuGet:
dotnet add package IronOcr
Krok 2: Aktualizacja przestrzeni nazw
// Before (ABBYY)
using FREngine;
using ABBYY.FineReader;
// After (IronOCR)
using IronOcr;
// Before (ABBYY)
using FREngine;
using ABBYY.FineReader;
// After (IronOCR)
using IronOcr;
Imports IronOcr
Krok 3: Inicjalizacja licencji
Dodaj to raz podczas uruchamiania aplikacji, przed jakimikolwiek wywołaniami OCR:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
W przypadku wdrożeń produkcyjnych klucz należy zapisać w zmiennej środowiskowej lub menedżerze sekretów:
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")
Przykłady migracji kodu
Cykl życia silnika w usłudze Windows a bezstanowy IronTesseract
Ceremonia inicjalizacji silnika ABBYY powinna znajdować się w otoczce serwisowej, ponieważ obiekty EngineLoader i IEngine są kosztowne w tworzeniu. Większość integracji produkcyjnych opakowuje silnik w usługę singletonową z wyraźnymi metodami uruchamiania i zamykania.
Podejście ABBYY FineReader:
using FREngine;
public class DocumentOcrService : IHostedService, IDisposable
{
private IEngine _engine;
public Task StartAsync(CancellationToken cancellationToken)
{
// Step 1: Create loader — requires Współpraca z 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 Współpraca z 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 Współpraca z 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
Podejście IronOCR:
using IronOcr;
public class DocumentOcrService
{
// Nie 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
{
// Nie 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
' Nie 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 nie ma cyklu życia silnika. Inicjuje się wewnętrznie przy pierwszym użyciu i nie wymaga wyraźnego wyłączenia. Obudowa usługi hostowanej, pole IEngine oraz metody StopAsync znikają. Jesli aplikacja przetwarza dokumenty rownoczesnie, kazdy watek tworzy wlasna instancje IronTesseract — blokowanie nie jest wymagane. Przewodnik konfiguracji IronTesseract obejmuje opcje konfiguracji, w tym właściwości TesseractVersion i Configuration.
Konfiguracja języka rozpoznawania
Konfiguracja jezykowa ABBYY obejmuje utworzenie obiektu LanguageParams, dodanie nazw jezykow, ktore musza zgadzac sie z zainstalowanymi plikami danych, i powiazanie tych parametrow z silnikiem przed przetworzeniem jakiegokolwiek dokumentu. Każdy dodatkowy język wymaga odpowiednich plików danych wdrożonych w ścieżce uruchomieniowej.
Podejście 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
Podejście 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)
Paczki jezykowe instaluja sie jako standardowe pakiety NuGet (dotnet add package IronOcr.Languages.French). Nie ma plików danych do ręcznego wdrażania, nie ma konfiguracji ścieżek, nie ma ponownej inicjalizacji silnika przy zmianie języków. Przewodnik po wielu językach obejmuje łączenie języków, a indeks języków zawiera listę wszystkich ponad 125 dostępnych pakietów.
Przetwarzanie plików TIFF z wieloma ramkami
ABBYY przetwarza wielostronicowe pliki TIFF, iterując klatki i dodając każdą klatkę jako oddzielną stronę dokumentu. Liczbę klatek należy pobrać z obiektu TIFF, a następnie każdą klatkę dodawać indywidualnie do kontenera dokumentu.
Podejście 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
Podejście 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 odczytuje każdą klatkę w wielostronicowym pliku TIFF bez ręcznej iteracji. Wynik umożliwia dostęp do poszczególnych stron za pomocą result.Pages, w tym tekstu, danych o współrzędnych oraz pewności dla każdej klatki. Przewodnik dotyczący plików wejściowych TIFF obejmuje zarówno obsługę wielo-ramkowych plików TIFF, jak i animowanych plików GIF.
Równoległe przetwarzanie wsadowe
Silnik oparty na COM firmy ABBYY nie jest bezpieczny do jednoczesnego wywoływania CreateFRDocument z wielu wątków bez zastosowania strategii synchronizacji. Procesory przetwarzania wsadowego zazwyczaj utrzymują pulę instancji silnika lub serializują dostęp za pomocą blokady. Każde z tych rozwiązań wymaga infrastruktury, którą eliminuje IronOCR.
Podejście 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
Podejście 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
Kazda instancja IronTesseract jest niezalezna. Parallel.ForEach wykorzystuje dostępne rdzenie CPU bez wspólnego stanu, blokad lub serializacji. Wersja ABBYY przetwarza dokumenty sekwencyjnie pomimo asynchronicznej nakładki; Wersja IronOCR przetwarza je w sposób prawdziwie równoległy. Przykład wielowątkowości ilustruje ten wzorzec za pomocą porównań czasowych. Aby uzyskać informacje na temat kontroli przepustowości na wyższym poziomie, zapoznaj się z przewodnikiem po optymalizacji prędkości.
Potok eksportu dokumentów
ABBYY obsługuje wiele formatów eksportu poprzez metodę Export z wartościami FileExportFormatEnum. Eksportowanie do formatu DOCX, RTF lub zwykłego tekstu wymaga utworzenia obiektów parametrów eksportu specyficznych dla formatu, a następnie wywołania document.Export z odpowiednią wartością enum i obiektem parametrów.
Podejście 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
Podejście 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
OcrResultIronOCR ujawnia .Text bezposrednio i dostarcza metody wyjsciowe bez obiektow parametrow lub wyliczen formatow. Wywolanie SaveAsSearchablePdf obsluguje eksport PDF w jednej linii w porownaniu do trzyetapowej sekwencji parametrow/eksportu w ABBYY. Przewodnik w formacie PDF z funkcją wyszukiwania obejmuje opcje zakresu stron i ustawienia kompresji. Przewodnik eksportu hOCR obejmuje format HOCR dla systemów wykorzystujących dane wyjściowe OCR z uwzględnieniem pozycji.
Dokumentacja API ABBYY FineReader do IronOCR
| Silnik ABBYY FineReader | Odpowiednik IronOCR |
|---|---|
new EngineLoader() |
Nie jest wymagane |
loader.GetEngineObject(sdkPath, licensePath) |
new IronTesseract() |
engine.LoadPredefinedProfile("...") |
Nie jest wymagane (obsługiwane wewnętrznie) |
engine.CreateLanguageParams() |
Nie jest wymagane |
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) + frameCount petla |
input.LoadImageFrames(tiff) |
engine.CreatePDFFile() potem 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 + BlockTypeEnum.BT_Table sprawdzenie |
result.Pages + dane współrzędnych słów |
block.GetAsTableBlock() |
result.Pages[i].Lines (ze współrzędnymi) |
engine.CreatePDFExportParams() |
Nie jest wymagane |
document.Export(path, FEF_PDF, params) |
result.SaveAsSearchablePdf(path) |
document.Export(path, FEF_TextUnicodeDefaults, null) |
File.WriteAllText(path, result.Text) |
engine.CreateDOCXExportParams() + Eksportować |
Nieobsługiwane bezpośrednio |
document.Close() |
Obslugiwana przez using na OcrInput |
_engine.GetLicenseInfo().ExpirationDate |
IronOcr.License.IsValidLicense |
Pliki licencyjne (ABBYY.lic, ABBYY.key) |
IronOcr.License.LicenseKey = "key" |
engine.CreateZone() + zone.SetBounds(x, y, w, h) |
new CropRectangle(x, y, width, height) |
Typowe problemy związane z migracją i ich rozwiązania
Problem 1: Błędy rejestracji COM po usunięciu SDK
ABBYY: Po usunięciu FREngine.dll z referencji projektu, kompilacja może nadal się nie powieść z Could not load type 'FREngine.EngineLoader' lub błędami interop COM z klas, które zachowały starą przestrzeń nazw.
Rozwiązanie: Wyszukaj wszystkie użycia FREngine i ABBYY.FineReader przed usunięciem odniesienia. Jakakolwiek klasa, która implementuje IDisposable w celu wyzerowania pola IEngine, wymaga zamiany logiki usuwania na bloki using na 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
Problem 2: Profil rozpoznawania nie ma odpowiednika
ABBYY: Kod, który wywołuje engine.LoadPredefinedProfile("DocumentConversion_Speed") lub engine.LoadPredefinedProfile("FieldLevelRecognition"), wykorzystuje profile specyficzne dla ABBYY, aby zrównoważyć dokładność z przepustowością. Nie ma w IronOCR odpowiedniej właściwości o nazwie Profile.
Rozwiązanie:IronOCR ujawnia te same kompromisy poprzez IronTesseract.Configuration. Aby zoptymalizować szybkość, ustaw ocr.Configuration.TesseractVersion = TesseractVersion.Tesseract5 (domyślnie) i zmniejsz filtry wstępnego przetwarzania. Aby uzyskać maksymalną dokładność, dodaj pełny proces przetwarzania wstępnego:
// Speed-optimized
var ocr = new IronTesseract();
// Nie 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();
// Nie 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()
' Nie 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
Przewodnik po korekcji jakości obrazu wyjaśnia, które filtry rozwiązują poszczególne problemy związane z jakością danych wejściowych. Przewodnik po optymalizacji szybkości obejmuje właściwości konfiguracyjne, które skracają czas przetwarzania czystych dokumentów.
Problem 3: Etap wdrażania pliku licencji pozostaje w CI/CD
ABBYY: Pipeline budowania zazwyczaj zawiera krok, który kopiuje ABBYY.lic oraz ABBYY.key z bezpiecznego magazynu do miejsca docelowego wdrożenia. Po migracji zespoły czasami zapominają o usunięciu tego kroku, pozostawiając nieaktywny kod wdrożeniowy, który odwołuje się do ścieżek, które już nie istnieją.
Rozwiązanie: Całkowicie usunąć krok dotyczący kopiowania pliku licencji. Zastąp to etapem wstrzykiwania zmiennej środowiskowej:
# 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}}
A podczas uruchamiania aplikacji:
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"))
Problem 4: Silnik nie jest bezpieczny dla wątków — istniejący kod blokujący
ABBYY: Aplikacje, które wywołują ABBYY z wielu wątków, zazwyczaj zawierają instrukcje SemaphoreSlim, lock lub instancje silnika lokalne dla wątku, aby uniknąć problemów z wątkowością COM. Ten kod synchronizacji jest specyficzny dla modelu wątków firmy ABBYY.
Rozwiązanie: Usuń cały kod synchronizacji otaczający wywołania ABBYY. IronTesseract z IronOCR jest bezpieczny do inicjalizacji na wątek.
// 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)
Problem 5: CreateImageInfo / FrameCount Wzorzec dla TIFF
ABBYY: Kod, ktory odczytuje liczbe ramek z plikow TIFF za pomoca engine.CreateImageInfo() i imageInfo.LoadImageFile() zanim rozpocznie petle przegladania ramek, nie ma bezposredniego odpowiednika w IronOCR, poniewaz OcrInput.LoadImageFrames zajmuje sie enumeracja ramek wewnetrznie.
Rozwiązanie: Całkowicie usuń pętlę zliczającą ramki:
// 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
Problem 6: Eksport do formatu DOCX nie ma bezpośredniego odpowiednika
ABBYY: document.Export(path, FileExportFormatEnum.FEF_DOCX, docxParams) generuje dokument Word.IronOCR nie generuje bezpośrednio plików DOCX.
Rozwiązanie:IronOCR tworzy pliki PDF z możliwością wyszukiwania oraz ustrukturyzowane dane tekstowe. W przypadku procesów wymagających pliku DOCX praktycznym rozwiązaniem jest utworzenie pliku PDF z możliwością wyszukiwania i konwersja go na dalszym etapie lub wyodrębnienie tekstu strukturalnego i zapisanie go w pliku DOCX przy użyciu biblioteki takiej jak 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
Przewodnik po wynikach odczytu obejmuje dostęp do danych dotyczących akapitów, wierszy, słów i współrzędnych na poziomie znaków do dalszego przetwarzania.
Lista kontrolna migracji ABBYY FineReader
Zadania przed migracją
Przed wprowadzeniem jakichkolwiek zmian należy przeprowadzić audyt kodu źródłowego:
# 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" .
Udokumentuj każdą klasę, która zawiera pole IEngine lub IFRDocument. Zwróć uwagę na używane formaty eksportu — pliki DOCX wymagają innego podejścia (patrz punkt 6 powyżej).
Zadania związane z aktualizacją kodu
- Usunąć odniesienie
FREngine.dllze wszystkich plików.csproj - Uruchom
dotnet add package IronOcrw każdym projekcie, który używał ABBYY - Dodaj
IronOcr.License.LicenseKey = ...podczas uruchamiania aplikacji (Program.cslub klasy uruchomieniowej) Zainstaluj pakiety NuGet jezykow dla kazdego jezyka innego niz angielski (dotnet add package IronOcr.Languages.French, itp.) - Usun wszystkie
EngineLoader,GetEngineObjectiLoadPredefinedProfilewywolania - Usuń wszystkie wywołania
CreateLanguageParamsilangParams.Languages.Add - Zamień
engine.CreateFRDocument()+document.AddImageFile()+document.Process()nanew IronTesseract().Read(path) - Zastąp pętle w wieloklatkowym TIFF
input.LoadImageFrames(tiffPath) - Zamień
document.PlainText.Textnaresult.Text - Zastap
frDocument.Pages[i].PlainText.Textnaresult.Pages[i].Text - Zastąp
document.Export(..., FEF_PDF, pdfParams)przezresult.SaveAsSearchablePdf(path) - Zamień wszystkie wywołania
document.Close()na blokiusingwOcrInput - Usun
SemaphoreSlimi kod blokujacy, ktory serializowal dostep do silnika ABBYY - Zamien
engine.CreateZone()/zone.SetBounds()/page.Zones.Add()nanew CropRectangle(x, y, width, height)przekazany doinput.LoadImage() - Usuń kroki kopiowania pliku licencji z potoków CI/CD
- Zaktualizuj obrazy Docker — usuń warstwę instalacji SDK, dodaj
libgdiplusdla docelowych systemów Linux
Testy po migracji
- Sprawdź wyniki ekstrakcji tekstu na reprezentatywnej próbce każdego typu dokumentu (faktury, umowy, zeskanowane formularze)
- Sprawdź, czy przetwarzanie wielostronicowych plików TIFF zwraca taką samą liczbę stron, jak liczba ramek wygenerowanych przez ABBYY
- Przetestuj dokumenty wielojęzyczne przy użyciu tych samych danych wejściowych, które zostały wykorzystane do porównania bazowego ABBYY
- Sprawdź, czy pliki PDF z możliwością wyszukiwania umożliwiają wyszukiwanie tekstu w programie Adobe Reader i przeglądarkach obsługujących format PDF
- Uruchom równoległy procesor wsadowy z produkcyjnym poziomem współbieżności i upewnij się, że nie występują żadne wyjątki
- Sprawdz
result.Confidencena znanych dobrych dokumentach, aby ustalic podstawowy prog dla bramek jakosci - Sprawdź inicjalizację klucza licencyjnego z zmiennej środowiskowej w środowisku wdrożeniowym staging
- Sprawdź, czy obraz Docker kompiluje się i uruchamia OCR bez montowania woluminu ABBYY SDK
- Sprawdź, czy proces CI/CD przebiega bez etapu kopiowania pliku licencji
- Uruchom profiler pamieci na przetwarzaczu wsadowym, aby potwierdzic, ze zadne obiekty
OcrInputnie wyciekaja (zweryfikuj umiejscowienieusing)
Kluczowe korzyści z migracji do IronOCR
Złożoność wdrożenia spada o rząd wielkości. Każde wdrożenie ABBYY wymagało instalacji SDK, umieszczenia pliku licencji, konfiguracji ścieżki środowiska uruchomieniowego oraz sprawdzenia, czy pliki znajdują się we właściwych ścieżkach, zanim aplikacja mogła zostać uruchomiona.IronOCR wdraża się jako zależność NuGet. dotnet publish generuje samodzielny artefakt z wbudowanym silnikiem OCR. Przewodnik wdrażania Docker oraz przewodnik konfiguracji Azure przedstawiają pełną konfigurację — oba mieszczą się na jednej stronie.
Interop COM zostal usuniety. Usuniecie warstwy COM eliminuje caly zakres bledow wykonawczych: bledy rejestracji COM na nowych maszynach, niezgodnosci w watkowaniu mieszkan, bledy cyklu zycia RCW oraz 15-25 linii try/finally szablonowego kodu, ktore byly wymagane przy kazdym wywolaniu przetwarzania dokumentow ABBYY. Kod źródłowy się zmniejsza. Wraz z tym zmniejsza się liczba potencjalnych błędów.
Wzrost wolumenu nie powoduje już przeglądów budżetu. Licencja wieczysta IronOCR obejmuje nieograniczoną liczbę dokumentów. Aplikacja, która przetwarza 10 000 dokumentów miesięcznie w pierwszym roku i 2 000 000 miesięcznie w trzecim roku, ponosi ten sam koszt licencji OCR. Nie ma liczników stron, faktur za nadwyżki ani renegocjacji poziomów wolumenu. Strona licencyjna pokazuje wszystkie poziomy — Professional License za 2999 USD obejmuje dziesięciu programistów przetwarzających dowolną ilość danych na dowolnej liczbie celów wdrożeniowych.
Wdrażanie wielopłatformowe otwiera nowe możliwości infrastrukturalne. Warstwa ABBYY COM wymaga systemu Windows. Zespoły, które chciały przenieść przetwarzanie dokumentów do kontenerów Linux ze względu na koszty lub gęstość, napotkały przeszkody.IronOCR działa identycznie w systemach Windows, Linux i macOS z tego samego pakietu NuGet. Migracja z ABBYY usuwa ograniczenie związane z systemem Windows z warstwy OCR stosu aplikacji. Przewodnik wdrożeniowy dla systemu Linux oraz przewodnik wdrożeniowy dla AWS obejmują kompletną konfigurację dla każdego środowiska.
Równoległa przepustowość jest dostępna bez konieczności prac infrastrukturalnych. Zniknęły strategie blokowania, które szeregowały dostęp do silnika ABBYY. IronTesseract instancje są niezależne: uruchom jedną na wątek, uruchom Parallel.ForEach na partii dokumentów, uzyskaj wyniki. Przepustowość skaluje się wraz z dostępnymi rdzeniami procesora bez konieczności pisania dodatkowego kodu. Przykład wielowątkowości pokazuje poprawę czasu rzeczywistego na sprzęcie wielordzeniowym.
Konfiguracja językowa jest odniesieniem do pakietu. Dodanie obsługi OCR w języku niemiećkim lub japońskim do integracji ABBYY wymagało zidentyfikowania plików danych, wdrożenia ich do ścieżek uruchomieniowych na każdym komputerze docelowym oraz obsługi błędów w przypadku braku plików. Z IronOCR, dotnet add package IronOcr.Languages.German dodaje pakiet językowy jako wersjonowane, odtwarzalne zależność NuGet. Menedżer pakietów zapewnia, że dane są obecne w każdej kompilacji. Przewodnik po niestandardowych pakietach językowych obejmuje szkolenie i wdrażanie niestandardowych modeli językowych dla wyspecjalizowanych dziedzin.
Często Zadawane Pytania
Dlaczego warto przejść z ABBYY FineReader Engine na IronOCR?
Typowe czynniki motywujące to eliminacja złożoności interoperacyjności COM, zastąpienie zarządzania licencjami opartego na plikach, uniknięcie rozliczeń za stronę, umożliwienie wdrażania w Dockerze/kontenerach oraz przyjęcie natywnego dla NuGet przepływu pracy, który integruje się ze standardowymi narzędziami .NET.
Jakie są główne zmiany w kodzie podczas migracji z ABBYY FineReader Engine do IronOCR?
Zastąp sekwencje inicjalizacji ABBYY FineReader instancjonowaniem IronTesseract, usuń zarządzanie cyklem życia COM (jawne wzorce Create/Load/Close) i zaktualizuj nazwy właściwości wyników. W rezultacie znacznie zmniejsza się liczba powtarzających się linii kodu.
Jak zainstalować IronOCR, aby rozpocząć migrację?
Uruchom polecenie „Install-Package IronOcr” w konsoli menedżera pakietów lub „dotnet add package IronOcr” w interfejsie CLI. Pakiety językowe są oddzielnymi pakietami: na przykład „dotnet add package IronOcr.Languages.French” dla języka francuskiego.
Czy IronOCR dorównuje dokładnością OCR silnika ABBYY FineReader w przypadku standardowych dokumentów biznesowych?
IronOCR zapewnia wysoką dokładność w przypadku standardowych treści biznesowych, w tym faktur, umów, paragonów i formularzy wypełnionych na komputerze. Filtry przetwarzania wstępnego obrazu (prostowanie, usuwanie szumów, wzmacnianie kontrastu) dodatkowo poprawiają rozpoznawanie w przypadku pogorszonej jakości danych wejściowych.
W jaki sposób IronOCR obsługuje dane językowe, które silnik ABBYY FineReader instaluje oddzielnie?
Dane językowe w IronOCR są dystrybuowane jako pakiety NuGet. Polecenie „dotnet add package IronOcr.Languages.German” instaluje obsługę języka niemiećkiego. Nie wymaga to ręcznego umieszczania plików ani podawania ścieżek katalogów.
Czy migracja z ABBYY FineReader Engine do IronOCR wymaga zmian w infrastrukturze wdrożeniowej?
IronOCR wymaga mniej zmian w infrastrukturze niż ABBYY FineReader Engine. Nie ma ścieżek binarnych SDK, lokalizacji plików licencyjnych ani konfiguracji serwerów licencyjnych. Pakiet NuGet zawiera kompletny silnik OCR, a klucz licencyjny jest ciągiem znaków ustawionym w kodzie aplikacji.
Jak skonfigurować licencjonowanie IronOCR po migracji?
W kodzie uruchamiającym aplikację przypisz IronOcr.License.LicenseKey = „YOUR-KEY”. W Dockerze lub Kubernetesie zapisz klucz jako zmienną środowiskową i odczytaj go podczas uruchamiania. Użyj License.IsValidLicense do sprawdzenia ważności przed przyjęciem ruchu.
Czy IronOCR może przetwarzać pliki PDF w taki sam sposób jak ABBYY FineReader?
Tak. IronOCR odczytuje zarówno natywne, jak i zeskanowane pliki PDF. Należy utworzyć instancję IronTesseract, wywołać ocr.Read(input), gdzie input jest ścieżką do pliku PDF lub obiektem OcrPdfInput, a następnie iterować strony OcrResult. Nie jest wymagany oddzielny proces renderowania plików PDF.
W jaki sposób IronOCR radzi sobie z wątkami podczas przetwarzania dużych ilości danych?
IronTesseract można bezpiecznie instancjonować dla każdego wątku. Uruchom jedną instancję na wątek w Parallel.ForEach lub puli zadań, uruchom OCR równolegle i usuń każdą instancję po zakończeniu. Nie jest wymagany żaden stan globalny ani blokowanie.
Jakie formaty wyjściowe obsługuje IronOCR po wyodrębnieniu tekstu?
IronOCR zwraca ustrukturyzowane wyniki, w tym tekst, współrzędne słów, wyniki pewności i strukturę strony. Opcje eksportu obejmują zwykły tekst, PDF z możliwością wyszukiwania oraz obiekty wyników ustrukturyzowanych do dalszego przetwarzania.
Czy ceny IronOCR są bardziej przewidywalne niż ceny ABBYY FineReader Engine w przypadku skalowania obciążenia?
IronOCR stosuje licencję wieczystą z opłatą ryczałtową, bez opłat za stronę lub wolumen. Niezależnie od tego, czy przetwarzasz 10 000, czy 10 milionów stron, koszt licencji pozostaje stały. Opcje licencji wolumenowych i zespołowych znajdują się na stronie z cennikiem IronOCR.
Co stanie się z moimi istniejącymi testami po migracji z ABBYY FineReader Engine do IronOCR?
Testy sprawdzające wyodrębnioną treść tekstową powinny nadal przechodzić pomyślnie po migracji. Testy weryfikujące wzorce wywołań API lub cykl życia obiektów COM będą wymagały aktualizacji, aby odzwierciedlały prostszy model inicjalizacji i wyników IronOCR.

