IRONSOFTWAREHOME
FILMY

Jak przeprowadzić OCR w NET Maui

Kannaopat Udonpant
Kannapat Udonpant
Updated: 28 czerwca 2026

Niniejszy przewodnik przeprowadza programistów .NET przez proces pełnej migracji zLEADTOOLS OCRdo IronOCR. Obejmuje on każdy etap, od wymiany pakietu NuGet po pełną migrację kodu, wraz z przykładami "przed i po" dla wzorców najbardziej dotkniętych procedurą inicjalizacji LEADTOOLS, strukturą wielu przestrzeni nazw oraz modelem wdrażania licencji opartym na plikach.

Dlaczego warto przejść z LEADTOOLS OCR

LEADTOOLS OCR jest dostarczany w ramach korporacyjnej platformy obrazówania, której korzenie sięgają 1990 roku, a API odzwierciedla tę tradycję. Minimalna konfiguracja robocza wymaga czterech pakietów NuGet, czterech przestrzeni nazw, dwóch fizycznych plików licencji wdrożonych w znanej ścieżce, warstwy kodeków zainicjowanej przed silnikiem, wyboru typu silnika spośród trzech opcji oraz blokującego wywołania startowego, które ładuje pliki binarne środowiska uruchomieniowego do pamięci. Wszystko to dzieje się, zanim rozpoznany zostanie choćby jeden znak. Zespoły przechodzące na IronOCR eliminują całą tę warstwę — jeden pakiet, jedna przestrzeń nazw, jedna linijka kodu do ustawienia klucza licencyjnego.

Oparty na plikach rozlokowanie licencji ulega przerwaniu w kontenerach. LEADTOOLS wymaga dwóch fizycznych plików — LEADTOOLS.LIC i LEADTOOLS.LIC.KEY — dostępnych do odczytu w określonej ścieżce na każdej maszynie uruchamiającej aplikację. W kontenerach Docker pliki te muszą być albo wbudowane w obraz (co powoduje ich ujawnienie w historii warstw), albo zamontowane w czasie wykonywania (co wymaga koordynacji woluminów w każdym środowisku orkiestracji). Azure Functions i AWS Lambda nie posiadają mechanizmu wdrażania plików licencyjnych bez stosowania rozwiązań alternatywnych. Pipeline'y CI/CD wymagają, aby pliki znajdowały się dokładnie w ścieżce używanej przez wywołanie startowe, w przeciwnym razie aplikacja zgłosi błąd przed przetworzeniem jakiegokolwiek dokumentu.IronOCR zastępuje oba pliki ciągiem znaków, który mieści się w zmiennej środowiskowej, sekrecie Kubernetes lub odwołaniu do Azure Key Vault.

Cztery pakiety dla jednego zadania. Działający projekt OCR LEADTOOLS wymaga co najmniej Leadtools, Leadtools.Ocr, Leadtools.Codecs i Leadtools.Forms.DocumentWriters. Obsługa PDF zabezpieczonych hasłem wymaga dodatkowego modułu Leadtools.Pdf, który może nie być zawarty w zakupionej paczce. Każdy pakiet musi być obecny, kompatybilny i posiadać wspólną wersję.IronOCR jest dostarczany jako jeden pakiet NuGet. Uwzględniono wszystkie funkcje — natywne wprowadzanie plików PDF, generowanie plików PDF z możliwością wyszukiwania, przetwarzanie wstępne, odczyt kodów BarCode.

Cykl życia silnika tworzy powierzchnię konserwacyjną. LEADTOOLS otacza silnik OCR w klasie serwisowej IDisposable, nie dla idiomatycznych powodów .NET, lecz ponieważ wywołanie Shutdown() musi poprzedzać Dispose(), inaczej aplikacja wywołuje błędy. Produkcyjna implementacja przetwarzaczy wsadowych LEADTOOLS zazwyczaj obejmuje wywołania GC.Collect() / GC.WaitForPendingFinalizers() pomiędzy częściami dokumentów, aby zrekompensować gromadzenie się instancji RasterImage.IronOCR używa standardowych bloków using. OcrInput to jedyny obiekt wymagający zlikwidowania.

