跳至頁尾內容
影片

如何在C#中對車牌進行OCR

本指南是為轉移OCR工作負載從Kofax OmniPage Capture SDK(現行銷售名為Tungsten Automation)至IronOCR的.NET開發人員提供的。 涵蓋了完整的遷移路徑:移除SDK安裝程式依賴,消除引擎生命周期儀式,替代區域識別模式,並現代化錯誤處理。 不需要閱讀比較文章。

為何從Kofax OmniPage(Tungsten)遷移

OmniPage Capture SDK是為企業文件管理基礎設施設計的,當時尚未有NuGet、Docker和CI/CD管道。 2005年時合理的每一個架構選擇,在2026年都會帶來摩擦。

SDK安裝程式在NuGet世界中無立足之地。每一個運行OmniPage程式碼的開發機器、構建代理和生產伺服器都需要在編譯前執行Tungsten SDK安裝程式。 無.csproj包引用可恢復。 升級SDK版本意味著需要在整個區域重新運行安裝程式。 新的開發人員不能在未聯絡管理SDK授權的人士並等待安裝程式存取權限的情況下運行項目。

授權文件是一種操作責任。每台調用.lic文件。 若部署到新伺服器且忘記了該文件,每次OCR調用都會因為授權例外失敗——而不是OCR錯誤。 若使用浮動授權,且應用程式伺服器與授權伺服器之間存在網路分區,則每次Initialize()呼叫都會失敗,直到重新連接,無論該機器是否在昨天成功驗證了其授權。

引擎生命周期管理在錯誤路徑中會洩漏資源。每一個可能的程式碼路徑都必須調用OmniPageEngine.Shutdown()——包括異常處理器、提前返回和超時——否則浮動授權席位將保持鎖定,直到授權伺服器的結帳超時。 防禦性書寫圍繞此限制需要在每個整合點包裹IDisposable模式,這些模式僅存在以保護避免引擎關閉被跳過。

硬體指紋與現代部署衝突。OmniPage激活與硬體綁定。 容器重啟、VM遷移和雲自動擴展都涉及硬體身份變更,促使重新激活要求。 與自動擴展不相容的部署模式就與雲基礎設施不相容。

按頁定價在大規模下不可預測。文件處理峰值——新客戶入駐、積壓批次提交——會對未編列預算的配額產生超量發票。IronOCR在授權階層內是永久且無限的:沒有按頁計費,沒有配額,沒有超量發票。

採購過程阻礙交付速度。OmniPage SDK設有限制銷售。 評估存取、價格和合同條款都需要一個長達4–12周的銷售接洽。 在產品交付截止日期內構建OCR功能的團隊無法等待企業採購週期。 dotnet add package IronOcr在30秒內解決。 請參考IronOCR授權頁面以獲得自助服務定價資訊——$999 Lite 至 $2,999 Enterprise,全部皆為永久授權。

根本問題

OmniPage 在讀取第一個字節前需要完整的初始化儀式,之後在最後一個字節後需要關閉儀式——每一個調用位置都必須管理這兩者:

// OmniPage: Ceremony required on EVERY entry point — init, process, shutdown
using var engine = new OmniPageEngine();
engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic"); // File must exist here
engine.Initialize();   // Network call to license server — can fail for license reasons, not OCR reasons

var document = engine.CreateDocument();
document.AddPage("invoice.jpg");
document.Recognize(new RecognitionSettings { Language = "English" });
string text = document.GetText();
document.Dispose();    // Must not be skipped
engine.Shutdown();     // Must not be skipped — omitting this locks a floating seat
// OmniPage: Ceremony required on EVERY entry point — init, process, shutdown
using var engine = new OmniPageEngine();
engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic"); // File must exist here
engine.Initialize();   // Network call to license server — can fail for license reasons, not OCR reasons

var document = engine.CreateDocument();
document.AddPage("invoice.jpg");
document.Recognize(new RecognitionSettings { Language = "English" });
string text = document.GetText();
document.Dispose();    // Must not be skipped
engine.Shutdown();     // Must not be skipped — omitting this locks a floating seat
Imports OmniPage

' OmniPage: Ceremony required on EVERY entry point — init, process, shutdown
Using engine As New OmniPageEngine()
    engine.SetLicenseFile("C:\Program Files\OmniPage\license.lic") ' File must exist here
    engine.Initialize()   ' Network call to license server — can fail for license reasons, not OCR reasons

    Dim document = engine.CreateDocument()
    document.AddPage("invoice.jpg")
    document.Recognize(New RecognitionSettings With {.Language = "English"})
    Dim text As String = document.GetText()
    document.Dispose()    ' Must not be skipped
    engine.Shutdown()     ' Must not be skipped — omitting this locks a floating seat
End Using
$vbLabelText   $csharpLabel
// IronOCR: One NuGet package, one line at startup, one call to read
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; // At app startup — once, ever
var text = new IronTesseract().Read("invoice.jpg").Text;
// IronOCR: One NuGet package, one line at startup, one call to read
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; // At app startup — once, ever
var text = new IronTesseract().Read("invoice.jpg").Text;
Imports IronOcr

' IronOCR: One NuGet package, one line at startup, one call to read
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY" ' At app startup — once, ever
Dim text = New IronTesseract().Read("invoice.jpg").Text
$vbLabelText   $csharpLabel

OmniPage版本包含八個不同步驟、兩個強制清理調用,一個網路依賴和一個文件系統依賴。 IronOCR版本有兩個。

IronOCR與 Kofax OmniPage(Tungsten):功能比較

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

