IRONSOFTWAREHOME
影片

如何在 .NET Maui 中執行 OCR

Kannaopat Udonpant
Kannapat Udonpant
Updated: 2026年6月28日

本指引為 .NET 開發者提供完整的LEADTOOLS OCR遷移至 IronOCR 之過程。 涵蓋從 NuGet 套件替換到完整的程式碼遷移,並針對 LEADTOOLS 的初始化程式、多重命名空間結構和基於文件的授權部署模型,提供前後例子。

為什麼要從LEADTOOLS OCR遷移

LEADTOOLS OCR 內含一個企業成像平台,其根源可追溯至1990年,其 API 反映了此一血統。 最低工作配置需要四個 NuGet 套件、四個命名空間、兩個已知路徑下的實體授權檔案、一個在引擎之前初始化的編解碼層、從三個選項中選擇一個引擎型別,以及一個載入運行時二進制檔至記憶體的阻塞啟動呼叫。 所有這些都會在識別單個字元之前運行。 遷移至IronOCR的團隊消除了整個層級——只需一個套件、一個命名空間、一行程式碼來設定授權金鑰。

基於文件的授權部署在容器中失效。 LEADTOOLS 需要兩個實體檔案——LEADTOOLS.LICLEADTOOLS.LIC.KEY——需在每台運行應用程式的機器上的特定路徑中可讀取。 在 Docker 容器中,這些文件必須烘焙進映像(在層次歷史中暴露它們)或者在運行時掛載(在每個協同環境中需要音量協調)。 Azure Functions 和 AWS Lambda 無法不使用變通方法進行授權文件部署。 CI/CD 管道需要在啟動呼叫使用的確切路徑上存在這些文件,否則應用程式會在處理單個文件之前拋出錯誤。IronOCR將這兩個文件替換為可放入環境變數、Kubernetes 密碼或 Azure Key Vault 引用中的字串。

四個套件完成一項任務。 一個正常運行的LEADTOOLS OCR專案至少需要 Leadtools.CodecsLeadtools.Forms.DocumentWriters。 受密碼保護的 PDF 支持需要一個可能未在購買的捆綁包中包含的額外 Leadtools.Pdf 模組。 每個套件必須存在、相容且版本一致。IronOCR以單一 NuGet 套件提供。 包括所有功能——原生 PDF 輸入、可搜索 PDF 輸出、預處理、條碼閱讀。

引擎生命週期建立了一個維護表面。 LEADTOOLS 將 OCR 引擎包裝在一個 IDisposable 服務類中,而不是因為符合 .NET 慣例,而是因為 Shutdown() 呼叫必須在 Dispose() 之前進行,否則應用程式會產生錯誤。 LEADTOOLS 批次處理器的生產實現通常在文件塊間包含 GC.Collect() / GC.WaitForPendingFinalizers() 呼叫,以補償積累的 RasterImage 實例。IronOCR使用標準 using 區塊。 OcrInput 是唯一需要處置的物件。

捆綁混淆在生產中出現。 LEADTOOLS 不公開發佈定價。 包含 OCR 的文件成像 SDK 估計每年每名開發人員花費 3,000 至 8,000 美元。 購買了沒有 PDF 模組的 OCR 模組的團隊在生產中發現對擁有密碼保護文件的 RasterSupport.IsLocked() 返回 true 時,發現了此空白。 OmniPage 引擎準確度層級需要在 LEADTOOLS 購買之上另簽 Kofax 授權協議。IronOCR授權所有功能在每個層級。 沒有次要供應商關係,沒有加密文件的單獨模組。

初始化成本影響冷啟動。 engine.Startup() 是一個阻塞呼叫,將運行時文件載入記憶體。 在無伺服器環境中,冷啟動延遲在意義下- Azure Functions, AWS Lambda - 一個 500–2000 毫秒的阻塞初始化在任何識別工作發生之前形成了一個結構性問題。IronOCR使用延遲初始化。 IronTesseract 實例在第一次使用時初始化,且同一個程式的後續呼叫都不會再支付初始化成本。

根本問題

LEADTOOLS 需要四個命名空間,四個 NuGet 套件,以及一個強制性啟動程式確定後才識別字元:

// LEADTOOLS: Four namespaces, four packages, six steps before recognition
using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;
using Leadtools.Forms.DocumentWriters;

RasterSupport.SetLicense(licPath, File.ReadAllText(keyPath)); // Step 1: two files on disk
var codecs = new RasterCodecs();                              // Step 2: codec layer
var engine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD); // Step 3: engine factory
engine.Startup(codecs, null, null, runtimePath);              // Step 4: blocking startup
// ... still need to create document, add page, call Recognize(), extract text
C#
// IronOCR: One namespace, one package, one line
using IronOcr;

IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var text = new IronTesseract().Read("document.jpg").Text;
C#

IronOCR vs LEADTOOLS OCR:功能比較

下列表格直接映射這兩個程式庫間的功能。