W środowisku produkcyjnym pojawiają się problemy z pakietami. Firma LEADTOOLS nie publikuje cen. Pakiet Document Imaging SDK — poziom obejmujący OCR — kosztuje szacunkowo 3000–8000 USD rocznie na programistę. Zespoły, które zakupiły moduł OCR bez modułu PDF, odkrywają lukę, gdy RasterSupport.IsLocked() zwraca true w odniesieniu do dokumentów zabezpieczonych hasłem w produkcji. Poziom dokładności silnika OmniPage wymaga oddzielnej umowy licencyjnej Kofax oprócz zakupu LEADTOOLS.IronOCR udziela licencji na wszystkie funkcje na każdym poziomie. Nie ma żadnych powiązań z dostawcami zewnętrznymi, nie ma też oddzielnego modułu do obsługi dokumentów zaszyfrowanych.

Koszt inicjalizacji wpływa na zimne starty. engine.Startup() to blokujące wywołanie, które ładuje pliki wykonawcze do pamięci. W środowiskach bezserwerowych, w których liczy się opóźnienie przy zimnym starcie — Azure Functions, AWS Lambda — blokująca inicjalizacja trwająca 500–2000 ms przed rozpoczęciem jakichkolwiek działań rozpoznawczych stanowi problem strukturalny.IronOCR wykorzystuje leniwą inicjalizację. Instancja IronTesseract inicjalizuje się przy pierwszym użyciu, a kolejne wywołania w tym samym procesie nie ponoszą żadnego kosztu inicjalizacji.

Podstawowy problem

LEADTOOLS wymaga czterech przestrzeni nazw, czterech pakietów NuGet oraz obowiązkowej procedury uruchamiania przed rozpoznaniem znaku:

// 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 a LEADTOOLS OCR: porównanie funkcji

Poniższa tabela przedstawia bezpośrednie powiązania między funkcjami obu bibliotek.

FunkcjaLEADTOOLS OCRIronOCR
Wymagane pakiety NuGetMinimum 4 strony (więcej w przypadku pliku PDF)1 (IronOcr)
Mechanizm licencji.LIC + .LIC.KEY para plikówKlucz ciągu znaków
Wdrażanie licencjiPliki na każdym komputerze produkcyjnymZmienna środowiskowa lub konfiguracja
Model cenowy3 000–15 000 USD+/programista/rok (szacunkowo)$999–2 999 USD jednorazowy
Inicjalizacja silnikaRęczne Startup() z ścieżką wykonawcząAutomatyczne, leniwe
Wyłączenie silnikaRęczne Shutdown() przed Dispose()Nie jest wymagane
Warstwa kodekówRasterCodecs wymagane dla wszystkich ładowań obrazówNie jest wymagane
Plik wejściowy PDFPętla rasteryzacji strona po stronieWbudowany parametr Password
Plik PDF chroniony hasłemWymaga oddzielnego modułu Leadtools.PdfWbudowany parametr Password
Wynik w formacie PDF z możliwością wyszukiwaniaDocumentWriter + PdfDocumentOptions + document.Save()result.SaveAsSearchablePdf()
Przetwarzanie wstępneOddzielne klasy komend (DeskewCommand, DespeckleCommand, itd.)Wbudowane metody filtracji na OcrInput
Wielostronicowy plik TIFFRęczna iteracja klatek przy użyciu CodecsLoadByteOrderinput.LoadImageFrames()
Strukturalny wynikPoziom strony i strefyStrona, akapit, wiersz, słowo, znak z współrzędnymi
Ocena pewnościOcrPageRecognizeStatus enumresult.Confidence jako procent
Odczytywanie BarCodeOddzielny moduł BarCode LEADTOOLSWbudowany (ReadBarCodes = true)
Bezpieczeństwo wątkówWymaga starannego zarządzaniaPełny (jeden IronTesseract na wątek)
Obsługiwane języki60–120 (w zależności od silnika)Ponad 125 pakietów językowych dostępnych za pośrednictwem NuGet
Wdrożenie językowetessdata files or engine-bundled filesPakiet NuGet dla każdego języka
WielopłatformoweObsługa konfiguracji środowiska uruchomieniowego dla poszczególnych platformWindows, Linux, macOS, Docker, Azure, AWS
Wdrożenie DockerPliki .KEY muszą być zamontowane lub dodane do obrazuStandard dotnet publish
Wsparcie komercyjneTakTak