功能 Kofax OmniPage SDK IronOCR
安裝方法 自訂SDK安裝程式(無NuGet) dotnet add package IronOcr
首次OCR呼叫所需時間 4到12週(採購)+ 小時(設置) 30秒
授權機制 .lic 文件於磁碟 + 可選的授權伺服器 應用程式啟動時的字串金鑰
硬體指紋 是(VM遷移時重新激活) 不是
需要授權伺服器 是(對浮動授權) 不是
按頁運行時計費 可用(可變) 不是
公布的價格 否(聯絡銷售) 是($999 / $1,499 / $2,999 永久)
年度維護費用 授權費用的18-25% 可選
引擎生命周期管理 必需(Initialize / Shutdown 不需要
Windows x64
Linux 是(2026年1月加入) 是(所有版本)
macOS 不是
Docker / Kubernetes 困難(安裝程式 + 映像中的授權文件) 簡單(僅NuGet包)
Azure / AWS Lambda 複雜(授權伺服器可達性) 簡潔
圖像輸入格式 JPG、PNG、BMP、TIFF、GIF等
本地PDF輸入 是(可能需要模塊授權) 是(內建,無需額外授權)
多頁TIFF
可搜尋的PDF輸出 是(result.SaveAsSearchablePdf()
自動前處理 設定-物件配置 內建 + 明確的管道API
支持的語言 120+ 125+(獨立的NuGet包)
結構化資料輸出 是(單詞坐標) 頁、段落、行、詞、字元及坐標
信心評估 是(result.Confidence,每單詞)
條碼識別 附加模塊(需要獨立授權) 內建(ocr.Configuration.ReadBarCodes = true
執行緒安全 複雜(引擎共享約束) 完整(每個執行緒一個IronTesseract
CI/CD管道支持 需要在每個代理上安裝程式 標準dotnet restore

快速入門:從Kofax OmniPage遷移至IronOCR

步驟1:用NuGet包替換SDK

OmniPage沒有可移除的NuGet包。 當遷移完成後,從.csproj文件中刪除手動DLL引用,然後從開發機器中解除安裝SDK。 接著安裝IronOCR:

dotnet add package IronOcr

IronOCR NuGet包匯入了OCR引擎、預處理過濾器和運行時間組件。 無需獨立安裝程式,無本地DLL管理,無組件註冊。

步驟2:更新命名空間

// Before (Kofax OmniPage)
using Kofax.OmniPage.CSDK;
using Kofax.OmniPageCSDK;
using CSDK;

// After (IronOCR)
using IronOcr;
// Before (Kofax OmniPage)
using Kofax.OmniPage.CSDK;
using Kofax.OmniPageCSDK;
using CSDK;

// After (IronOCR)
using IronOcr;
Imports IronOcr
$vbLabelText   $csharpLabel

步驟3:初始化許可證

在應用程式啟動時新增一行。這取代了engine.Initialize()調用:

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

將金鑰儲存於環境變數中(appsettings.json而非源程式碼中。 金鑰是離線讀取的——無網路調用在運行時發生。

程式碼遷移範例

引擎初始化錯誤路徑替代

OmniPage生命周期模型中最危險的方面不是快樂路徑——而是錯誤路徑。 如果engine.Shutdown()之間拋出的異常都可能鎖定浮動授權席位。 生產程式碼需要在每個文件處理調用周圍使用try/finally塊:

Kofax OmniPage方法:

// OmniPage: try/finally required everywhere to protect license seat release
public string ProcessInvoice(string imagePath)
{
    var engine = new OmniPageEngine();
    engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic");

    try
    {
        engine.Initialize(); // Contacts license server — failure here locks nothing
        var document = engine.CreateDocument();

        try
        {
            document.AddPage(imagePath);
            document.Recognize(new RecognitionSettings { Language = "English" });
            return document.GetText();
        }
        catch (RecognitionException ex)
        {
            // Log, rethrow — but Shutdown must still be called
            throw new OcrProcessingException("Recognition failed", ex);
        }
        finally
        {
            document.Dispose(); // Inner finally: release document
        }
    }
    catch (LicenseValidationException ex)
    {
        throw new OcrProcessingException("License validation failed", ex);
    }
    finally
    {
        engine.Shutdown(); // Outer finally: release license seat — MUST execute
    }
}
// OmniPage: try/finally required everywhere to protect license seat release
public string ProcessInvoice(string imagePath)
{
    var engine = new OmniPageEngine();
    engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic");

    try
    {
        engine.Initialize(); // Contacts license server — failure here locks nothing
        var document = engine.CreateDocument();

        try
        {
            document.AddPage(imagePath);
            document.Recognize(new RecognitionSettings { Language = "English" });
            return document.GetText();
        }
        catch (RecognitionException ex)
        {
            // Log, rethrow — but Shutdown must still be called
            throw new OcrProcessingException("Recognition failed", ex);
        }
        finally
        {
            document.Dispose(); // Inner finally: release document
        }
    }
    catch (LicenseValidationException ex)
    {
        throw new OcrProcessingException("License validation failed", ex);
    }
    finally
    {
        engine.Shutdown(); // Outer finally: release license seat — MUST execute
    }
}
Imports System

' OmniPage: try/finally required everywhere to protect license seat release
Public Function ProcessInvoice(imagePath As String) As String
    Dim engine As New OmniPageEngine()
    engine.SetLicenseFile("C:\Program Files\OmniPage\license.lic")

    Try
        engine.Initialize() ' Contacts license server — failure here locks nothing
        Dim document = engine.CreateDocument()

        Try
            document.AddPage(imagePath)
            document.Recognize(New RecognitionSettings With {.Language = "English"})
            Return document.GetText()
        Catch ex As RecognitionException
            ' Log, rethrow — but Shutdown must still be called
            Throw New OcrProcessingException("Recognition failed", ex)
        Finally
            document.Dispose() ' Inner finally: release document
        End Try
    Catch ex As LicenseValidationException
        Throw New OcrProcessingException("License validation failed", ex)
    Finally
        engine.Shutdown() ' Outer finally: release license seat — MUST execute
    End Try
End Function
$vbLabelText   $csharpLabel

IronOCR方法:

// IronOCR:不是license seat to release, no engine to shut down
public string ProcessInvoice(string imagePath)
{
    using var input = new OcrInput();
    input.LoadImage(imagePath);

    var ocr = new IronTesseract();
    var result = ocr.Read(input);
    return result.Text;
    // OcrInput disposal is automatic — no license implications on any code path
}
// IronOCR:不是license seat to release, no engine to shut down
public string ProcessInvoice(string imagePath)
{
    using var input = new OcrInput();
    input.LoadImage(imagePath);

    var ocr = new IronTesseract();
    var result = ocr.Read(input);
    return result.Text;
    // OcrInput disposal is automatic — no license implications on any code path
}
Imports IronOcr

Public Function ProcessInvoice(imagePath As String) As String
    Using input As New OcrInput()
        input.LoadImage(imagePath)

        Dim ocr As New IronTesseract()
        Dim result = ocr.Read(input)
        Return result.Text
    End Using
End Function
$vbLabelText   $csharpLabel

OcrInput處理已載入影像資料的記憶體清理。 不存在try/finally來保護授權席位。 此方法中的異常在方法自身範圍之外無副作用。 請參見IronTesseract設置指南以獲得配置選項,包括執行緒本地實例。

基於區域的識別遷移

OmniPage的主結構化提取機制為區域定義:文件區域使用坐標邊界和識別型別(OCR、ICR、OMR、條形碼)逐區聲明。 開發人員根據文件模板定義CropRectangle來實現相同的目標提取,無需模板管理或區域型別聲明:

Kofax OmniPage方法:

// OmniPage: Zone-based form extraction with template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    // Define zones per document template
    var zones = new List<FormZone>
    {
        new FormZone { Name = "InvoiceNumber", Type = "OCR",
            X = 520, Y = 80, Width = 200, Height = 30 },
        new FormZone { Name = "InvoiceDate",   Type = "OCR",
            X = 520, Y = 120, Width = 200, Height = 30 },
        new FormZone { Name = "TotalAmount",   Type = "OCR",
            X = 520, Y = 580, Width = 200, Height = 30 },
        new FormZone { Name = "VendorName",    Type = "OCR",
            X = 50,  Y = 80,  Width = 300, Height = 40 }
    };

    var template = new FormTemplate { Name = "StandardInvoice", Zones = zones };
    var settings = new RecognitionSettings { Language = "English" };

    var document = engine.CreateDocument();
    document.AddPage(invoicePath);
    document.ApplyTemplate(template);
    document.Recognize(settings);

    var fields = new Dictionary<string, string>();
    foreach (var zone in zones)
        fields[zone.Name] = document.GetZoneText(zone.Name);

    document.Dispose();
    engine.Shutdown();
    return fields;
}
// OmniPage: Zone-based form extraction with template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    // Define zones per document template
    var zones = new List<FormZone>
    {
        new FormZone { Name = "InvoiceNumber", Type = "OCR",
            X = 520, Y = 80, Width = 200, Height = 30 },
        new FormZone { Name = "InvoiceDate",   Type = "OCR",
            X = 520, Y = 120, Width = 200, Height = 30 },
        new FormZone { Name = "TotalAmount",   Type = "OCR",
            X = 520, Y = 580, Width = 200, Height = 30 },
        new FormZone { Name = "VendorName",    Type = "OCR",
            X = 50,  Y = 80,  Width = 300, Height = 40 }
    };

    var template = new FormTemplate { Name = "StandardInvoice", Zones = zones };
    var settings = new RecognitionSettings { Language = "English" };

    var document = engine.CreateDocument();
    document.AddPage(invoicePath);
    document.ApplyTemplate(template);
    document.Recognize(settings);

    var fields = new Dictionary<string, string>();
    foreach (var zone in zones)
        fields[zone.Name] = document.GetZoneText(zone.Name);

    document.Dispose();
    engine.Shutdown();
    return fields;
}
Imports System
Imports System.Collections.Generic

' OmniPage: Zone-based form extraction with template management
Public Function ExtractInvoiceFields(invoicePath As String) As Dictionary(Of String, String)
    Using engine As New OmniPageEngine()
        engine.SetLicenseFile(licensePath)
        engine.Initialize()

        ' Define zones per document template
        Dim zones As New List(Of FormZone) From {
            New FormZone With {.Name = "InvoiceNumber", .Type = "OCR", .X = 520, .Y = 80, .Width = 200, .Height = 30},
            New FormZone With {.Name = "InvoiceDate", .Type = "OCR", .X = 520, .Y = 120, .Width = 200, .Height = 30},
            New FormZone With {.Name = "TotalAmount", .Type = "OCR", .X = 520, .Y = 580, .Width = 200, .Height = 30},
            New FormZone With {.Name = "VendorName", .Type = "OCR", .X = 50, .Y = 80, .Width = 300, .Height = 40}
        }

        Dim template As New FormTemplate With {.Name = "StandardInvoice", .Zones = zones}
        Dim settings As New RecognitionSettings With {.Language = "English"}

        Dim document = engine.CreateDocument()
        document.AddPage(invoicePath)
        document.ApplyTemplate(template)
        document.Recognize(settings)

        Dim fields As New Dictionary(Of String, String)()
        For Each zone In zones
            fields(zone.Name) = document.GetZoneText(zone.Name)
        Next

        document.Dispose()
        engine.Shutdown()
        Return fields
    End Using
End Function
$vbLabelText   $csharpLabel

IronOCR方法:

// IronOCR: CropRectangle targets specific regions — no template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
    var fields = new Dictionary<string, string>();
    var ocr = new IronTesseract();

    // Extract each field by reading only the target region
    var fieldRegions = new Dictionary<string, CropRectangle>
    {
        ["InvoiceNumber"] = new CropRectangle(520, 80,  200, 30),
        ["InvoiceDate"]   = new CropRectangle(520, 120, 200, 30),
        ["TotalAmount"]   = new CropRectangle(520, 580, 200, 30),
        ["VendorName"]    = new CropRectangle(50,  80,  300, 40)
    };

    foreach (var (fieldName, region) in fieldRegions)
    {
        using var input = new OcrInput();
        input.LoadImage(invoicePath, region);
        fields[fieldName] = ocr.Read(input).Text.Trim();
    }

    return fields;
}
// IronOCR: CropRectangle targets specific regions — no template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
    var fields = new Dictionary<string, string>();
    var ocr = new IronTesseract();

    // Extract each field by reading only the target region
    var fieldRegions = new Dictionary<string, CropRectangle>
    {
        ["InvoiceNumber"] = new CropRectangle(520, 80,  200, 30),
        ["InvoiceDate"]   = new CropRectangle(520, 120, 200, 30),
        ["TotalAmount"]   = new CropRectangle(520, 580, 200, 30),
        ["VendorName"]    = new CropRectangle(50,  80,  300, 40)
    };

    foreach (var (fieldName, region) in fieldRegions)
    {
        using var input = new OcrInput();
        input.LoadImage(invoicePath, region);
        fields[fieldName] = ocr.Read(input).Text.Trim();
    }

    return fields;
}
Imports System.Collections.Generic

' IronOCR: CropRectangle targets specific regions — no template management
Public Function ExtractInvoiceFields(invoicePath As String) As Dictionary(Of String, String)
    Dim fields As New Dictionary(Of String, String)()
    Dim ocr As New IronTesseract()

    ' Extract each field by reading only the target region
    Dim fieldRegions As New Dictionary(Of String, CropRectangle) From {
        {"InvoiceNumber", New CropRectangle(520, 80, 200, 30)},
        {"InvoiceDate", New CropRectangle(520, 120, 200, 30)},
        {"TotalAmount", New CropRectangle(520, 580, 200, 30)},
        {"VendorName", New CropRectangle(50, 80, 300, 40)}
    }

    For Each kvp In fieldRegions
        Dim fieldName = kvp.Key
        Dim region = kvp.Value
        Using input As New OcrInput()
            input.LoadImage(invoicePath, region)
            fields(fieldName) = ocr.Read(input).Text.Trim()
        End Using
    Next

    Return fields
End Function
$vbLabelText   $csharpLabel

(x, y, width, height)是以像素為單位。 OCR引擎僅處理指定的區域,而不是整頁——正如區域處理在OmniPage中的效率優點。 基於區域的OCR指南涵蓋了坐標選擇模式,包括如何使用OcrInput的多區域API從單一影像載入中提取多個區域。

結構化資料輸出遷移

OmniPage通過專用的導出管道暴露文件結構:格式設置應用於已識別文件,並將輸出寫入指定格式(RTF、XML、CSV、可搜索PDF)的文件。 存取單詞級坐標需要使用明確位置查詢來反覆運算結果迭代器。 IronOCR將相同的結構化資料直接暴露於結果物件上,無需二次導出步驟:

Kofax OmniPage方法:

// OmniPage: Structured output requires format configuration and file export
public void ExtractStructuredContent(string imagePath, string outputDir)
{
    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    var settings = new RecognitionSettings { Language = "English" };
    var document = engine.CreateDocument();
    document.AddPage(imagePath);
    document.Recognize(settings);

    // Word-level coordinates through result iterator
    var iterator = document.GetResultIterator(ResultIteratorLevel.Word);
    while (iterator.MoveNext())
    {
        string word = iterator.GetText();
        var bounds = iterator.GetBoundingBox();
        double confidence = iterator.GetConfidence();
        Console.WriteLine($"Word: {word} at ({bounds.X},{bounds.Y}) conf:{confidence:F1}%");
    }

    // Structured export requires separate output format configuration
    var outputSettings = new OutputSettings
    {
        Format = OutputFormat.XML,
        IncludeCoordinates = true,
        IncludeConfidence = true
    };
    document.SaveAs(Path.Combine(outputDir, "output.xml"), outputSettings);

    document.Dispose();
    engine.Shutdown();
}
// OmniPage: Structured output requires format configuration and file export
public void ExtractStructuredContent(string imagePath, string outputDir)
{
    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    var settings = new RecognitionSettings { Language = "English" };
    var document = engine.CreateDocument();
    document.AddPage(imagePath);
    document.Recognize(settings);

    // Word-level coordinates through result iterator
    var iterator = document.GetResultIterator(ResultIteratorLevel.Word);
    while (iterator.MoveNext())
    {
        string word = iterator.GetText();
        var bounds = iterator.GetBoundingBox();
        double confidence = iterator.GetConfidence();
        Console.WriteLine($"Word: {word} at ({bounds.X},{bounds.Y}) conf:{confidence:F1}%");
    }

    // Structured export requires separate output format configuration
    var outputSettings = new OutputSettings
    {
        Format = OutputFormat.XML,
        IncludeCoordinates = true,
        IncludeConfidence = true
    };
    document.SaveAs(Path.Combine(outputDir, "output.xml"), outputSettings);

    document.Dispose();
    engine.Shutdown();
}
Imports System
Imports System.IO

Public Sub ExtractStructuredContent(imagePath As String, outputDir As String)
    Using engine As New OmniPageEngine()
        engine.SetLicenseFile(licensePath)
        engine.Initialize()

        Dim settings As New RecognitionSettings With {.Language = "English"}
        Dim document = engine.CreateDocument()
        document.AddPage(imagePath)
        document.Recognize(settings)

        ' Word-level coordinates through result iterator
        Dim iterator = document.GetResultIterator(ResultIteratorLevel.Word)
        While iterator.MoveNext()
            Dim word As String = iterator.GetText()
            Dim bounds = iterator.GetBoundingBox()
            Dim confidence As Double = iterator.GetConfidence()
            Console.WriteLine($"Word: {word} at ({bounds.X},{bounds.Y}) conf:{confidence:F1}%")
        End While

        ' Structured export requires separate output format configuration
        Dim outputSettings As New OutputSettings With {
            .Format = OutputFormat.XML,
            .IncludeCoordinates = True,
            .IncludeConfidence = True
        }
        document.SaveAs(Path.Combine(outputDir, "output.xml"), outputSettings)

        document.Dispose()
        engine.Shutdown()
    End Using
End Sub
$vbLabelText   $csharpLabel

IronOCR方法:

// IronOCR: Structured data directly on OcrResult — no export step
public void ExtractStructuredContent(string imagePath)
{
    var result = new IronTesseract().Read(imagePath);

    Console.WriteLine($"Confidence: {result.Confidence:F1}%");
    Console.WriteLine($"Pages: {result.Pages.Length}");

    foreach (var page in result.Pages)
    {
        foreach (var paragraph in page.Paragraphs)
        {
            Console.WriteLine($"[Paragraph at ({paragraph.X},{paragraph.Y})]");
            Console.WriteLine(paragraph.Text);

            foreach (var word in paragraph.Words)
            {
                // Per-word confidence and coordinates — no iterator needed
                Console.WriteLine(
                    $"  Word: '{word.Text}' " +
                    $"at ({word.X},{word.Y}) " +
                    $"conf: {word.Confidence:F1}%");
            }
        }
    }
}
// IronOCR: Structured data directly on OcrResult — no export step
public void ExtractStructuredContent(string imagePath)
{
    var result = new IronTesseract().Read(imagePath);

    Console.WriteLine($"Confidence: {result.Confidence:F1}%");
    Console.WriteLine($"Pages: {result.Pages.Length}");

    foreach (var page in result.Pages)
    {
        foreach (var paragraph in page.Paragraphs)
        {
            Console.WriteLine($"[Paragraph at ({paragraph.X},{paragraph.Y})]");
            Console.WriteLine(paragraph.Text);

            foreach (var word in paragraph.Words)
            {
                // Per-word confidence and coordinates — no iterator needed
                Console.WriteLine(
                    $"  Word: '{word.Text}' " +
                    $"at ({word.X},{word.Y}) " +
                    $"conf: {word.Confidence:F1}%");
            }
        }
    }
}
Public Sub ExtractStructuredContent(imagePath As String)
    Dim result = New IronTesseract().Read(imagePath)

    Console.WriteLine($"Confidence: {result.Confidence:F1}%")
    Console.WriteLine($"Pages: {result.Pages.Length}")

    For Each page In result.Pages
        For Each paragraph In page.Paragraphs
            Console.WriteLine($"[Paragraph at ({paragraph.X},{paragraph.Y})]")
            Console.WriteLine(paragraph.Text)

            For Each word In paragraph.Words
                ' Per-word confidence and coordinates — no iterator needed
                Console.WriteLine(
                    $"  Word: '{word.Text}' " &
                    $"at ({word.X},{word.Y}) " &
                    $"conf: {word.Confidence:F1}%")
            Next
        Next
    Next
End Sub
$vbLabelText   $csharpLabel

OcrResult物件的層次——頁、段落、行、詞、字元——提供OmniPage的結果迭代器暴露的相同位置資料,但作為可導航的物件圖形,而不是順序游標。 信心水準不需要額外配置即可在結果、頁、段落和單詞級別取得。 讀取結果指南涵蓋了所有結構資料屬性,信心水準指南則涵蓋了每個單詞驗證模式。

多頁TIFF處理遷移

OmniPage的多頁TIFF工作流程需要從TIFF容器逐頁提取,每頁有獨立的識別調用和文件處理。 這將建立比例於頁數的樣板和資源管理範圍。 IronOCR在一次調用中載入多幀TIFF文件:

Kofax OmniPage方法:

// OmniPage: Page-by-page TIFF extraction with per-page lifecycle
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
    var pageTexts = new List<string>();

    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    // Open TIFF and iterate frames
    var tiffContainer = engine.OpenTiff(tiffPath);
    int pageCount = tiffContainer.GetPageCount();

    var settings = new RecognitionSettings { Language = "English" };

    for (int i = 0; i < pageCount; i++)
    {
        var page = tiffContainer.GetPage(i); // Extract frame
        var document = engine.CreateDocument();

        try
        {
            document.AddPage(page);
            document.Recognize(settings);
            pageTexts.Add(document.GetText());
        }
        finally
        {
            document.Dispose(); // Dispose per page to manage memory
        }
    }

    tiffContainer.Dispose();
    engine.Shutdown();
    return pageTexts;
}
// OmniPage: Page-by-page TIFF extraction with per-page lifecycle
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
    var pageTexts = new List<string>();

    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    // Open TIFF and iterate frames
    var tiffContainer = engine.OpenTiff(tiffPath);
    int pageCount = tiffContainer.GetPageCount();

    var settings = new RecognitionSettings { Language = "English" };

    for (int i = 0; i < pageCount; i++)
    {
        var page = tiffContainer.GetPage(i); // Extract frame
        var document = engine.CreateDocument();

        try
        {
            document.AddPage(page);
            document.Recognize(settings);
            pageTexts.Add(document.GetText());
        }
        finally
        {
            document.Dispose(); // Dispose per page to manage memory
        }
    }

    tiffContainer.Dispose();
    engine.Shutdown();
    return pageTexts;
}
Imports System.Collections.Generic

