Przejdź do treści stopki
FILMY

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

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>
XML

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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}}
YAML

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

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

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

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

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

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

  1. Usunąć odniesienie FREngine.dll ze wszystkich plików .csproj
  2. Uruchom dotnet add package IronOcr w każdym projekcie, który używał ABBYY
  3. Dodaj IronOcr.License.LicenseKey = ... podczas uruchamiania aplikacji (Program.cs lub klasy uruchomieniowej) Zainstaluj pakiety NuGet jezykow dla kazdego jezyka innego niz angielski (dotnet add package IronOcr.Languages.French, itp.)
  4. Usun wszystkie EngineLoader, GetEngineObject i LoadPredefinedProfile wywolania
  5. Usuń wszystkie wywołania CreateLanguageParams i langParams.Languages.Add
  6. Zamień engine.CreateFRDocument() + document.AddImageFile() + document.Process() na new IronTesseract().Read(path)
  7. Zastąp pętle w wieloklatkowym TIFF input.LoadImageFrames(tiffPath)
  8. Zamień document.PlainText.Text na result.Text
  9. Zastap frDocument.Pages[i].PlainText.Text na result.Pages[i].Text
  10. Zastąp document.Export(..., FEF_PDF, pdfParams) przez result.SaveAsSearchablePdf(path)
  11. Zamień wszystkie wywołania document.Close() na bloki using w OcrInput
  12. Usun SemaphoreSlim i kod blokujacy, ktory serializowal dostep do silnika ABBYY
  13. Zamien engine.CreateZone() / zone.SetBounds() / page.Zones.Add() na new CropRectangle(x, y, width, height) przekazany do input.LoadImage()
  14. Usuń kroki kopiowania pliku licencji z potoków CI/CD
  15. Zaktualizuj obrazy Docker — usuń warstwę instalacji SDK, dodaj libgdiplus dla 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.Confidence na 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 OcrInput nie wyciekaja (zweryfikuj umiejscowienie using)

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.

Zwróć uwagęABBYY FineReader i Tesseract są zastrzeżonymi znakami towarowymi ich właścicieli. Ta strona nie jest powiązana, popierana ani sponsorowana przez ABBYY ani Google. Wszystkie nazwy produktów, logo i marki są własnością ich odpowiednich właścicieli. Porównania mają charakter wyłącznie informacyjny i odzwierciedlają informacje dostępne publicznie w momencie pisania.

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.

Kannaopat Udonpant
Inżynier oprogramowania
Zanim stał się inżynierem oprogramowania, Kannapat ukończył doktorat z zasobów środowiskowych na Uniwersytecie Hokkaido w Japonii. W czasie studiowania, Kannapat również został członkiem Laboratorium Robotyki Pojazdów, które jest częścią Wydziału Inżynierii Bioprodukcji. W 2022 roku wykorzystał swoje umiejętności w ...
Czytaj więcej

Zespół wsparcia Iron

Jesteśmy online 24 godziny, 5 dni w tygodniu.
Czat
E-mail
Zadzwoń do mnie