Szybki start: Migracja zLEADTOOLS OCRdo IronOCR

Krok 1: Zastąp pakiet NuGet

Usuń wszystkie pakiety 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

Zainstaluj IronOCR z NuGet:

dotnet add package IronOcr

Krok 2: Aktualizacja przestrzeni nazw

Zastąp wszystkie dyrektywy LEADTOOLS using jednym importem IronOCR:

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

// After (IronOCR)
using IronOcr;
C#

Krok 3: Inicjalizacja licencji

Usuń wszystkie wywołania RasterSupport.SetLicense() oraz referencje do plików .LIC / .LIC.KEY. Dodaj klucz licencyjny IronOCR podczas uruchamiania aplikacji:

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

Klucz licencyjny może być przechowywany w dowolnym standardowym wzorcu zarządzania sekretami .NET — appsettings.json, Azure Key Vault, AWS Secrets Manager lub sekret Kubernetes. Nie ma potrzeby wdrażania żadnych plików wraz z plikiem binarnym aplikacji.

Przykłady migracji kodu

Uruchamianie i wyłączanie silnika Usunięcie cyklu życia

LEADTOOLS wymaga ścisłego przestrzegania wzorca klasy usługowej, ponieważ cykl życia silnika musi być zarządzany w sposób jawny. Konstruktor uruchamia silnik, Dispose() wyłącza go w odpowiedniej kolejności, a każdy kod, który pomija Shutdown() przed Dispose(), wywołuje błąd wykonania.

Podejście 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#

Podejście 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#

Instancja IronTesseract inicjalizuje się przy pierwszym użyciu. Brak argumentów konstruktora, brak ścieżki wykonawczej, brak wywołania Startup(). Powyższa klasa serwisowa w ogóle nie musi implementować IDisposable — silnik jest bezstanowy, a obiekty OcrInput używane wewnątrz każdego wywołania Read() zajmują się własnym czyszczeniem przez standardowy wzorzec using. Podręcznik konfiguracji IronTesseract obejmuje opcje konfiguracyjne, w tym wybór języka i optymalizację wydajności dla scenariuszy produkcyjnych.

Wieloklatkowe przetwarzanie partii plików TIFF

LEADTOOLS przetwarza pliki TIFF z wieloma ramkami, pobierając wszystkie ramki z kodera, a następnie iterując z explicytnymi parametrami lastPage przy każdym wywołaniu _codecs.Load(). Każdy obraz ramki musi zostać usunięty ręcznie, w przeciwnym razie pamięć będzie się zapełniać.

Podejście 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#

Podejście 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() odczytuje wszystkie ramki z TIFFu w jednym wywołaniu. Bez zapytań o liczbę klatek, bez pętli, bez jawnego usuwania po każdej klatce. Wersja równoległa tworzy jedną instancję IronTesseract na wątek, co jest prawidłowym wzorcem — patrz przykład wielowątkowości dla pełnego modelu wątków. W przypadku opcji wejściowych specyficznych dla formatu TIFF, przewodnik dotyczący formatów TIFF i GIF obejmuje wybór klatek oraz obsługę wielu formatów.

Uproszczenie procesu tworzenia dokumentacji