' OmniPage: Page-by-page TIFF extraction with per-page lifecycle
Public Function ExtractFromMultiPageTiff(tiffPath As String) As List(Of String)
    Dim pageTexts As New List(Of String)()

    Using engine As New OmniPageEngine()
        engine.SetLicenseFile(licensePath)
        engine.Initialize()

        ' Open TIFF and iterate frames
        Dim tiffContainer = engine.OpenTiff(tiffPath)
        Dim pageCount As Integer = tiffContainer.GetPageCount()

        Dim settings As New RecognitionSettings With {.Language = "English"}

        For i As Integer = 0 To pageCount - 1
            Dim page = tiffContainer.GetPage(i) ' Extract frame
            Dim document = engine.CreateDocument()

            Try
                document.AddPage(page)
                document.Recognize(settings)
                pageTexts.Add(document.GetText())
            Finally
                document.Dispose() ' Dispose per page to manage memory
            End Try
        Next

        tiffContainer.Dispose()
        engine.Shutdown()
    End Using

    Return pageTexts
End Function
$vbLabelText   $csharpLabel

IronOCR方法:

// IronOCR: Multi-frame TIFF loaded in one call — all pages in one result
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
    using var input = new OcrInput();
    input.LoadImageFrames(tiffPath); // All frames loaded automatically

    var result = new IronTesseract().Read(input);

    // Each TIFF frame becomes a page in the result
    return result.Pages.Select(page => page.Text).ToList();
}
// IronOCR: Multi-frame TIFF loaded in one call — all pages in one result
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
    using var input = new OcrInput();
    input.LoadImageFrames(tiffPath); // All frames loaded automatically

    var result = new IronTesseract().Read(input);

    // Each TIFF frame becomes a page in the result
    return result.Pages.Select(page => page.Text).ToList();
}
Imports System.Collections.Generic
Imports IronOcr