功能LEADTOOLS OCRIronOCR
所需的NuGet包至少 4(PDF 更加)1 (IronOcr)
授權機制.LIC + .LIC.KEY 文件配對字串密鑰
許可部署每台生產機上的文件環境變數或配置
價格模型$3,000–$15,000+/個開發者/年(估計)$999–$2,399 一次性永久
引擎初始化手動 Startup() 和運行路徑自動,延遲
引擎關閉手動 Shutdown()Dispose() 之前不需要
編解碼層RasterCodecs 需要用于所有影像載入不需要
PDF輸入逐頁光柵化迴圈原生 LoadPdf()
受密碼保護的 PDF需要獨立 Leadtools.Pdf 模組內建於 Password 參數
可搜尋的PDF輸出DocumentWriter + PdfDocumentOptions + document.Save()result.SaveAsSearchablePdf()
預處理單獨的指令類 (DeskewCommand, DespeckleCommand等等)內建過濾方法於 OcrInput
多頁TIFF手動逐幀迭代使用 CodecsLoadByteOrderinput.LoadImageFrames()
結構化輸出頁面和區域級別頁面、段落、行、字、字元和坐標
信心評估OcrPageRecognizeStatus 列舉result.Confidence 以百分比顯示
條碼識別單獨的 LEADTOOLS 條形碼模組內建在 (ReadBarCodes = true)
執行緒安全需要小心管理完整的(每個執行緒一個 IronTesseract
支持的語言60–120(引擎依賴)125+ 通過 NuGet 語言包
語言部署tessdata files or engine-bundled files每種語言一個 NuGet 套件
跨平臺通過每個平臺的運行時配置支持Windows、Linux、macOS、Docker、Azure、AWS
Docker部署.KEY 文件必須掛載或烘焙標準 dotnet publish
商業支持

快速入門:從LEADTOOLS OCR到IronOCR的遷移

步驟1:替換NuGet包

删除所有 LEADTOOLS 包:

dotnet remove package Leadtools
dotnet remove package Leadtools.Ocr
dotnet remove package Leadtools.Codecs
dotnet remove package Leadtools.Forms.DocumentWriters
dotnet remove package Leadtools.Pdf
SHELL

NuGet安裝IronOCR:

dotnet add package IronOcr

步驟2:更新命名空間

将所有 LEADTOOLS using 指令替換爲單個IronOCR導入:

// Before (LEADTOOLS)
using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;
using Leadtools.Forms.DocumentWriters;

// After (IronOCR)
using IronOcr;
C#

步驟3:初始化許可證

刪除所有 RasterSupport.SetLicense() 呼叫和 .LIC / .LIC.KEY 文件引用。 在應用啟動時加入IronOCR授權金鑰:

// Single line replaces the entire file-based license setup
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";

// Production pattern: pull from environment variable or secrets manager
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
C#

授權金鑰可以儲存在任何標準 .NET 秘密管理模式 - appsettings.json,Azure Key Vault,AWS Secrets Manager,或 Kubernetes 密碼。 無需將任何文件與應用程式二進制文件一起部署。

程式碼遷移範例

引擎啟動和關閉生命周期移除

LEADTOOLS 強制一個嚴格的服務類模式,因爲引擎生命週期必須顯式管理。 構造函式啓動引擎,Dispose() 以正確的順序關閉引擎,任何程式碼路徑在 Shutdown() 之前跳過 Dispose() 將生成運行時錯誤。

LEADTOOLS OCR 方法:

using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;

// Service class required purely to manage engine lifecycle
public class LeadtoolsOcrService : IDisposable
{
    private IOcrEngine _engine;
    private RasterCodecs _codecs;
    private readonly string _runtimePath;

    public LeadtoolsOcrService(string licPath, string keyPath, string runtimePath)
    {
        _runtimePath = runtimePath;

        // License setup — two files, both must be present
        RasterSupport.SetLicense(licPath, File.ReadAllText(keyPath));

        // Codec layer — required before engine creation
        _codecs = new RasterCodecs();

        // Engine factory — engine type determines capability and cost
        _engine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD);

        // Blocking startup — loads runtime into memory (500–2000ms)
        _engine.Startup(_codecs, null, null, _runtimePath);
    }

    public bool IsReady => _engine?.IsStarted ?? false;

    public string Process(string imagePath)
    {
        if (!IsReady)
            throw new InvalidOperationException("Engine not started");

        using var image = _codecs.Load(imagePath);
        using var doc = _engine.DocumentManager.CreateDocument();
        var page = doc.Pages.AddPage(image, null);
        page.Recognize(null);
        return page.GetText(-1);
    }

    public void Dispose()
    {
        // Order is mandatory: Shutdown before Dispose
        if (_engine?.IsStarted == true)
            _engine.Shutdown();

        _engine?.Dispose();
        _codecs?.Dispose();
    }
}
C#

IronOCR方法:

using IronOcr;

// No lifecycle management needed — the service class becomes trivial
public class OcrService
{
    private readonly IronTesseract _ocr = new IronTesseract();

    // Always ready — no IsStarted check needed
    public string Process(string imagePath) => _ocr.Read(imagePath).Text;

    // No Dispose() needed for the engine
    // No Startup(), no Shutdown(), no codec layer
}
C#