Tworzenie PDFów z możliwością wyszukiwania w LEADTOOLS wymaga skonfigurowania instancji DocumentWriter na silniku, skonstruowania obiektu PdfDocumentOptions z typem wyjściowym i ustawieniami nakładki, zastosowania opcji przez SetOptions(), a następnie wywołania document.Save() z formatem enum. Każdy z tych kroków jest oddzielnym obiektem i oddzielnym wywołaniem API.

Podejście 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#

Podejście 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() zastępuje cały łańcuch PdfDocumentOptions + SetOptions() + document.Save(). Warstwa obrazu nakładająca się na tekst działa automatycznie. Aby uzyskać pełną dokumentację dotyczącą plików PDF z funkcją wyszukiwania, zapoznaj się z przewodnikiem dotyczącym plików PDF z funkcją wyszukiwania, który omawia opcje wyjściowe, oraz przykładem pliku PDF z funkcją wyszukiwania, który pokazuje integrację ze strumieniami odpowiedzi ASP.NET.

Migracja ekstrakcji pól wielostrefowych

OCR oparte na strefach w LEADTOOLS używa obiektów OcrZone z granicami LeadRect, atrybutami OcrZoneType i właściwościami OcrZoneCharacterFilters. Wiele stref jest dodawanych do jednej strony i rozpoznawanych w jednym wywołaniu page.Recognize(), a następnie ekstraktowane według indeksu stref. Indeks stref odpowiada kolejności wstawiania, co oznacza, że pętla wyodrębniania musi zachować tę kolejność.

Podejście 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#

Podejście 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 przekazywane bezpośrednio do LoadImage() zastępuje całą konfigurację OcrZone. Nie ma potrzeby śledzenia indeksu strefy, nie jest wymagane wywołanie page.Zones.Clear(), ani nie jest potrzebna kontrola statusu rozpoznania. Przewodnik po OCR oparty na regionach obejmuje wzorce ekstrakcji jedno- i wieloregionalnej. Aby zapoznać się z kompletnym samouczkiem dotyczącym wyodrębniania pól z faktur, zobacz samouczek dotyczący OCR faktur.

Pobieranie danych ustrukturyzowanych za pomocą współrzędnych słów WORD

Strukturalne dane wyjściowe LEADTOOLS działają na poziomie strony i strefy. Aby uzyskać dane na poziomie słów z współrzędnymi ramki ograniczającej, programista uzyskuje dostęp do obiektów OcrWord wewnątrz rozpoznawanej strefy. API wymaga przetworzenia kolekcji stref po rozpoznaniu i iteracji listy słów dla każdej strefy.

Podejście 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#

Podejście 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#

Hierarchia wyników IronOCR schodzi od Pages przez Paragraphs, Lines, Words i Characters. Każdy poziom udostępnia X, Y, Width, Height, Text i Confidence. Wzorzec dostępu oparty na strefach LEADTOOLS znika — nie jest wymagana iteracja stref, aby uzyskać dostęp do danych WORD. Przewodnik "Wyniki odczytu" obejmuje kompletny model wyjściowy wraz ze schematami dostępu do współrzędnych. Dokumentacja API OcrResult opisuje każdą właściwość w hierarchii wyników.

Dokumentacja APILEADTOOLS OCRdo IronOCR