' IronOCR: Multi-frame TIFF loaded in one call — all pages in one result
Public Function ExtractFromMultiPageTiff(tiffPath As String) As List(Of String)
    Using input As New OcrInput()
        input.LoadImageFrames(tiffPath) ' All frames loaded automatically

        Dim result = New IronTesseract().Read(input)

        ' Each TIFF frame becomes a page in the result
        Return result.Pages.Select(Function(page) page.Text).ToList()
    End Using
End Function
$vbLabelText   $csharpLabel

LoadImageFrames在一次調用中從TIFF容器讀取每幀。 結果將每幀暴露為一個OcrResult.Page,保留逐頁結構,不需要手動迭代迴圈或每頁清理。對於生產TIFF批次工作流,請參見TIFF和GIF輸入指南

安全執行緒並行處理遷移

OmniPage引擎是一個共享的,有狀態的資源。 在執行緒間共享一個CreateDocument()可能導致交錯狀態。 安全模式——每個執行緒一個引擎實例——與引擎的重型初始化成本相衝突。IronOCR實例不共享狀態:在每個執行緒建立一個IronTesseract,無需任何協調開銷:

Kofax OmniPage方法:

// OmniPage: Shared engine with locking to serialize document operations
public ConcurrentDictionary<string, string> ProcessBatchWithEngine(
    string[] imagePaths, string licensePath)
{
    var results = new ConcurrentDictionary<string, string>();
    var engineLock = new object();

    // One engine — document operations must be serialized
    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    var settings = new RecognitionSettings { Language = "English" };

    Parallel.ForEach(imagePaths, imagePath =>
    {
        lock (engineLock) // Serialize: one recognition at a time
        {
            var document = engine.CreateDocument();
            try
            {
                document.AddPage(imagePath);
                document.Recognize(settings);
                results[imagePath] = document.GetText();
            }
            finally
            {
                document.Dispose();
            }
        }
    });

    engine.Shutdown();
    return results;
}
// OmniPage: Shared engine with locking to serialize document operations
public ConcurrentDictionary<string, string> ProcessBatchWithEngine(
    string[] imagePaths, string licensePath)
{
    var results = new ConcurrentDictionary<string, string>();
    var engineLock = new object();

    // One engine — document operations must be serialized
    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    var settings = new RecognitionSettings { Language = "English" };

    Parallel.ForEach(imagePaths, imagePath =>
    {
        lock (engineLock) // Serialize: one recognition at a time
        {
            var document = engine.CreateDocument();
            try
            {
                document.AddPage(imagePath);
                document.Recognize(settings);
                results[imagePath] = document.GetText();
            }
            finally
            {
                document.Dispose();
            }
        }
    });

    engine.Shutdown();
    return results;
}
Imports System.Collections.Concurrent
Imports System.Threading.Tasks