IronTesseract 實例在首次使用時初始化。 沒有構造函式參數,沒有運行時路徑,沒有 Startup() 调用。 上述服務類無需實作 IDisposable——引擎是無狀態的,且每個 Read() 呼叫中使用的 OcrInput 物件透過標準 using 模式自行完成清除。 IronTesseract 設置指南 涵蓋了配置選項,包括語言選擇和生產場景的性能調整。

多幀 TIFF 批次處理

LEADTOOLS 通過查詢編解碼器總幀數來處理多幀 TIFF 文件,然後在每個 _codecs.Load() 調用上迴圈設置顯式的 lastPage 參數。 每一幀圖像都必須手動處理,否則記憶體會積累。

LEADTOOLS OCR 方法:

using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;

public class LeadtoolsTiffBatchService
{
    private readonly IOcrEngine _engine;
    private readonly RasterCodecs _codecs;

    public List<string> ProcessMultiFrameTiff(string tiffPath)
    {
        var pageTexts = new List<string>();

        // Must query page count before iterating
        var info = _codecs.GetInformation(tiffPath, true);
        int frameCount = info.TotalPages;

        using var document = _engine.DocumentManager.CreateDocument();

        for (int frameNum = 1; frameNum <= frameCount; frameNum++)
        {
            // Load one frame at a time — must specify firstPage/lastPage
            using var frameImage = _codecs.Load(
                tiffPath,
                0,                            // bitsPerPixel
                CodecsLoadByteOrder.BgrOrGray,
                frameNum,                     // firstPage
                frameNum);                    // lastPage

            var page = document.Pages.AddPage(frameImage, null);
            page.Recognize(null);
            pageTexts.Add(page.GetText(-1));

            // GC pressure accumulates if disposal is missed on any frame
        }

        return pageTexts;
    }

    public Dictionary<string, List<string>> ProcessTiffDirectory(string directoryPath)
    {
        var results = new Dictionary<string, List<string>>();

        foreach (var tiffFile in Directory.GetFiles(directoryPath, "*.tiff"))
        {
            results[tiffFile] = ProcessMultiFrameTiff(tiffFile);

            // Manual GC between files to prevent memory growth
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        return results;
    }
}
C#

IronOCR方法:

using IronOcr;

public class TiffBatchService
{
    private readonly IronTesseract _ocr = new IronTesseract();

    public List<string> ProcessMultiFrameTiff(string tiffPath)
    {
        using var input = new OcrInput();
        input.LoadImageFrames(tiffPath);  // All frames loaded automatically

        var result = _ocr.Read(input);

        // Per-frame text available through result.Pages
        return result.Pages.Select(p => p.Text).ToList();
    }

    public Dictionary<string, List<string>> ProcessTiffDirectory(string directoryPath)
    {
        var results = new Dictionary<string, List<string>>();

        foreach (var tiffFile in Directory.GetFiles(directoryPath, "*.tiff"))
        {
            results[tiffFile] = ProcessMultiFrameTiff(tiffFile);
        }

        return results;
    }

    // Parallel processing across files — thread-safe out of the box
    public Dictionary<string, List<string>> ProcessTiffDirectoryParallel(string directoryPath)
    {
        var concurrentResults = new System.Collections.Concurrent.ConcurrentDictionary<string, List<string>>();
        var tiffFiles = Directory.GetFiles(directoryPath, "*.tiff");

        Parallel.ForEach(tiffFiles, tiffFile =>
        {
            using var input = new OcrInput();
            input.LoadImageFrames(tiffFile);
            var result = new IronTesseract().Read(input);
            concurrentResults[tiffFile] = result.Pages.Select(p => p.Text).ToList();
        });

        return new Dictionary<string, List<string>>(concurrentResults);
    }
}
C#

LoadImageFrames() 在一次調用中讀取 TIFF 的所有幀。 無需幀計數查詢,無需迴圈,無需顯式逐幀處理。 並行版本建立一個每執行緒一個 IronTesseract 實例,這是正確的模式——完整的執行緒模型詳見 多執行緒範例。 對於 TIFF 特定的輸入選項,TIFF 和 GIF 輸入指南 涵蓋幀選擇和多格式處理。

文件編寫器管道簡化

LEADTOOLS 可搜索 PDF 建立需要在引擎上配置一個 DocumentWriter 實例,構建一個包含輸出型別和覆蓋設定的 PdfDocumentOptions 物件,通過 SetOptions() 應用選項,然後使用格式枚舉調用 document.Save()。 這些步驟中的每個都是一個單獨的物件和一個單獨的 API 調用。

LEADTOOLS OCR 方法:

using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;
using Leadtools.Forms.DocumentWriters;

public class LeadtoolsDocumentWriterService
{
    private readonly IOcrEngine _engine;
    private readonly RasterCodecs _codecs;