LEADTOOLS OCRIronOCR
RasterSupport.SetLicense(licPath, keyContent)IronOcr.License.LicenseKey = "key"
new RasterCodecs()Nie jest wymagane
OcrEngineManager.CreateEngine(OcrEngineType.LEAD)new IronTesseract()
engine.Startup(codecs, null, null, runtimePath)Nie jest wymagane
engine.IsStartedNie jest wymagane (zawsze gotowe)
engine.Shutdown()Nie jest wymagane
engine.Dispose()Nie jest wymagane
_codecs.Load(imagePath)input.LoadImage(imagePath)
_codecs.Load(path, 0, BgrOrGray, page, page)input.LoadPdf(path) lub input.LoadImageFrames(path)
_codecs.GetInformation(path, true).TotalPagesNie jest wymagane — automatyczne
engine.DocumentManager.CreateDocument()Nie jest wymagane
document.Pages.AddPage(image, null)input.LoadImage(imagePath)
page.Recognize(null)ocr.Read(input) (rozpoznanie jest częścią Read())
page.GetText(-1)result.Text
page.RecognizeStatusresult.Confidence (procent)
OcrZone { Bounds = new LeadRect(x, y, w, h) }new CropRectangle(x, y, w, h)
page.Zones.Clear()Nie jest wymagane
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 }Obsługiwane automatycznie przez SaveAsSearchablePdf()
engine.DocumentWriterInstance.SetOptions(format, opts)Nie jest wymagane
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

Typowe problemy związane z migracją i ich rozwiązania

Problem 1: Błędy rozpoznawania ścieżki do pliku licencji

LEADTOOLS: RasterSupport.SetLicense() rozwiązuje ścieżki plików .LIC i .LIC.KEY względem katalogu roboczego, co różni się w bin/Debug, bin/Release, kontenerach Docker i pulach aplikacji IIS. Typowym trybem awarii jest ścieżka, która działa podczas rozwoju, ale wywołuje "License file not found" w produkcji, ponieważ katalog roboczy uległ zmianie.

Rozwiązanie: Usuń oba pliki licencyjne i całkowicie usuń wywołanie SetLicense(). Zastąp pojedynczym przypisaniem ciągu znaków, które odczytuje wartość ze zmiennej środowiskowej:

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

Klucz ciągu znaków zachowuje się identycznie w każdym środowisku. Zapisz to w tym samym menedżerze sekretów, który jest już używany dla ciągów połączeń z bazą danych.

Problem 2: Wyjątek "Silnik nie został uruchomiony"

LEADTOOLS: Wywołanie dowolnej metody rozpoznania przed ukończeniem engine.Startup(), lub po wywołaniu engine.Shutdown() (na przykład w sytuacji wyścigu wymuszania usunięcia przed użyciem podczas zamykania), wywołuje InvalidOperationException z "Silnik nie został uruchomiony." Trwałe klasy serwisowe muszą się przed tym zabezpieczyć przy pomocy IsStarted.

Rozwiązanie: IronTesseract nie wymaga wywołania startowego i nie ma statusu rozpoczęcia/zatrzymania. Blokada IsStarted i cała klasa serwisowa cyklu życia mogą zostać usunięte:

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

Problem 3: Gromadzenie pamięci przez RasterImage podczas przetwarzania wsadowego

LEADTOOLS: Przetwarzanie wsadowe iterujące po stronach PDF lub plikach obrazów tworzy instancje RasterImage w pętli. Jeśli jakakolwiek ścieżka kodu nie zlikwiduje RasterImage — z powodu wyjątku wyrzuconego przed zakończeniem bloku using, lub przekazywania manualnego z usuniętym wywołaniem — niezlikwidowane obrazy gromadzą się w pamięci. Kod produkcyjny LEADTOOLS zazwyczaj obejmuje wywołania GC.Collect() / GC.WaitForPendingFinalizers() między wsadami jako mechanizm kompensujący.

Rozwiązanie: Usuń wszystkie wywołania GC.Collect(). OcrInput to jedyny IDisposable w potoku IronOCR, i jest przypisany do każdej operacji wsadowej ze standardowym blokiem 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#

Aby uzyskać dodatkowe wskazówki dotyczące optymalizacji pamięci, zapoznaj się z wpisem na blogu poświęconym redukcji alokacji pamięci.

Problem 4: Opcje DocumentWriterInstance są zachowywane między wywołaniami