' OmniPage: Shared engine with locking to serialize document operations
Public Function ProcessBatchWithEngine(imagePaths As String(), licensePath As String) As ConcurrentDictionary(Of String, String)
    Dim results As New ConcurrentDictionary(Of String, String)()
    Dim engineLock As New Object()

    ' One engine — document operations must be serialized
    Using engine As New OmniPageEngine()
        engine.SetLicenseFile(licensePath)
        engine.Initialize()

        Dim settings As New RecognitionSettings With {.Language = "English"}

        Parallel.ForEach(imagePaths, Sub(imagePath)
                                        SyncLock engineLock ' Serialize: one recognition at a time
                                            Dim document = engine.CreateDocument()
                                            Try
                                                document.AddPage(imagePath)
                                                document.Recognize(settings)
                                                results(imagePath) = document.GetText()
                                            Finally
                                                document.Dispose()
                                            End Try
                                        End SyncLock
                                    End Sub)
        engine.Shutdown()
    End Using

    Return results
End Function
$vbLabelText   $csharpLabel

IronOCR方法:

// IronOCR: One IronTesseract per thread — no locking, genuine parallelism
public ConcurrentDictionary<string, string> ProcessBatchInParallel(string[] imagePaths)
{
    var results = new ConcurrentDictionary<string, string>();

    Parallel.ForEach(imagePaths, imagePath =>
    {
        // Each thread creates its own instance — no shared state, no locks
        var ocr = new IronTesseract();
        using var input = new OcrInput();
        input.LoadImage(imagePath);

        var result = ocr.Read(input);
        results[imagePath] = result.Text;
    });

    return results;
}
// IronOCR: One IronTesseract per thread — no locking, genuine parallelism
public ConcurrentDictionary<string, string> ProcessBatchInParallel(string[] imagePaths)
{
    var results = new ConcurrentDictionary<string, string>();

    Parallel.ForEach(imagePaths, imagePath =>
    {
        // Each thread creates its own instance — no shared state, no locks
        var ocr = new IronTesseract();
        using var input = new OcrInput();
        input.LoadImage(imagePath);

        var result = ocr.Read(input);
        results[imagePath] = result.Text;
    });

    return results;
}
Imports System.Collections.Concurrent
Imports System.Threading.Tasks
Imports IronOcr

' IronOCR: One IronTesseract per thread — no locking, genuine parallelism
Public Function ProcessBatchInParallel(imagePaths As String()) As ConcurrentDictionary(Of String, String)
    Dim results As New ConcurrentDictionary(Of String, String)()

    Parallel.ForEach(imagePaths, Sub(imagePath)
        ' Each thread creates its own instance — no shared state, no locks
        Dim ocr As New IronTesseract()
        Using input As New OcrInput()
            input.LoadImage(imagePath)

            Dim result = ocr.Read(input)
            results(imagePath) = result.Text
        End Using
    End Sub)

    Return results
End Function
$vbLabelText   $csharpLabel

OmniPage版本通過鎖定來序列化所有識別,消除並行性。 IronOCR版本無需協調即可並行處理文件。 對於高吞吐量批量工作流,請參見多執行緒範例速度優化指南

從掃描檔案生成可搜索PDF

OmniPage的可搜索PDF輸出需要在保存之前組裝一個具有明確OutputFormat配置的識別管道。 IronOCR通過在結果物件上調用一個方法即可生成可搜索的PDF:

Kofax OmniPage方法:

