跳至頁尾內容
影片

從 ABBYY FineReader 遷移到 IronOCR

本指南指導.NET開發人員每一步驟替換ABBYY FineReader Engine SDK為IronOCR。 涵蓋了去除COM依賴和SDK安裝程式工件的機械步驟,並將ABBYY的API映射到IronOCR等效,以提供在生產ABBYY整合中最常見的前後程式碼範例。 遷移的目標是那些已決定ABBYY的企業成本和部署複雜性不再符合其專案需求的團隊。

為什麼從ABBYY FineReader遷移

ABBYY FineReader Engine是一個功能強大的OCR平台,但其架構是為了企業Windows店舖與專用基礎設施團隊而設計的。 當.NET團隊的實際工作負載是發票處理、合約數位化或掃描表單提取時,該架構變成了一個責任,而不是資產。

COM互操作債務隨時間增長。 在.NET中,每個ABBYY整合都通過COM互操作層運行。 COM物件需要顯式生命周期管理:建立、初始化、處理,然後在finally塊中關閉,否則流程會泄漏記憶體。 每個接觸ABBYY的程式碼路徑都承擔此模式。 經過兩到三年的功能新增後,生命周期儀式通過服務類、背景工作者和請求處理程式進行傳播。 結果是每個與OCR相關的類中有30-50%的樣板在切換至IronTesseract時完全消失。

SDK安裝程式阻礙了現代部署模式。 ABBYY通過Windows SDK安裝程式部署,該安裝程式在硬編碼路徑放置二進制文件、語言資料、運行時文件和許可證文件。 將使用ABBYY的服務容器化需要從該安裝程式輸出烤製300+MB的自定義基礎映像,或在啟動時掛載具有許可證文件的卷。這兩種方法都不適合標準Kubernetes或雲原生流水線。IronOCR是一個NuGet包:同樣的dotnet restore產生完整的OCR引擎。

按頁面收費將體積變成了成本中心。 ABBYY基於體積的許可模式對超過包含門檻的每個頁面收費。 一個應用在啟動時每月處理50,000份文件,兩年後達到500,000份,其OCR成本隨著其成功而成比例增長。 IronOCR對許可證收取固定費用——每月處理200萬頁的團隊其許可費用與每月處理2000頁的團隊完全相同。

語言資料需要人工部署協調。 ABBYY語言包作為文件位於SDK運行時目錄中。 新增語言意味著識別正確的資料文件,將它們複製到每個部署目標的正確路徑中,並更新CI/CD腳本來包含它們。 在IronOCR中,新增法語是dotnet add package IronOcr.Languages.French——包管理器處理其餘事宜。

許可證文件故障在無預警情況下衝擊生產環境。 ABBYY許可證以loader.GetEngineObject()運行時位於特定的磁盤路徑上。 如果這些文件在新的生產伺服器上缺失——錯誤的部署腳本、文件複製失敗、權限問題——該調用在啟動時拋出。過期的許可證以同樣的方式失敗。 IronOCR的許可是一個鍵值,可以在啟動程式碼中分配,並可儲存在任何秘密管理器中,通過IronOcr.License.IsValidLicense進行檢查,在應用接受流量之前進行驗證。

執行緒安全需要單一共享引擎實例。 ABBYY的引擎不是簡單地執行緒安全的,無法同時從多個執行緒進行CreateFRDocument調用。 生產實現使用鎖定策略或處理器池。 IronOCR的IronTesseract是無狀態的:每個執行緒啟動一個實例,並發運行識別,無需鎖定,完成時釋放。

根本問題

// 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對比ABBYY FineReader:功能比較

下表涵蓋了評估此遷移的團隊所需的相關功能。