LEADTOOLS: engine.DocumentWriterInstance.SetOptions() modyfikuje stan na współdzielonej instancji piszącej silnika. Jeżeli jedna ze ścieżek kodu ustawi PdfDocumentOptions z DocumentType = PdfDocumentType.PdfA, a kolejne wywołanie nie zresetuje tych opcji przed wywołaniem document.Save(), format wyjściowy z poprzedniego wywołania zostaje utrzymany. Jest to efekt uboczny z zachowaniem stanu w współdzielonej instancji silnika.

**Rozwiązanie:**IronOCR nie posiada współdzielonego stanu pisania. Każde wywołanie SaveAsSearchablePdf() jest niezależne:

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

Każde wywołanie generuje standardowy plik PDF z nakładką graficzną i warstwą tekstową z możliwością wyszukiwania. Nie ma wspólnego stanu opcji, który można by zresetować między wywołaniami.

Problem 5: Nieprawidłowy pakiet — brakujący moduł w czasie wykonywania

LEADTOOLS: Zespoły, które zakupiły SDK obrazowania dokumentów, mogą stwierdzić, że typy Leadtools.Pdf są niedostępne, lub że zaszyfrowane strony PDF wyrzucają RasterException z RasterExceptionCode.FeatureNotSupported. Dzieje się tak, gdy zakupiony pakiet nie zawiera modułu PDF, a błąd ujawnia się dopiero w czasie wykonywania w środowisku produkcyjnym.

**Rozwiązanie:**IronOCR zawiera wszystkie funkcje na każdym poziomie licencji. Nie ma oddzielnego modułu PDF, dodatku dla zaszyfrowanych dokumentów ani drugiego dostawcy dla silnika z wyższą dokładnością. Po zainstalowaniu pojedynczego pakietu IronOcr, pełen zestaw funkcji jest dostępny bez żadnych dodatkowych zakupów:

dotnet add package IronOcr

Problem 6: Niezgodność indeksu stref po zmianie kolejności

LEADTOOLS: Ekstrakcja stref używa indeksowania pozycyjnego — page.Zones[0].Text, page.Zones[1].Text — co wiąże logikę ekstrakcji z kolejnością wstawiania w page.Zones.Add(). Zmiana kolejności definicji stref w celu dopasowania do zmienionego układu formularza powoduje ciche przerwanie ekstrakcji poprzez przesunięcie wszystkich kolejnych indeksów.

**Rozwiązanie:**IronOCR używa nazwanych zmiennych z CropRectangle na pole. Zmiana kolejności definicji pól nie ma wpływu na ekstrakcję, ponieważ zakres każdego pola jest niezależny:

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

Lista kontrolna migracji LEADTOOLS OCR

Przed migracją

Przed wprowadzeniem jakichkolwiek zmian należy przeprowadzić audyt kodu źródłowego w celu zidentyfikowania wszystkich miejsc użycia 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

Należy odnotować, który pakiet LEADTOOLS został zakupiony — moduł OCR, moduł PDF oraz typ silnika (LEAD vs Tesseract vs OmniPage) — aby zapewnić przetestowanie równoważnych funkcji IronOCR podczas walidacji po migracji.