// OmniPage: Searchable PDF requires OutputSettings configuration before save
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    var recognitionSettings = new RecognitionSettings
    {
        Language = "English",
        AccuracyMode = "Maximum",
        PreserveLayout = true
    };

    var outputSettings = new OutputSettings
    {
        Format = OutputFormat.SearchablePDF,
        Compression = PDFCompression.Standard,
        ImageQuality = 85,
        EmbedFonts = true
    };

    foreach (var pdfPath in scannedPdfPaths)
    {
        var document = engine.OpenPDF(pdfPath);
        document.RecognizeAll(recognitionSettings);

        string outputPath = Path.Combine(
            outputDirectory,
            Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");

        document.SaveAs(outputPath, outputSettings);
        document.Dispose();
    }

    engine.Shutdown();
}
// OmniPage: Searchable PDF requires OutputSettings configuration before save
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
    using var engine = new OmniPageEngine();
    engine.SetLicenseFile(licensePath);
    engine.Initialize();

    var recognitionSettings = new RecognitionSettings
    {
        Language = "English",
        AccuracyMode = "Maximum",
        PreserveLayout = true
    };

    var outputSettings = new OutputSettings
    {
        Format = OutputFormat.SearchablePDF,
        Compression = PDFCompression.Standard,
        ImageQuality = 85,
        EmbedFonts = true
    };

    foreach (var pdfPath in scannedPdfPaths)
    {
        var document = engine.OpenPDF(pdfPath);
        document.RecognizeAll(recognitionSettings);

        string outputPath = Path.Combine(
            outputDirectory,
            Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");

        document.SaveAs(outputPath, outputSettings);
        document.Dispose();
    }

    engine.Shutdown();
}
Imports System.IO

' OmniPage: Searchable PDF requires OutputSettings configuration before save
Public Sub ConvertArchiveToSearchable(scannedPdfPaths As String(), outputDirectory As String)
    Using engine As New OmniPageEngine()
        engine.SetLicenseFile(licensePath)
        engine.Initialize()

        Dim recognitionSettings As New RecognitionSettings With {
            .Language = "English",
            .AccuracyMode = "Maximum",
            .PreserveLayout = True
        }

        Dim outputSettings As New OutputSettings With {
            .Format = OutputFormat.SearchablePDF,
            .Compression = PDFCompression.Standard,
            .ImageQuality = 85,
            .EmbedFonts = True
        }

        For Each pdfPath As String In scannedPdfPaths
            Dim document = engine.OpenPDF(pdfPath)
            document.RecognizeAll(recognitionSettings)

            Dim outputPath As String = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(pdfPath) & "_searchable.pdf")

            document.SaveAs(outputPath, outputSettings)
            document.Dispose()
        Next
    End Using

    engine.Shutdown()
End Sub
$vbLabelText   $csharpLabel

IronOCR方法:

// IronOCR: Searchable PDF output is one method call on the result
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
    var ocr = new IronTesseract();

    foreach (var pdfPath in scannedPdfPaths)
    {
        using var input = new OcrInput();
        input.LoadPdf(pdfPath); // All pages loaded automatically

        var result = ocr.Read(input);

        string outputPath = Path.Combine(
            outputDirectory,
            Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");

        result.SaveAsSearchablePdf(outputPath);
        Console.WriteLine($"Processed: {Path.GetFileName(pdfPath)} ({result.Pages.Length} pages)");
    }
}
// IronOCR: Searchable PDF output is one method call on the result
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
    var ocr = new IronTesseract();

    foreach (var pdfPath in scannedPdfPaths)
    {
        using var input = new OcrInput();
        input.LoadPdf(pdfPath); // All pages loaded automatically

        var result = ocr.Read(input);

        string outputPath = Path.Combine(
            outputDirectory,
            Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");

        result.SaveAsSearchablePdf(outputPath);
        Console.WriteLine($"Processed: {Path.GetFileName(pdfPath)} ({result.Pages.Length} pages)");
    }
}
Imports System.IO
Imports IronOcr

Public Sub ConvertArchiveToSearchable(scannedPdfPaths As String(), outputDirectory As String)
    Dim ocr As New IronTesseract()

    For Each pdfPath As String In scannedPdfPaths
        Using input As New OcrInput()
            input.LoadPdf(pdfPath) ' All pages loaded automatically

            Dim result = ocr.Read(input)

            Dim outputPath As String = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(pdfPath) & "_searchable.pdf")

            result.SaveAsSearchablePdf(outputPath)
            Console.WriteLine($"Processed: {Path.GetFileName(pdfPath)} ({result.Pages.Length} pages)")
        End Using
    Next
End Sub
$vbLabelText   $csharpLabel

SaveAsSearchablePdf在PDF中嵌入了一個文字層,使其在文件管理系統中可索引,而不改變原始掃描的視覺外觀。 可搜索PDF指南涵蓋了文字層選項,而PDF OCR範例展示了端到端的掃描PDF處理。

Kofax OmniPage API至IronOCR映射參考

Kofax OmniPage IronOCR 等效
using Kofax.OmniPage.CSDK; using IronOcr;
using Kofax.OmniPageCSDK; using IronOcr;
using CSDK; using IronOcr;
new OmniPageEngine() 無需——無引擎物件
engine.SetLicenseFile(path) IronOcr.License.LicenseKey = "key";
engine.Initialize() 不需要
engine.Shutdown() 不需要
engine.CreateDocument() new OcrInput()
document.AddPage(imagePath) input.LoadImage(imagePath)
engine.OpenPDF(pdfPath) input.LoadPdf(pdfPath)
engine.OpenTiff(tiffPath) input.LoadImageFrames(tiffPath)
document.Recognize(settings) new IronTesseract().Read(input)
document.RecognizeAll(settings) new IronTesseract().Read(input) (全部頁面一次性)
document.GetText() result.Text
document.GetZoneText(zoneName) input.LoadImage(path, cropRectangle) + result.Text
document.Dispose() using var input = new OcrInput() (自動)
iterator.GetText() result.Pages[n].Words[m].Text
iterator.GetBoundingBox() result.Pages[n].Words[m].X / Y / Width / Height
iterator.GetConfidence() result.Pages[n].Words[m].Confidence
engine.LoadLanguageDictionary("German") dotnet add package IronOcr.Languages.German
settings.PrimaryLanguage = "English" ocr.Language = OcrLanguage.English;
settings.SecondaryLanguages = new[] {"German"} ocr.Language = OcrLanguage.English + OcrLanguage.German;
settings.DeskewImage = true input.Deskew();
settings.DespeckleLevel = 2 input.DeNoise();
settings.ContrastEnhancement = true input.Contrast();
settings.AutoRotate = true input.Deskew(); (包括旋轉校正)
document.SaveAs(path, outputSettings) result.SaveAsSearchablePdf(path)
OutputFormat.SearchablePDF result.SaveAsSearchablePdf(path)
OutputFormat.XML (帶坐標) result.Pages / .Paragraphs / .Words 物件圖
FormZone 帶坐標邊界 new CropRectangle(x, y, width, height)
每頁授權計數 不適用
.lic 文件部署 不適用
授權伺服器配置 不適用

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

問題1:生產中找不到授權文件的例外

Kofax OmniPage: 每次部署都需要.lic文件存在於對應於應用程式流程可存取的特定路徑。 若部署管道未明確將授權文件複製到目標伺服器,則在引擎初始化時導致LicenseException。 安全團隊經常會標記在容器鏡像中嵌入.lic文件或將其提交到源程式碼控制。

解決方案: IronOCR從字串中讀取其授權。 金鑰儲存為環境變數,並在啟動時讀取——無文件需部署,無路徑需配置:

// At application startup — reads IRONOCR_LICENSE_KEY from environment
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
    ?? throw new InvalidOperationException("IronOCR license key not configured.");
// At application startup — reads IRONOCR_LICENSE_KEY from environment
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
    ?? throw new InvalidOperationException("IronOCR license key not configured.");
Imports System