    public void CreateSearchablePdfFromImages(string[] imagePaths, string outputPath)
    {
        using var document = _engine.DocumentManager.CreateDocument();

        foreach (var imagePath in imagePaths)
        {
            using var image = _codecs.Load(imagePath);
            var page = document.Pages.AddPage(image, null);
            page.Recognize(null);
        }

        // DocumentWriter configuration — four properties to set before save
        var pdfOptions = new PdfDocumentOptions
        {
            DocumentType = PdfDocumentType.Pdf,
            ImageOverText = true,     // Image layer visible, text layer searchable
            Linearized = false,
            Title = "Searchable Output"
        };

        // Apply options to the engine's writer instance
        _engine.DocumentWriterInstance.SetOptions(DocumentFormat.Pdf, pdfOptions);

        // Save with format enum — the format must match the options set above
        document.Save(outputPath, DocumentFormat.Pdf, null);
    }

    public void CreateSearchablePdfFromPdf(string inputPdfPath, string outputPath)
    {
        var pdfInfo = _codecs.GetInformation(inputPdfPath, true);
        using var document = _engine.DocumentManager.CreateDocument();

        for (int i = 1; i <= pdfInfo.TotalPages; i++)
        {
            using var pageImage = _codecs.Load(inputPdfPath, 0,
                CodecsLoadByteOrder.BgrOrGray, i, i);

            var page = document.Pages.AddPage(pageImage, null);
            page.Recognize(null);
        }

        var pdfOptions = new PdfDocumentOptions
        {
            DocumentType = PdfDocumentType.Pdf,
            ImageOverText = true,
            Title = Path.GetFileNameWithoutExtension(inputPdfPath)
        };

        _engine.DocumentWriterInstance.SetOptions(DocumentFormat.Pdf, pdfOptions);
        document.Save(outputPath, DocumentFormat.Pdf, null);
    }
}
C#

IronOCR方法:

using IronOcr;

public class SearchablePdfService
{
    private readonly IronTesseract _ocr = new IronTesseract();

    public void CreateSearchablePdfFromImages(string[] imagePaths, string outputPath)
    {
        using var input = new OcrInput();
        foreach (var imagePath in imagePaths)
            input.LoadImage(imagePath);

        var result = _ocr.Read(input);
        result.SaveAsSearchablePdf(outputPath);  // DocumentWriter pipeline: gone
    }

    public void CreateSearchablePdfFromPdf(string inputPdfPath, string outputPath)
    {
        using var input = new OcrInput();
        input.LoadPdf(inputPdfPath);

        var result = _ocr.Read(input);
        result.SaveAsSearchablePdf(outputPath);
    }

    // Get bytes directly — useful for streaming responses in ASP.NET
    public byte[] CreateSearchablePdfBytes(string inputPdfPath)
    {
        using var input = new OcrInput();
        input.LoadPdf(inputPdfPath);
        return _ocr.Read(input).SaveAsSearchablePdfBytes();
    }
}
C#

SaveAsSearchablePdf() 替換整個 PdfDocumentOptions + SetOptions() + document.Save() 鏈。 圖像覆文字層行為是自動的。 如需完整的可搜索 PDF 輸出文件,可搜索 PDF 使用指南 涵蓋輸出選項,可搜索 PDF 範例 展示如何整合 ASP.NET 響應流。

多區域字段提取迁移

LEADTOOLS 基於區域的 OCR 使用 OcrZone 物件與 LeadRect 邊界、OcrZoneTypeOcrZoneCharacterFilters 屬性。 多個區域被新增至單一頁面,在一次 page.Recognize() 呼叫中被識別,然後通過區域索引提取。 區域索引匹配插入順序,意味著提取迴圈必須保持此排序。

LEADTOOLS OCR 方法:

using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;

public class LeadtoolsFormFieldExtractor
{
    private readonly IOcrEngine _engine;
    private readonly RasterCodecs _codecs;

    // Invoice field extraction using named zones
    public InvoiceFields ExtractInvoiceFields(string invoicePath)
    {
        using var image = _codecs.Load(invoicePath);
        using var document = _engine.DocumentManager.CreateDocument();
        var page = document.Pages.AddPage(image, null);

        // Must clear auto-detected zones before adding custom ones
        page.Zones.Clear();

        // Zone definitions — index order matters for extraction
        var zoneDefinitions = new[]
        {
            new { Name = "InvoiceNumber", X = 450, Y = 80,  W = 200, H = 30 },
            new { Name = "InvoiceDate",   X = 450, Y = 115, W = 200, H = 30 },
            new { Name = "VendorName",    X = 50,  Y = 150, W = 300, H = 40 },
            new { Name = "TotalAmount",   X = 450, Y = 600, W = 200, H = 30 }
        };

        foreach (var def in zoneDefinitions)
        {
            var zone = new OcrZone
            {
                Bounds = new LeadRect(def.X, def.Y, def.W, def.H),
                ZoneType = OcrZoneType.Text,
                CharacterFilters = OcrZoneCharacterFilters.None,
                RecognitionModule = OcrZoneRecognitionModule.Auto
            };
            page.Zones.Add(zone);
        }

        page.Recognize(null);

        // Extract by index — must match insertion order exactly
        return new InvoiceFields
        {
            InvoiceNumber = page.Zones[0].Text?.Trim(),
            InvoiceDate   = page.Zones[1].Text?.Trim(),
            VendorName    = page.Zones[2].Text?.Trim(),
            TotalAmount   = page.Zones[3].Text?.Trim()
        };
    }
}

public class InvoiceFields
{
    public string InvoiceNumber { get; set; }
    public string InvoiceDate   { get; set; }
    public string VendorName    { get; set; }
    public string TotalAmount   { get; set; }
}
C#

IronOCR方法:

using IronOcr;

public class FormFieldExtractor
{
    private readonly IronTesseract _ocr = new IronTesseract();