Migracja kodu

  1. Usuń wszystkie pakiety NuGet LEADTOOLS: Leadtools, Leadtools.Ocr, Leadtools.Codecs, Leadtools.Forms.DocumentWriters, Leadtools.Pdf
  2. Zainstaluj pakiet NuGet IronOcr
  3. Zastąp wszystkie dyrektywy LEADTOOLS using z using IronOcr;
  4. Usuń z projektu i artefaktów wdrożeniowych pliki .LIC i .LIC.KEY
  5. Zastąp RasterSupport.SetLicense(licPath, keyContent) z IronOcr.License.LicenseKey = "key" podczas uruchamiania aplikacji
  6. Usuń wszystkie klasy serwisowe IDisposable, które istnieją wyłącznie do zarządzania cyklem życia IOcrEngine i RasterCodecs
  7. Zastąp OcrEngineManager.CreateEngine() + engine.Startup() z new IronTesseract()
  8. Zastąp _codecs.Load(imagePath) z input.LoadImage(imagePath) wewnątrz bloku using var input = new OcrInput()
  9. Zastąp pętle stron TIFF z wieloma ramkami używając input.LoadImageFrames(tiffPath)
  10. Zastąp pętlę iteracji po stronach PDF używając input.LoadPdf(pdfPath)
  11. Zastąp document.Pages.AddPage() + page.Recognize(null) + page.GetText(-1) z ocr.Read(input).Text
  12. Zastąp wzorce OcrZone + page.Zones.Add() z input.LoadImage(path, new CropRectangle(x, y, w, h))
  13. Zastąp PdfDocumentOptions + DocumentWriterInstance.SetOptions() + document.Save() z result.SaveAsSearchablePdf(path)
  14. Zastąp klasy komend przetwarzania wstępnego (DeskewCommand, DespeckleCommand, AutoBinarizeCommand) z input.Deskew(), input.DeNoise(), input.Binarize() na instancji OcrInput
  15. Usuń wszystkie wywołania GC.Collect() / GC.WaitForPendingFinalizers() dodane w celu rekompensaty zarządzania pamięcią LEADTOOLS

Po migracji

  • Sprawdź, czy rozpoznany tekst wyjściowy odpowiada wynikom LEADTOOLS na reprezentatywnej próbce obrazów i plików PDF
  • Potwierdź, że wskaźniki zaufania mieszczą się w oczekiwanych zakresach, używając result.Confidence
  • Test przetwarzania plików TIFF z wieloma ramkami daje taką samą liczbę stron jak pętla iteracyjna ramek w LEADTOOLS
  • Sprawdź, czy plik PDF z możliwością wyszukiwania umożliwia wyszukiwanie tekstu w czytniku PDF (Adobe Acrobat lub jego odpowiednik)
  • Przetestuj ekstrakcję pól opartą na strefach w odniesieniu do sprawdzonych wartości pól z faktur lub formularzy produkcyjnych
  • Zweryfikuj, że rozszyfrowywanie PDF zabezpieczonych hasłem działa bez oddzielnego modułu Leadtools.Pdf
  • Uruchom przetwarzanie wsadowe pod obciążeniem i potwierdź brak wzrostu pamięci (najpierw usuń wszystkie GC.Collect())
  • Przetestuj wdrożenie Docker i CI/CD bez plików licencyjnych — sprawdź, czy klucz licencyjny typu string jest poprawnie rozpoznawany ze zmiennej środowiskowej
  • Przetestuj przetwarzanie równoległe z użyciem Parallel.ForEach, aby zweryfikować bezpieczeństwo wątków
  • Potwierdź, że ekstrakcja strukturalna danych (result.Pages, page.Paragraphs, page.Words) zwraca poprawne współrzędne

Kluczowe korzyści z migracji do IronOCR

Rozlokowanie staje się bezstanowe. Pliki LEADTOOLS .LIC i .LIC.KEY są artefaktami, które każde środowisko wdrożeniowe musi posiadać. Kontenery, w których są one wbudowane, ujawniają dane licencyjne w historii obrazów. Kontejnery, które je montują, wymagają koordynacji woluminów. Po migracji do IronOCR licencja jest ciągiem znaków w zmiennej środowiskowej. Artefaktem wdrożeniowym jest standardowe odwołanie NuGet. Żadnych plików, żadnych ścieżek, żadnej strategii montowania. Przewodnik wdrażania Docker oraz przewodnik wdrażania Azure przedstawiają kompletną konfigurację środowisk kontenerowych.

Cztery pakiety stają się jednym. Migracja redukuje Leadtools, Leadtools.Ocr, Leadtools.Codecs, Leadtools.Forms.DocumentWriters, i opcjonalnie Leadtools.Pdf do pojedynczej referencji IronOcr. Wszystkie funkcje — przetwarzanie wstępne, natywne dane wejściowe w formacie PDF, dane wyjściowe w formacie PDF z możliwością wyszukiwania, odczyt kodów BarCode, ekstrakcja danych strukturalnych, obsługa ponad 125 języków — są zawarte w tym jednym pakiecie. Zestaw funkcji nie zależy od tego, który pakiet został zakupiony.