' At application startup — reads IRONOCR_LICENSE_KEY from environment
IronOcr.License.LicenseKey = If(Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY"), Throw New InvalidOperationException("IronOCR license key not configured."))
$vbLabelText   $csharpLabel

在Docker中,傳遞--env IRONOCR_LICENSE_KEY=YOUR-KEY。 在Azure App Service中,將其設為應用設置。 在Kubernetes中,通過Secret進行注入。 無文件掛載,無路徑配置,無嵌入式授權文件需進行安全審查。

問題2:過程崩潰後的浮動授權座位鎖定

Kofax OmniPage: 當應用程式流程崩潰,被監視器終止,或通過engine.Shutdown()不會執行。 浮動授權座位保持結賬狀態,直到授權伺服器的超時過期——通常為30–60分鐘。 在一個豆莢經常重啟的容器化環境中,此狀況會耗盡授權池。

解決方案:IronOCR沒有已 結賬的授權座位的概念。 過程崩潰不會釋放資源,也不會鎖定外部系統。 下一個過程立即啟動,無需等待座位可用:

// IronOCR: Process crash has no license implications whatsoever
// Start processing immediately after any failure
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("document.jpg"); //不是seat to check out
// IronOCR: Process crash has no license implications whatsoever
// Start processing immediately after any failure
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("document.jpg"); //不是seat to check out
Imports IronOcr

' IronOCR: Process crash has no license implications whatsoever
' Start processing immediately after any failure
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim result = New IronTesseract().Read("document.jpg") '不是seat to check out
$vbLabelText   $csharpLabel

對於彈性的ASP.NET Core服務,請參考異步OCR指南以獲得與託管服務和優雅關閉緊密整合的模式。

問題3:Docker映像製作失敗——安裝程式無法以非互動方式運行

Kofax OmniPage: OmniPage SDK安裝程式是一個GUI或半互動安裝程式,無法在docker build環境中良好運行。 試圖將SDK嵌入到容器映像中的團隊會在安裝程式的授權協議或組件選擇步驟中遇到失敗。 變通方法(靜默安裝開關,預提取DLL副本)未文件化且版本特定。

解決方案: IronOCR的Dockerfile是三行:

FROM mcr.microsoft.com/dotnet/aspnet:8.0
RUN apt-get update && apt-get install -y libgdiplus  # Single Linux dependency
COPY --from=build /app/publish /app
WORKDIR /app
ENTRYPOINT ["dotnet", "YourApp.dll"]

libgdiplus是唯一的系統依賴。IronOCRNuGet包由dotnet restore在構建階段內參照還原,如同其他包引用。 Docker部署指南涵蓋了Debian和Alpine基本映像,Linux部署指南則涵蓋了分發特定的包名稱。

問題4:虛擬機遷移或雲自動擴展後需要重新激活

Kofax OmniPage: OmniPage激活與硬體身份連結。 移動虛擬機於物理主機之間、在新實例中自動擴展,或用新鏡像替換容器的雲環境都會觸發需要重新激活的硬體身份變更。 在事件進行中聯絡Tungsten支援以重新激活會增加操作風險。

解決方案: IronOCR的授權金鑰與硬體無關。 相同的金鑰可在任何機器、任何容器、任何雲區域、任何授權階層內自動擴展實例使用。 無需重新激活,無需聯絡支援,無停機:

// Identical startup code on any hardware topology
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
bool valid = IronOcr.License.IsLicensed; // Verify without a network call
// Identical startup code on any hardware topology
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
bool valid = IronOcr.License.IsLicensed; // Verify without a network call
' Identical startup code on any hardware topology
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim valid As Boolean = IronOcr.License.IsLicensed ' Verify without a network call
$vbLabelText   $csharpLabel

問題5:RecognitionSettings物件在IronOCR中無對應

Kofax OmniPage: OmniPage根據每個文件使用PreprocessingSettings配置引擎行為。 遷移的團隊期望在IronOCR中有一個並行配置物件。

解決方案: IronOCR將配置拆分為兩個表面:IronTesseract上的引擎設置。 無設置物件需要例化:

// OmniPage settings object →IronOCRmethod calls on OcrInput
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.English;              // RecognitionSettings.Language
// ocr.Configuration.TesseractVersion = ...     // Engine version already optimized

using var input = new OcrInput();
input.LoadImage(imagePath);
input.Deskew();                                  // settings.DeskewImage = true
input.DeNoise();                                 // settings.DespeckleLevel = 2
input.Contrast();                                // settings.ContrastEnhancement = true

var result = ocr.Read(input);
// OmniPage settings object →IronOCRmethod calls on OcrInput
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.English;              // RecognitionSettings.Language
// ocr.Configuration.TesseractVersion = ...     // Engine version already optimized

using var input = new OcrInput();
input.LoadImage(imagePath);
input.Deskew();                                  // settings.DeskewImage = true
input.DeNoise();                                 // settings.DespeckleLevel = 2
input.Contrast();                                // settings.ContrastEnhancement = true

var result = ocr.Read(input);
Imports IronOcr

' OmniPage settings object → IronOCR method calls on OcrInput
Dim ocr As New IronTesseract()
ocr.Language = OcrLanguage.English ' RecognitionSettings.Language
' ocr.Configuration.TesseractVersion = ... ' Engine version already optimized

Using input As New OcrInput()
    input.LoadImage(imagePath)
    input.Deskew() ' settings.DeskewImage = true
    input.DeNoise() ' settings.DespeckleLevel = 2
    input.Contrast() ' settings.ContrastEnhancement = true

    Dim result = ocr.Read(input)
End Using
$vbLabelText   $csharpLabel

影像品質校正指南列出了所有可用的預處理方法,並提供有關哪些過濾器適用於哪些文件品質配置的指南。

問題6:無法直接移植區域模板文件

Kofax OmniPage: OmniPage表單模板在專有的.fpf模板文件中儲存區域定義,該文件定義了字段位置、識別型別和每個文件類別的驗證規則。 這些文件無法由IronOCR導入。

解決方案: 從模板文件中提取坐標資料(它們是XML格式的),並將每個區域轉換為CropRectangle。 字段名稱將變成字典鍵; 區域坐標直接映射到(x, y, width, height)參數:

// Convert OmniPage zone definition toIronOCRCropRectangle
// OmniPage zone: Name="TotalDue" X="490" Y="612" Width="180" Height="28"
//IronOCRequivalent:
var totalDueRegion = new CropRectangle(490, 612, 180, 28);

using var input = new OcrInput();
input.LoadImage(invoicePath, totalDueRegion);
string totalDue = new IronTesseract().Read(input).Text.Trim();
// Convert OmniPage zone definition toIronOCRCropRectangle
// OmniPage zone: Name="TotalDue" X="490" Y="612" Width="180" Height="28"
//IronOCRequivalent:
var totalDueRegion = new CropRectangle(490, 612, 180, 28);

using var input = new OcrInput();
input.LoadImage(invoicePath, totalDueRegion);
string totalDue = new IronTesseract().Read(input).Text.Trim();
Imports IronOcr

' Convert OmniPage zone definition to IronOCR CropRectangle
' OmniPage zone: Name="TotalDue" X="490" Y="612" Width="180" Height="28"
' IronOCR equivalent:
Dim totalDueRegion As New CropRectangle(490, 612, 180, 28)

Using input As New OcrInput()
    input.LoadImage(invoicePath, totalDueRegion)
    Dim totalDue As String = New IronTesseract().Read(input).Text.Trim()
End Using
$vbLabelText   $csharpLabel

對於區域在每頁變化的文件,使用不同的CropRectangle值分區載入相同圖像,而不是重複載入文件。區域裁切範例展示了來自單一圖像源的多區域讀取。

Kofax OmniPage遷移檢查表

遷移前

識別程式碼庫中所有的OmniPage整合點:

# Find all OmniPage namespace references
grep -r "Kofax\|OmniPage\|CSDK" --include="*.cs" --include="*.csproj" .

# Find engine lifecycle calls
grep -r "Initialize\|Shutdown\|SetLicenseFile" --include="*.cs" .

# Find document and zone creation patterns
grep -r "CreateDocument\|AddPage\|FormZone\|RecognitionSettings\|PreprocessingSettings" --include="*.cs" .

# Find output format configuration
grep -r "OutputSettings\|OutputFormat\|SaveAs\|GetText" --include="*.cs" .

# Find license file path references
grep -r "\.lic\|license\.lic\|licensePath" --include="*.cs" --include="*.config" --include="*.json" .
# Find all OmniPage namespace references
grep -r "Kofax\|OmniPage\|CSDK" --include="*.cs" --include="*.csproj" .

# Find engine lifecycle calls
grep -r "Initialize\|Shutdown\|SetLicenseFile" --include="*.cs" .

# Find document and zone creation patterns
grep -r "CreateDocument\|AddPage\|FormZone\|RecognitionSettings\|PreprocessingSettings" --include="*.cs" .

# Find output format configuration
grep -r "OutputSettings\|OutputFormat\|SaveAs\|GetText" --include="*.cs" .

# Find license file path references
grep -r "\.lic\|license\.lic\|licensePath" --include="*.cs" --include="*.config" --include="*.json" .
SHELL

在開始前清點:

  • 統計有OmniPage命名空間導入的文件數量
  • 列出所有FormZone定義,並提取其坐標資料
  • 確定載入了哪些語言字典並映射到IronOcr.Languages.* NuGet包
  • 注明使用的輸出格式(可搜索的PDF、純文字、XML)及其在IronOCR中的對應
  • 在部署腳本和配置中找到所有的.lic文件引用

程式碼遷移

  1. 從所有.csproj文件中移除OmniPage DLL引用
  2. 在每個執行OCR的項目中運行dotnet add package IronOcr
  3. 為每個使用中的語言新增語言包:dotnet add package IronOcr.Languages.[Language]
  4. 在每個應用程式入口點新增IronOcr.License.LicenseKey = ...;(Program.cs、Startup.cs或服務主機)
  5. using CSDK;和相關命名空間導入
  6. 移除所有的Initialize調用
  7. 移除所有IDisposable實現
  8. new OcrInput() + engine.CreateDocument() + document.AddPage()
  9. engine.OpenPDF() + 每頁迭代
  10. engine.OpenTiff() + 每幀迴圈
  11. document.Recognize(settings)
  12. CropRectangle(x, y, width, height)實例
  13. 用區域input.LoadImage(path, cropRectangle) + document.GetZoneText()
  14. 將可搜索的PDF的result.SaveAsSearchablePdf(path)
  15. result.Pages / .Paragraphs / .Words屬性遍歷替換結果迭代器模式
  16. 移除input.Contrast()方法調用替換布林標誌
  17. 從部署腳本、配置文件和基礎設施即程式碼範本中移除授權文件路徑

遷移後

  • 在現場語料庫中從100多個代表性文件的樣本中核對OCR準確性——逐行與OmniPage輸出比較發票、合同和表單
  • 確認基於CropRectangle的區域提取返回文字匹配OmniPage GetZoneText()輸出對應的每個映射字段
  • 測試多頁PDF處理:驗證頁數、頁面順序和文字連貫性
  • 測試多幀TIFF處理:確認所有幀已被處理且幀序列得以保留
  • 驗證可搜索的PDF輸出在使用的文件管理系統中可被索引(SharePoint、OpenText等)
  • 運行50多個並行文件的並行處理測試,確認沒有執行緒安全性問題
  • 測試語言包覆蓋範圍:通過IronOcr.Languages.*載入每個以前使用的語言字典並驗證識別質量
  • 確認過程崩潰和容器重啟不需要任何授權恢復行動
  • 在CI代理上運行Docker生成——驗證dotnet restore解決IronOCR,無需安裝程式步驟
  • 驗證信心水準可在每個單詞上取得,並符合下游驗證工作流程的資料質量期望
  • 檢查應用程式在任何路徑中缺少.lic文件時能正常啟動

遷移至IronOCR的主要好處

部署複雜度從幾周下降到幾分鐘。以前需要SDK安裝程式存取、dotnet restore部署。 新的開發人員克隆了程式碼庫並運行了該項目。 新的生產伺服器由CI/CD管道提供。容器映像生成不需要安裝程式步驟。

授權風險完全消失。 沒有浮動席位可用盡,沒有結帳超時需等待,沒有重新激活的硬體指紋,沒有在部署管道中保護的.lic文件。 凌晨3點的應用崩潰不會釋放任何東西,也不會鎖定任何東西。 現場值班工程師重新啟動過程; OCR立即重新運行。 IronOCR產品頁面涵蓋了所有授權層次。

跨平台部署成為標準NuGet目標。 macOS開發機器可正常運行,無平台異常。 Linux生產伺服器不需要2026年1月的SDK版本。 Docker容器從標準mcr.microsoft.com/dotnet/aspnet基本映像構建,僅需一個系統依賴。 同樣的.csproj中生成了一個能在Windows、Linux和macOS上工作的構建。 請參見Azure部署指南AWS部署指南以獲取雲端特定的配置。

並行吞吐量隨硬體擴展。 OmniPage的共享引擎架構需要鎖定以序列化並發識別。 IronOCR的無狀態IronTesseract實例隨可用CPU核心數線性擴展。 雙倍增加批量處理伺服器的核心數量可以在不更改配置或增加授權的情況下加倍吞吐量。

結構化資料存取是即時的。 Characters將完整的文件結構作為一個可導航的.NET物件圖暴露。 每個單詞物件上都有信心水準和坐標屬性。 無需二次導出管道、格式配置和無需文件輸出即可存取位置資料。

成本固定且可預測。 OmniPage的SDK授權、年度維護和按頁執行時費用,創造了一個隨使用而擴展且每年復發的成本。 IronOCR的$999–$2,999永久是一次性購買。 一個每年處理500,000頁的工作流程會在幾周內補足IronOCR授權成本。 IronOCR文件中心教程可無需支援合同存取。

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

常見問題

為什麼我應該從 Kofax OmniPage 轉移到 IronOCR?

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

從 Kofax OmniPage 遷移到 IronOCR 時的主要程式碼變更是什麼?

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

如何安裝IronOCR以開始遷移?

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

IronOCR 能否比肩 Kofax OmniPage 在標準業務文件的 OCR 準確性?

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

IronOCR 如何處理 Kofax OmniPage 依賴單獨安裝的語言資料?

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

從 Kofax OmniPage 遷移到 IronOCR 是否需要更改部署基礎設施?

IronOCR 所需的基礎設施更改比 Kofax OmniPage 要少。沒有 SDK 二進位路徑、授權檔案放置或授權伺服器配置。NuGet 封裝包含完整的 OCR 引擎,並且授權金鑰是應用程式程式碼中設定的字串。

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

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

IronOCR 能否像 Kofax OmniPage 一樣處理 PDF?

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

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

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

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

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

IronOCR 的定價是否比 Kofax OmniPage 在擴展工作負載上更可預測?

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

從 Kofax OmniPage 遷移到 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天。
聊天
電子郵件
給我打電話