    // Each field gets its own CropRectangle-scoped Read() call
    // No zone index management, no zone ordering dependency
    public InvoiceFields ExtractInvoiceFields(string invoicePath)
    {
        return new InvoiceFields
        {
            InvoiceNumber = ReadRegion(invoicePath, 450, 80,  200, 30),
            InvoiceDate   = ReadRegion(invoicePath, 450, 115, 200, 30),
            VendorName    = ReadRegion(invoicePath, 50,  150, 300, 40),
            TotalAmount   = ReadRegion(invoicePath, 450, 600, 200, 30)
        };
    }

    private string ReadRegion(string imagePath, int x, int y, int width, int height)
    {
        using var input = new OcrInput();
        input.LoadImage(imagePath, new CropRectangle(x, y, width, height));
        return _ocr.Read(input).Text.Trim();
    }

    // Batch: extract the same field from many invoices in parallel
    public Dictionary<string, string> ExtractInvoiceNumbersBatch(string[] invoicePaths)
    {
        var results = new System.Collections.Concurrent.ConcurrentDictionary<string, string>();

        Parallel.ForEach(invoicePaths, invoicePath =>
        {
            using var input = new OcrInput();
            input.LoadImage(invoicePath, new CropRectangle(450, 80, 200, 30));
            results[invoicePath] = new IronTesseract().Read(input).Text.Trim();
        });

        return new Dictionary<string, string>(results);
    }
}
C#

CropRectangle 直接傳遞給 LoadImage() 替換整個 OcrZone 設置。沒有需要跟踪的區域索引,沒有需要的 page.Zones.Clear() 通話,也不需要識別狀態檢查。 基於區域的 OCR 指南 涵蓋單區域和多區域提取模式。 完整的賬單字段提取教程請參閱發票 OCR 教程

帶有詞座標的結構化資料提取

LEADTOOLS 結構化輸出運行於頁面和區域層級。 要獲得帶有邊界框坐標的字級資料,開發人員需從識別出的區域中存取 OcrWord 物件。 API 需要在識別後通過區域集合操作,每區域迭代單詞列表。

LEADTOOLS OCR 方法:

using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;

public class LeadtoolsStructuredExtractor
{
    private readonly IOcrEngine _engine;
    private readonly RasterCodecs _codecs;

    public List<WordLocation> ExtractWordsWithLocations(string imagePath)
    {
        var wordLocations = new List<WordLocation>();

        using var image = _codecs.Load(imagePath);
        using var document = _engine.DocumentManager.CreateDocument();
        var page = document.Pages.AddPage(image, null);
        page.Recognize(null);

        // Access words through the zone collection
        foreach (OcrZone zone in page.Zones)
        {
            foreach (OcrWord word in zone.Words)
            {
                wordLocations.Add(new WordLocation
                {
                    Text       = word.Value,
                    X          = word.Bounds.X,
                    Y          = word.Bounds.Y,
                    Width      = word.Bounds.Width,
                    Height     = word.Bounds.Height,
                    Confidence = word.Confidence
                });
            }
        }

        return wordLocations;
    }
}

public class WordLocation
{
    public string Text       { get; set; }
    public int    X          { get; set; }
    public int    Y          { get; set; }
    public int    Width      { get; set; }
    public int    Height     { get; set; }
    public int    Confidence { get; set; }
}
C#

IronOCR方法:

using IronOcr;

public class StructuredExtractor
{
    private readonly IronTesseract _ocr = new IronTesseract();

    public List<WordLocation> ExtractWordsWithLocations(string imagePath)
    {
        var result = _ocr.Read(imagePath);

        // Five-level hierarchy: Pages > Paragraphs > Lines > Words > Characters
        return result.Pages
            .SelectMany(page => page.Paragraphs)
            .SelectMany(para => para.Lines)
            .SelectMany(line => line.Words)
            .Select(word => new WordLocation
            {
                Text       = word.Text,
                X          = word.X,
                Y          = word.Y,
                Width      = word.Width,
                Height     = word.Height,
                Confidence = (int)word.Confidence
            })
            .ToList();
    }