功能 ABBYY FineReader Engine IronOCR
安裝 SDK安裝程式(Windows) dotnet add package IronOcr
獲取 聯繫銷售(4-12週) 自助 NuGet
授權模式 Enterprise,每伺服器或每頁 永久性,$999-$2,999一次性
許可管理 .lic + .key 文件在磁盤上 程式碼或環境變數中的鍵值
.NET整合 COM 互操作 本地 .NET
COM依賴 不是
執行緒安全 需要鎖定策略 完整的(每個執行緒一個IronTesseract
支持的語言 190+ 125+
語言安裝 SDK路徑中的運行時資料文件 NuGet 語言套件
PDF輸入 是(通過CreatePDFFile 是(本地,input.LoadPdf()
可搜尋的 PDF 輸出 是(導出流水線) 是(result.SaveAsSearchablePdf()
自動預處理 基於配置文件 內建(Deskew、DeNoise、Contrast、Binarize、Sharpen)
基於區域的OCR 區域物件(SetBounds CropRectangle參數
條形碼讀取 是(ocr.Configuration.ReadBarCodes = true
跨平台 Windows, Linux, macOS Windows、Linux、macOS、Docker、Azure、AWS
Docker部署 需要自定義基礎映像 標準.NET基礎映像+libgdiplus
置信度評分 是(result.Confidence
獲得第一次OCR結果的時間 4-12 週(採購) 同一天

快速入門:ABBYY FineReader到IronOCR遷移

步驟1:替換NuGet包

ABBYY FineReader Engine沒有NuGet包。 通過解除安裝SDK和從專案文件中刪除手動程式集引用來刪除它:


<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

然後從Visual Studio的References節點中刪除FREngine.dll COM互操作引用,或直接從專案文件中刪除相應條目。 從NuGet安裝IronOCR:

dotnet add package IronOcr

步驟2:更新命名空間

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

步驟3:初始化許可證

在應用啟動時新增此程式碼,在任何OCR調用之前:

IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
$vbLabelText   $csharpLabel

將密鑰儲存在環境變數或秘密管理器中,用於生產部署:

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

程式碼遷移範例

在Windows服務和無狀態IronTesseract中的引擎生命周期

ABBYY的引擎初始化儀式應放在服務包裝器中,因為IEngine物件的建立成本很高。 大多數生產整合將引擎包裝在具有顯式啟動和閉包方法的singleton服務中。

ABBYY FineReader方法:

using FREngine;

public class DocumentOcrService : IHostedService, IDisposable
{
    private IEngine _engine;

    public Task StartAsync(CancellationToken cancellationToken)
    {
        // Step 1: Create loader — requiresCOM 互操作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 — requiresCOM 互操作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 COM interop 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

IronOCR方法:

using IronOcr;

public class DocumentOcrService
{
    //不是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
{
    //不是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
    '不是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沒有引擎生命周期。 它在首次使用時內部初始化,不需要顯式關機。 托管的服務包裝器、StopAsync方法都會消失。 如果應用並發處理文件,每個執行緒建立其自己的IronTesseract實例——無需鎖定。 IronTesseract設置指南涵蓋了配置選項,包括Configuration屬性。

識別語言設置

ABBYY語言配置涉及建立一個LanguageParams物件,新增必須與已安裝資料文件匹配的語言名稱字串,並在處理任何文件之前將這些參數與引擎關聯。 每新增一種語言需要運行時路徑中的相應資料文件。

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

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

語言包作為標準NuGet包安裝(dotnet add package IronOcr.Languages.French)。 無需手動部署資料文件,無路徑配置,切換語言時無需重新初始化引擎。 多語言指南涵蓋了合併語言,語言索引列出了所有125+ 可用包。

多幀TIFF處理

ABBYY通過迭代幀並將每個幀作為單獨的文件頁面來處理多頁TIFF文件。 必須從TIFF物件中檢索幀數,然後將每一幀單獨新增到文件容器中。

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

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 讀取多頁TIFF中的每個幀,無需手動迭代。 結果通過result.Pages提供每頁存取,包括文字、座標資料和每幀的置信度。 TIFF輸入指南涵蓋了多幀TIFF和動畫GIF處理。

並行批量處理

ABBYY基於COM的引擎在沒有同步策略的情況下不能安全地從多個執行緒同步調用CreateFRDocument。 生產批量處理器通常維持引擎實例池或通過鎖定進行序列化存取。 這兩種方法都新增了IronOCR消除的基礎設施。

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

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

每個IronTesseract實例是獨立的。 Parallel.ForEach 飽和可用CPU核心,無需任何共享狀態、鎖定或序列化。 ABBYY版本的文件即使在異步包裝器下也是順序處理; IronOCR版本真正並行地處理它們。 多執行緒範例展示了此模式的時間比較。 如需高層次吞吐量控制,請參閱速度優化指南

文件導出流水線

ABBYY通過其FileExportFormatEnum值支持多種導出格式。 導出到DOCX、RTF或純文字要求建立特定格式的導出參數物件,然後調用document.Export,並將適當的枚舉值和參數物件傳遞給它。

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

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

IronOCR的.Text並提供不需要參數物件或格式枚舉的輸出方法。 SaveAsSearchablePdf調用處理PDF導出一步到位,而ABBYY需要三步參數/導出順序。 可搜索PDF指南涵蓋了頁面範圍選項和壓縮設置。 hOCR導出指南涵蓋了HOCR格式,用於消費位置感知的OCR輸出系統。

ABBYY FineReader API到IronOCR映射參考

ABBYY FineReader Engine IronOCR 等效
new EngineLoader() 不需要
loader.GetEngineObject(sdkPath, licensePath) new IronTesseract()
engine.LoadPredefinedProfile("...") 不需要(內部處理)
engine.CreateLanguageParams() 不需要
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迴圈 input.LoadImageFrames(tiff)
engine.CreatePDFFile() 然後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檢查 result.Pages + 單詞座標資料
block.GetAsTableBlock() result.Pages[i].Lines(帶座標)
engine.CreatePDFExportParams() 不需要
document.Export(path, FEF_PDF, params) result.SaveAsSearchablePdf(path)
document.Export(path, FEF_TextUnicodeDefaults, null) File.WriteAllText(path, result.Text)
engine.CreateDOCXExportParams() + 導出 不直接支持
document.Close() OcrInput上處理
_engine.GetLicenseInfo().ExpirationDate IronOcr.License.IsValidLicense
許可文件(ABBYY.key IronOcr.License.LicenseKey = "key"
engine.CreateZone() + zone.SetBounds(x, y, w, h) new CropRectangle(x, y, width, height)

常見的遷移問題与解決方案

問題1:刪除SDK後的COM註冊錯誤

ABBYY: 從專案引用中刪除Could not load type 'FREngine.EngineLoader'或舊命名空間保留的類的COM互操作錯誤而失敗。

解決方案:在移除引用之前搜索所有ABBYY.FineReader的用法。 任何實現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

問題2:識別配置檔案無對應的等效

ABBYY: 調用engine.LoadPredefinedProfile("FieldLevelRecognition")的程式碼使用ABBYY專用的配置檔案來平衡精確度與吞吐量。 IronOCR中無名為Profile的對應屬性。

解決方案: IronOCR通過IronTesseract.Configuration提供相同的權衡。 為了速度優化,請設置ocr.Configuration.TesseractVersion = TesseractVersion.Tesseract5(預設)並減少預處理過濾器。 為了實現最大的精確度,請新增完整的預處理流水線:

// Speed-optimized
var ocr = new IronTesseract();
//不是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();
//不是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()
'不是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

影像質量校正指南 說明了哪些過濾器解決了哪些輸入質量問題。 速度優化指南涵蓋了降低乾淨文件處理時間的配置屬性。

問題3:許可證文件部署步驟仍然存在於CI/CD中

ABBYY: 構建管道中通常包含一步,從安全儲存庫將ABBYY.key文件複製到部署目標。 遷移後,團隊有時會遺漏刪除此步驟,留下引用不再存在的文件路徑的無效部署程式碼。

解決方案: 完全刪除許可證文件複製步驟。 用環境變數註入步驟替換它:

# 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: SetIronOCRlicense
#   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: SetIronOCRlicense
#   env:
#     IRONOCR_LICENSE_KEY: ${{secrets.IRONOCR_LICENSE}}
YAML

然後在應用程式啟動時:

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

問題4:引擎非執行緒安全——現有鎖定程式碼

ABBYY: 從多個執行緒中調用ABBYY的應用程式通常包含lock語句,或執行緒本地引擎實例,以避免COM執行緒問題。 此同步程式碼是特定於ABBYY的執行緒模型的。

解決方案: 刪除所有包裹ABBYY調用的同步程式碼。 IronOCR的IronTesseract可以安全地按執行緒實例化:

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

問題5:TIFF的FrameCount模式

ABBYY: 使用OcrInput.LoadImageFrames內部處理幀枚舉。

解決方案: 完全刪除幀計數迴圈:

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

問題6:DOCX導出無直接對應

ABBYY: document.Export(path, FileExportFormatEnum.FEF_DOCX, docxParams)會產生一個Word文件。 IronOCR不直接產生DOCX輸出。

解決方案: IronOCR生成可搜索的PDF和結構化文字資料。 對於需要DOCX輸出的工作流,實際的遷移途徑是生成可搜索的PDF並在下游轉換,或提取結構化文字並使用如Open XML SDK這樣的庫寫入DOCX:

//IronOCRto 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
}
//IronOCRto 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

讀取結果指南涵蓋了存取段落、行、單詞和字元級座標資料以進行下游處理。

ABBYY FineReader遷移檢查表

遷移前的任務

在做任何更改之前審核程式碼庫:

# 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

記錄持有IFRDocument字段的每個類。 注意哪些導出格式正在使用—— DOCX輸出需要另一種方法(見上面的問題6)。

程式碼更新任務

  1. 從所有FREngine.dll引用
  2. 對每個使用ABBYY的專案運行dotnet add package IronOcr
  3. 在應用啟動時新增Program.cs或啟動類)
  4. 為每個非英語語言安裝語言NuGet包(dotnet add package IronOcr.Languages.French,等等)
  5. 刪除所有LoadPredefinedProfile調用
  6. 刪除所有langParams.Languages.Add調用
  7. engine.CreateFRDocument() + document.AddImageFile() + document.Process()
  8. input.LoadImageFrames(tiffPath)替換多幀TIFF迴圈
  9. document.PlainText.Text
  10. frDocument.Pages[i].PlainText.Text
  11. document.Export(..., FEF_PDF, pdfParams)
  12. 將所有OcrInput
  13. 刪除SemaphoreSlim和序列化ABBYY引擎存取的鎖定程式碼
  14. engine.CreateZone() / zone.SetBounds() / new CropRectangle(x, y, width, height)
  15. 從CI/CD管道中移除許可證文件複製步驟
  16. 更新Docker映像——移除SDK安裝層,為Linux目標新增libgdiplus

遷移後測試

  • 在每種文件型別的代表性樣本上驗證文字提取輸出(發票、合同、掃描表格)
  • 確認多頁TIFF處理返回的頁數與ABBYY生成的幀數相同
  • 用於ABBYY基線比較的相同輸入測試多語言文件
  • 驗證可搜索的PDF輸出在Adobe Reader和瀏覽器PDF查看器中是否可以進行文字搜索
  • 與生產並發級運行並行批量處理器,確認無異常發生
  • 在已知良好的文件上檢查result.Confidence,以建立質量門限的基線
  • 測試在階段部署環境中從環境變數中初始化許可鍵
  • 驗證Docker映像構建並運行OCR,無需安裝ABBYY SDK卷掛載
  • 確認CI/CD管道在沒有許可證文件複制步驟的情況下完成
  • 在批量處理器上運行記憶體分析器,確認using位置)

遷移至IronOCR的主要好處

部署複雜性減少了一個數量級。 每個ABBYY部署均需安裝SDK、放置許可證文件、配置運行時路徑,以及在應用程式啟動之前驗證文件是否位於正確路徑。 IronOCR作為一個NuGet依賴關係進行部署。 dotnet publish生成包含OCR引擎的自包含工件。 Docker部署指南Azure安裝指南顯示了完整的配置——兩者都放在單頁上。

COM互操作消失。 移除COM層消除了整個類別的運行時故障:新機器上的COM註冊錯誤、單元執行緒不匹配、try/finally樣板。 程式碼庫縮小。 錯誤表面與之一起縮小。

體積增長不再觸發預算審查。 IronOCR的永久許可覆蓋無限制的文件量。 在第一年每月處理10,000份文件,第三年每月處理2,000,000份文件的應用程式承擔相同的OCR許可成本。 沒有按頁面計數器,沒有超額發票,沒有體積層級重新談判。 許可頁面顯示所有層級——Professonal許可含10名開發者提供對任何部署目標處理任何文件量的支援費用為$2,999。

跨平台部署開啟新的基礎設施選項。 ABBYY的COM層需要Windows。 想要將文件處理移動到Linux容器的團隊出於成本或密度原因無法實現此願望。 IronOCR在Windows、Linux和macOS上從同一個NuGet包運行一致。 從ABBYY遷移將Windows約束從應用程式堆疊的OCR層中刪除。 Linux部署指南AWS安裝指南各自覆蓋了環境的完整設置。

沒有基礎設施工作可以進行並行吞吐。 序列化ABBYY引擎存取的鎖定策略已經消失。 Parallel.ForEach,然後獲取結果。 吞吐量隨著可用的CPU核心而擴展,無需任何額外程式碼。 多執行緒範例顯示了在多核硬體上的時間改善。

語言配置是包引用。 將德語或日語OCR支援新增到ABBYY整合中意味着識別資料文件,將它們部署到每台目標機器的運行時路徑,並在文件缺失時處理故障。 使用IronOCR,dotnet add package IronOcr.Languages.German將語言包作為版本控制的可重現的NuGet依賴關係新增。 包管理器確保資料在每次構建時保持存在。 自定義語言包指南涵蓋了自定義語言模型的訓練和在專業化域的部署。

請注意ABBYY FineReader 和 Tesseract 是其各自所有者的註冊商標。 本網站不與 ABBYY 或 Google 有關聯,未經其認可或贊助。 所有產品名稱、標誌和品牌均為其各自所有者的財產。 比較僅供資訊用途,並反映撰寫時獲得的公開資訊。

常見問題

為什麼要從ABBYY FineReader Engine遷移到IronOCR?

常見的驅動因素包括消除COM互操作複雜性、更換基於文件的許可證管理、避免按頁收費、啟用Docker/容器部署,以及採取與標準.NET工具整合的NuGet本地工作流。

從ABBYY FineReader Engine遷移到IronOCR時的主要程式碼變更是什麼?

用IronTesseract的實例化替換ABBYY FineReader的初始化序列,移除COM生命週期管理(顯式的Create/Load/Close模式),並更新結果屬性名稱。結果是顯著減少樣板程式碼。

如何安裝IronOCR以開始遷移?

在套件管理器控制台中運行 'Install-Package IronOcr' 或在CLI中運行 'dotnet add package IronOcr'。語言包是單獨的包:例如 'dotnet add package IronOcr.Languages.French' 安裝法語支持。

IronOCR的OCR準確度是否能與ABBYY FineReader Engine匹敵?

IronOCR在包括發票、合同、收據和鍵入的表單等標準業務內容上達到高準確性。圖像預處理過濾器(糾偏、噪音去除、對比度增強)進一步提高了變質輸入的識別。

IronOCR如何處理ABBYY FineReader Engine單獨安裝的語言資料?

IronOCR中的語言資料分發為NuGet包。'dotnet add package IronOcr.Languages.German' 安裝德語支持。不涉及手動文件放置或目錄路徑。

從ABBYY FineReader Engine遷移到IronOCR是否需要對部署基礎設施進行更改?

IronOCR比ABBYY FineReader Engine需要更少的基礎設施更改。沒有SDK二進制路徑、許可證文件放置或許可證伺服器配置。NuGet包包含完整的OCR引擎,許可證金鑰是應用程式碼中的一個字串設置。

遷移後如何配置IronOCR許可證?

在應用啟動程式碼中分配IronOcr.License.LicenseKey = "YOUR-KEY"。在Docker或Kubernetes中,將金鑰儲存為環境變數並在啟動時讀取。在接受流量之前,使用License.IsValidLicense驗證。

IronOCR能像ABBYY FineReader一樣處理PDF嗎?

是的。IronOCR能讀取本地和掃描的PDF。實例化IronTesseract,調用ocr.Read(input),其中input是PDF路徑或OcrPdfInput,並迭代OcrResult頁面。不需要單獨的PDF渲染管道。

IronOCR如何處理大批量處理中的多執行緒?

IronTesseract可以安全地在每個執行緒中實例化。在Parallel.ForEach或Task池中為每個執行緒建立一個實例,並發併運行OCR,當完成時處置每個實例。不需要全域狀態或鎖定。

IronOCR在文字提取後支持哪些輸出格式?

IronOCR返回結構化結果,包括文字、單詞坐標、信心分數和頁面結構。導出選項包括純文字、可搜索PDF以及下游處理的結構化結果物件。

IronOCR的定價在可擴展工作負載方面是否比ABBYY FineReader Engine更可預測?

IronOCR使用固定費率的永久性許可證,沒有按頁或按量收費。無論您處理10000頁還是1000萬頁,許可證費用保持不變。團隊和批量許可證選項可在IronOCR定價頁面上找到。

從ABBYY FineReader Engine遷移到IronOCR後,我現有的測試會發生什麼變化?

遷移後,斷言提取的文字內容的測試應繼續通過。需要更新的測試是驗證API調用模式或COM物件生命週期,這需要反映IronOCR較簡單的初始化和結果模型。

Kannaopat Udonpant
軟體工程師
在成為軟體工程師之前,Kannapat在日本北海道大學完成了環境資源博士學位。在攻讀學位期間,Kannapat還成為車輛機器人實驗室的一員,該實驗室隸屬於生產工程系。在2022年,他憑藉C#技能加入了Iron Software的工程團隊,專注於IronPDF。Kannapat珍視他的工作,因為他能直接向撰寫大部分IronPDF程式碼的開發者學習。除了同儕學習,Kannapat還喜歡在Iron Software工作的社交方面。不寫程式碼或文件時,Kannapat通常在他的PS5上玩遊戲或重看The Last of Us。

Iron 支援團隊

我們線上24小時,每週5天。
聊天
電子郵件
給我打電話