Przetwarzanie wsadowe eliminuje ręczne zarządzanie pamięcią. Kod wsadowy LEADTOOLS zawiera defensywne wywołania GC.Collect() i explicytne zlikwidowanie RasterImage, aby zapobiec nagromadzeniu pamięci podczas uruchamiania wielu dokumentów. Zakres OcrInputIronOCR z blokiem using zarządza sprzątaniem automatycznie. Bezpieczne dla wątków przetwarzanie równoległe z Parallel.ForEach — jedna instancja IronTesseract na wątek — dostarcza przepustowość wielordzeniową bez kodu synchronizującego. Zapoznaj się z przewodnikiem po optymalizacji prędkości, aby dostroić przepustowość produkcji.

Przewidywalny całkowity koszt. Szacowany koszt LEADTOOLS dla pięcioosobowego zespołu programistów wynosi 15 000–40 000 USD w pierwszym roku, a roczna opłata za utrzymanie, wymagana do otrzymywania aktualizacji, wynosi około 20–25% kosztu licencji.IronOCR Professional w cenie 2999 USD obejmuje dziesięciu programistów i dziesięć lokalizacji wdrożeniowych w ramach jednorazowego zakupu na czas nieograniczony. W cenie zawarty jest roczny dostęp do aktualizacji. Dalsze korzystanie po upływie okresu aktualizacji nie wymaga dodatkowej opłaty. Strona licencyjna IronOCR publikuje wszystkie ceny bezpośrednio, bez konsultacji sprzedażowej.

Strukturalne dane bez konfiguracji stref. Po migracji, dane na poziomie słów, linii i akapitów z współrzędnymi ramki ograniczającej są dostępne bezpośrednio na OcrResult — nie są wymagane definicje stref. Pięciopoziomowa hierarchia (Pages, Paragraphs, Lines, Words, Characters) ujawnia współrzędne pozycji i wskaźniki zaufania na element. Aplikacje, które wymagały konfiguracji stref LEADTOOLS w celu uzyskania ustrukturyzowanego wyciągu, otrzymują bogatsze dane z prostszego interfejsu API. Strona z wynikami OCR zawiera podsumowanie kompletnego modelu wyjściowego.

Zwróć uwagę: Adobe Acrobat, Kofax OmniPage, LEADTOOLS, i Tesseract są zarejestrowanymi znakami towarowymi ich właścicieli. Ta strona nie jest powiązana, wspierana ani sponsorowana przez Adobe Inc., Apryse, Google, Kofax ani LEAD Technologies. Wszystkie nazwy produktów, logo i marki są własnością ich odpowiednich właścicieli. Porównania mają charakter wyłącznie informacyjny i odzwierciedlają informacje dostępne publicznie w momencie pisania.

Powiązane artykuły

Key in blue circle

Uzyskaj natychmiast swój darmowy 30-dniowy Klucz Testowy.

Your trial license will be sent to your email address

Brak ograniczeń. 100% dostępności. Bez karty kredytowej.

bullet_checkedNie wymaga karty kredytowej ani tworzenia kontaBrak ograniczeń. 100% dostępności. Bez karty kredytowej.
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried IronPDF
Otrzymaj swoją Konsultację Bez Zobowiązań
Wypełnij poniższy formularz lub wyślij e-mail na sales@ironsoftware.com
Twoje dane zawsze będą utrzymywane w tajemnicy.
Zaufane przez miliony inżynierów na całym świecie
Logotypy klientów Iron Software
Otrzymaj swój darmowy Klucz Próbny na 30 dni natychmiast.
Nie wymaga karty kredytowej ani tworzenia konta