    // Paragraph-level extraction with position data
    public void PrintDocumentStructure(string imagePath)
    {
        var result = _ocr.Read(imagePath);
        Console.WriteLine($"Document confidence: {result.Confidence}%");

        foreach (var page in result.Pages)
        {
            Console.WriteLine($"Page {page.PageNumber}:");
            foreach (var paragraph in page.Paragraphs)
            {
                Console.WriteLine($"  Paragraph at ({paragraph.X}, {paragraph.Y}):");
                Console.WriteLine($"  {paragraph.Text}");
            }
        }
    }
}
C#

IronOCR 的結果層次結構從 Pages 經過 WordsCharacters。 每一級別暴露 TextConfidence。 LEADTOOLS 的基於區域的存取模式消失了——無需區域迭代即可達到文字資料。 讀取結果使用指南 涵蓋了具有座標存取模式的完整輸出模型。 OcrResult API 參考 記錄了結果層次結構的每個屬性。

LEADTOOLS OCR API 到IronOCR映射參考

LEADTOOLS OCRIronOCR
RasterSupport.SetLicense(licPath, keyContent)IronOcr.License.LicenseKey = "key"
new RasterCodecs()不需要
OcrEngineManager.CreateEngine(OcrEngineType.LEAD)new IronTesseract()
engine.Startup(codecs, null, null, runtimePath)不需要
engine.IsStarted不需要(總是準備好)
engine.Shutdown()不需要
engine.Dispose()不需要
_codecs.Load(imagePath)input.LoadImage(imagePath)
_codecs.Load(path, 0, BgrOrGray, page, page)input.LoadPdf(path)input.LoadImageFrames(path)
_codecs.GetInformation(path, true).TotalPages不需要——自動
engine.DocumentManager.CreateDocument()不需要
document.Pages.AddPage(image, null)input.LoadImage(imagePath)
page.Recognize(null)ocr.Read(input)(識別是 Read() 的一部分)
page.GetText(-1)result.Text
page.RecognizeStatusresult.Confidence(百分比)
OcrZone { Bounds = new LeadRect(x, y, w, h) }new CropRectangle(x, y, w, h)
page.Zones.Clear()不需要
page.Zones.Add(zone)input.LoadImage(path, cropRect)
zone.Words / word.Boundsresult.Pages[n].Words / word.X, word.Y
DeskewCommand().Run(image)input.Deskew()
DespeckleCommand().Run(image)input.DeNoise()
AutoBinarizeCommand().Run(image)input.Binarize()
ContrastBrightnessCommand().Run(image)input.Contrast()
new PdfDocumentOptions { ImageOverText = true }SaveAsSearchablePdf()自動處理
engine.DocumentWriterInstance.SetOptions(format, opts)不需要
document.Save(path, DocumentFormat.Pdf, null)result.SaveAsSearchablePdf(path)
_codecs.Options.Pdf.Load.Password = passwordinput.LoadPdf(path, Password: password)
RasterSupport.IsLocked(RasterSupportType.Document)IronOcr.License.IsLicensed

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

問題1:許可證文件路徑解析失敗

LEADTOOLS:RasterSupport.SetLicense() 解析 .LIC.LIC.KEY 文件路徑相對於工作目錄,此工作目錄在 bin/Release、Docker 容器和 IIS 應用程式池之間有所不同。 在開發中有效但在生產中因工作目錄變化而產生 "License file not found" 的路径是常见故障模式。

**解決方案:**完整刪除兩個許可證文件和 SetLicense() 調用。 用從環境變數讀取的單個字串分配替換:

// Remove this:
// RasterSupport.SetLicense(licPath, File.ReadAllText(keyPath));

// Replace with this:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
    ?? throw new InvalidOperationException("IRONOCR_LICENSE environment variable not set");
C#

在任何環境中字串密鑰的行爲完全相同。 將其儲存在已用於資料庫連接字串的相同祕密管理器中。

問題2:引擎未啟動異常

**LEADTOOLS:**在 engine.Startup() 完成之前或在 engine.Shutdown() 被調用后(例如,在關閉期間的處理結束前置用和使用競賽狀況所發生)調用任何識別方法,會擲出"引擎未啟動"InvalidOperationException。長命服務類必須防範此狀況並進行 IsStarted 檢查。

解決方案:IronTesseract 不需要啟動呼叫,並且沒有已啓/未啓的狀態。 IsStarted 防護和整個生命週期服務類可以被刪除:

// Remove the guard:
// if (!_engine.IsStarted)
//     throw new InvalidOperationException("Engine not started");

// IronTesseract is always ready — just call Read()
var result = _ocr.Read(imagePath);
C#

問題3:批量處理時的 RasterImage 記憶體積累

**LEADTOOLS:**無論哪個程式碼路徑沒有正確處理 RasterImage 實例——由於在 using 區塊退出之前擲出的異常或在缺少手動調用處置模式中——未釋放的影像會堆積在記憶中。 LEADTOOLS 的生產程式碼通常在批次之間包含 GC.Collect() / GC.WaitForPendingFinalizers() 呼叫作為補償機制。

**解決方案:**請刪除所有 GC.Collect() 呼叫。 OcrInput 是IronOCR管道中的唯一 IDisposable,其範疇僅限於每批次操作的標準 using 區塊:

// Remove this pattern:
// GC.Collect();
// GC.WaitForPendingFinalizers();

// Replace with standard using scope:
foreach (var filePath in filePaths)
{
    using var input = new OcrInput();
    input.LoadImage(filePath);
    var text = _ocr.Read(input).Text;
    // input disposed here — no accumulation
}
C#

有關更多記憶體優化指導,請參閱記憶體分配減少部落格

問題4:DocumentWriterInstance 選項在調用之間持續存在

LEADTOOLS: engine.DocumentWriterInstance.SetOptions() 修改了共享引擎寫入實例的狀態。 如果一條路徑使用 DocumentType = PdfDocumentType.PdfA 設置了 PdfDocumentOptions,而後續調用在 document.Save() 之前未重置這些選項,前一次呼叫的輸出格式將持續保存。 這在共享引擎實例上是一個有狀態的副效應。

**解決方案:**IronOCR沒有共享的寫入器狀態。 每次 SaveAsSearchablePdf() 调用是独立的:

// Remove the options setup:
// var pdfOptions = new PdfDocumentOptions { ... };
// _engine.DocumentWriterInstance.SetOptions(DocumentFormat.Pdf, pdfOptions);
// document.Save(outputPath, DocumentFormat.Pdf, null);

// Replace with:
result.SaveAsSearchablePdf(outputPath);
C#

每次調用產生一個標準的 PDF 輸出,其中包含圖像重疊和可搜索的文字層。 在呼叫之間沒有共享選項狀態需要重置。

問題5:錯誤配置包—運行時缺少模組

**LEADTOOLS:**購買了文件成像 SDK 的團隊可能發現 Leadtools.Pdf 型別不可用,或者加密的 PDF 頁面在生產中運行時拋出含有 RasterExceptionCode.FeatureNotSupportedRasterException。 這發生在已購買的捆綁包不包括 PDF 模塊時,錯誤僅在生產中運行時纔會出現。

**解決方案:**IronOCR所有許可層都有全部功能。 沒有單獨的 PDF 模塊,沒有加密文件的附加程式,也沒有高精度引擎的次級供應商。安裝單個 IronOcr 套件後,所有功能集隨即可用,無需額外購買:

dotnet add package IronOcr

問題6:區域索引在重新排序後不匹配

**LEADTOOLS:**區域提取使用位置索引 - page.Zones[1].Text - 將提取邏輯與 page.Zones.Add() 中的插入順序綁定。 重新排序區域定義以匹配更改的表格佈局會靜默破壞提取,因為所有隨後的索引都被移動了。

**解決方案:**IronOCR使用帶有每個字段 CropRectangle 的命名變數。 重新排序區字段定義對提取沒有影響,因爲每個字段是獨立範圍的:

// Each field is independent — reorder freely without breaking extraction
var invoiceNumber = ReadRegion(imagePath, 450, 80,  200, 30);
var invoiceDate   = ReadRegion(imagePath, 450, 115, 200, 30);
var vendorName    = ReadRegion(imagePath, 50,  150, 300, 40);
var totalAmount   = ReadRegion(imagePath, 450, 600, 200, 30);
C#

LEADTOOLS OCR遷移檢查清單

遷移前

在更改任何程式碼之前,審覈程式碼庫以識別所有 LEADTOOLS 使用:

# Find all LEADTOOLS namespace imports
grep -rn "using Leadtools" --include="*.cs" .

# Find engine lifecycle calls
grep -rn "OcrEngineManager\|\.Startup(\|\.Shutdown()" --include="*.cs" .

# Find license file references
grep -rn "SetLicense\|LEADTOOLS\.LIC\|\.LIC\.KEY" --include="*.cs" .

# Find RasterCodecs usage
grep -rn "RasterCodecs\|_codecs\.Load\|GetInformation" --include="*.cs" .

# Find DocumentWriter usage
grep -rn "DocumentWriterInstance\|PdfDocumentOptions\|DocumentFormat\." --include="*.cs" .

# Find zone-based OCR
grep -rn "OcrZone\|page\.Zones\|LeadRect\|ZoneType" --include="*.cs" .

# Find GC workarounds to remove
grep -rn "GC\.Collect\|WaitForPendingFinalizers" --include="*.cs" .
SHELL

注意購買的 LEADTOOLS 套餐 - OCR 模塊、PDF 模塊和引擎型別(LEAD vs Tesseract vs OmniPage) - 確保在遷移後驗證期間測試等效的IronOCR功能。

程式碼遷移

  1. 删除所有 LEADTOOLS NuGet 包:Leadtools.Pdf
  2. 安裝 IronOcr NuGet 套件
  3. 使用 using IronOcr; 替換所有 LEADTOOLS using 指令
  4. 從項目和部署工件中刪除 .LIC.LIC.KEY 文件
  5. 在應用啟動時用 IronOcr.License.LicenseKey = "key" 替換 RasterSupport.SetLicense(licPath, keyContent)
  6. 刪除所有僅用於管理 IOcrEngineRasterCodecs 生命周期的 IDisposable 服務類
  7. new IronTesseract() 替換 OcrEngineManager.CreateEngine() + engine.Startup()
  8. using var input = new OcrInput() 區塊內用 input.LoadImage(imagePath) 替換 _codecs.Load(imagePath)
  9. input.LoadImageFrames(tiffPath) 替換多幀 TIFF 頁面迴圈
  10. input.LoadPdf(pdfPath) 替代 PDF 頁面迭代迴圈
  11. ocr.Read(input).Text 替代 document.Pages.AddPage() + page.Recognize(null) + page.GetText(-1)
  12. input.LoadImage(path, new CropRectangle(x, y, w, h)) 替換 OcrZone + page.Zones.Add() 模式
  13. result.SaveAsSearchablePdf(path) 替代 PdfDocumentOptions + DocumentWriterInstance.SetOptions() + document.Save()
  14. 使用 input.DeNoise()input.Binarize() 代替預處理命令類 (DeskewCommand, DespeckleCommand, AutoBinarizeCommand) 在 OcrInput 實例上
  15. 刪除爲補償 LEADTOOLS 記憶體管理而新增的所有 GC.Collect() / GC.WaitForPendingFinalizers() 调用

遷移後

  • 驗證已識別的文字輸出是否與 LEADTOOLS 輸出一致,並對代表性樣本的圖像和 PDF 進行測試
  • 使用 result.Confidence 確認信心水準在預期範圍內
  • 測試多幀 TIFF 處理是否產生與 LEADTOOLS 幀迭代迴圈相同數量的頁面
  • 驗證可搜索的 PDF 輸出能否在 PDF 閱讀器(Adobe Acrobat 或同類)中進行文字搜索
  • 根據生產發票或表單中的已知良好字段值測試基於區域的字段提取
  • 確認密碼保護的 PDF 解密無需單獨的 Leadtools.Pdf 模块即可工作
  • 在高負載下進行批量處理,確認沒有記憶體增長(先移除所有 GC.Collect()
  • 無需許可證文件的 Docker 和 CI/CD 部署測試- 確認字串許可密鑰從環境變數正確解析
  • 使用 Parallel.ForEach 進行並行處理的測試,以驗證執行緒安全性
  • 確認結構化資料提取 (result.Pages, page.Paragraphs, page.Words) 返回正確的坐標

遷移至IronOCR的主要好處

部署變得無狀態。 LEADTOOLS .LIC.LIC.KEY 文件是每個部署環境必須攜帶的工件。 將其烘焙進的容器在鏡像歷史中暴露許可資料。 與之掛載的容器需要音量協調。 遷移到IronOCR之後,許可證是一個環境變數中的字串。 部署工件是一個標準的 NuGet 參考。 沒有文件,沒有路徑,沒有安裝策略。 適合容器化環境完整設置的 Docker 部署指南Azure 部署指南

**四個套件合為一體。**遷移將 Leadtools.Forms.DocumentWriters 和可選的 Leadtools.Pdf 減少到一個 IronOcr 引用。 所有能力——預處理、原生 PDF 輸入、可搜索 PDF 輸出、條碼閱讀、結構化資料提取、125+ 語言支持——都包括在這個一個包中。 功能設置不依賴於購買了哪個包。

批處理消除手動記憶體管理。 LEADTOOLS 批次碼處載防禦 GC.Collect() 調用和顯式 RasterImage 清除以防止多文件執行期間記憶體積累。IronOCR的 OcrInput 受標準 using 區塊範疇管理,自動處理清理。 使用 Parallel.ForEach 進行的以執行緒安全爲基礎的並行處理 - 每個執行緒一個 IronTesseract 實例 - 提供多核吞吐量,無需同步程式碼。 查看速度優化指南中的生產吞吐量調優。

**可預見的總成本。**五名開發人員團隊的 LEADTOOLS 估計成本在第一年花費 15,000–40,000 美元,每年約 20–25% 的許可證成本用於接收更新。IronOCRProfessional 以 2,999 美元的一次性永久購買覆蓋十名開發人員和十個部署地點。 一年更新期包括在內。 在更新期之後繼續使用無需額外支付。 IronOCR 授權頁面 直接發佈所有層價格,無需銷售協商。

結構化資料無需區域設置。 經過遷移後,具有邊界框座標的字元級、行間級和段落級資料直接可在 OcrResult 上獲取 —— 無需區域定義。 五級層次結構 (Pages, Paragraphs, Lines, Words, Characters) 各自暴露位置座標和每個元素的信心水準。 需要 LEADTOOLS 區域配置以實現結構化提取的應用能夠從簡單的 API 中獲得更加豐富的資料。 OCR 結果功能頁 總結了完整輸出模型。

[(Adobe Acrobat、Kofax OmniPage、LEADTOOLS 和 Tesseract 是各自所有者的註冊商標。 本網站與 Adobe Inc.、Apryse、Google、Kofax 或 LEAD Technologies 無關,也未受其認可或贊助。 所有產品名稱、標誌和品牌均為其各自所有者的財產。 比較僅供資訊用途,並反映撰寫時獲得的公開資訊。)}]

相關文章

Key in blue circle

立即免費取得 30 天試用金鑰

Your trial license will be sent to your email address

無任何限制。100% 解鎖。無需信用卡。

bullet_checked無需信用卡或建立帳號無任何限制。100% 解鎖。無需信用卡。
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried IronPDF
獲取您的無義務諮詢
填寫以下表格或發送電子郵件至sales@ironsoftware.com
您的詳細資訊將始終保密。
被全球數百萬工程師信任
Iron Software的客戶標誌
立即獲取您的30天試用金鑰
無需信用卡或帳戶建立