Przejdź do treści stopki
FILMY

Jak wyodrębnić tekst arabski z obrazów za pomocą narzędzi OCR

Ten przewodnik przeprowadza programistów .NET przez proces pełnej migracji z GdPicture.NET OCR do IronOCR. Obejmuje to zamianę pakietów, zmiany przestrzeni nazw oraz praktyczne wzorce kodu "przed i po" dla każdego głównego przepływu pracy OCR, ze szczególnym naciskiem na wyeliminowanie cyklu życia identyfikatorów obrazów typu integer, który definiuje model zarządzania zasobami GdPicture. Nie jest wymagana wcześniejsza lektura artykułu porównawczego.

Dlaczego warto przejść z GdPicture.NET

GdPicture.NET to platforma do przetwarzania obrazów dokumentów stworzona dla zespołów, które potrzebują integracji skanera, obsługi DICOM, edycji plików PDF, dodawania adnotacji i OCR od jednego dostawcy. Gdy jedynym wymaganiem jest OCR, cennik platformy i architektura API powodują tarcia, które z czasem się kumulują.

Koszt wtyczki dla podstawowego przepływu pracy OCR. Wyodrębnianie tekstu ze skanowanych plików PDF i tworzenie wyników z możliwością wyszukiwania wymaga trzech oddzielnych komponentów licencyjnych: Core SDK za około 4000 USD, wtyczki OCR za około 2000 USD oraz wtyczki PDF za około 2000 USD. Oznacza to koszt początkowy w wysokości 8000 USD plus 20% rocznej opłaty za utrzymanie. Zespoły, które potrzebują wyłącznie funkcji OCR, ponoszą pełne koszty związane z platformą do przetwarzania obrazów dokumentów.IronOCR obejmuje ten sam proces — OCR obrazu, OCR PDF, wstępne przetwarzanie, generowanie przeszukiwalnego PDF — z pojedynczego pakietu od $999 do $2,999 za licencję wieczystą, bez decyzji licencyjnych na funkcje. Pełny podział planów taryfowych można znaleźć na stronie licencyjnej IronOCR.

Cykl życia identyfikatora obrazu Integer. Każdy obraz załadowany przez GdPicture zwraca int. Przekazujesz tę wartość całkowitą do operacji OCR, a następnie wywołujesz ReleaseGdPictureImage, gdy skończysz. Ten wzorzec istnieje przed IDisposable. Działa, jeśli jest stosowana prawidłowo; gdy zostanie pominięte, powoduje wyciek pamięci. W usługach produkcyjnych przetwarzających setki dokumentów dziennie jedno przeoczone wywołanie wydania w gałęzi błędów powoduje wzrost zużycia pamięci, który jest naprawdę trudny do zdiagnozowania. OcrInput z IronOCR implementuje IDisposable — instrukcja using eliminuje cały ciężar czyszczenia.

Przestrzeń nazw specyficzna dla wersji. GdPicture osadza główny numer wersji w swojej przestrzeni nazw: using GdPicture14. Aktualizacja do następnego wydania głównej wersji wymaga aktualizacji tej dyrektywy using w każdym pliku źródłowym, który odnosi się do klas GdPicture. Duża aplikacja z OCR rozłożona na dziesiątki usług sprawia, że jest to wielogodzinne zadanie typu "znajdź i zamień", które nie zapewnia żadnej poprawy funkcjonalności. Przestrzeń nazw w IronOCR jest IronOcr we wszystkich głównych wersjach.

Wymaganie na folder zasobów zewnętrznych. GdPicture OCR wymaga właściwości ResourceFolder, która wskazuje na katalog plików językowych .traineddata w czasie wykonywania. Ta ścieżka działa na maszynie deweloperskiej, a następnie ulega awarii na serwerach Linux, kontenerach Docker i wdrożeniach Azure App Service, gdzie struktura katalogów nie istnieje.IronOCR zawiera wsparcie języka angielskiego wewnątrz pakietu NuGet; Dodatkowe języki instaluje się jako pakiety NuGet, które są dołączane do wyników kompilacji.

Inicjalizacja w trzech komponentach. Proces OCR PDF w GdPicture wymaga instancjonowania GdPictureImaging, GdPictureOCR oraz GdPicturePDF — trzech oddzielnych komponentów z indywidualnymi wymaganiami likwidacyjnymi — plus rejestracja menedżera licencji i przypisanie folderu zasobów.IronOCR wymaga jednej linii kodu podczas uruchamiania i jednej klasy w momencie wywołania.

Brak natywnej bezpieczeństwa wątków. Instancje GdPicture OCR nie są bezpieczne dla wątków. Równoległe przetwarzanie dokumentów wymaga starannego zarządzania instancjami i synchronizacji, aby uniknąć uszkodzenia stanu.IronOCR jest zaprojektowany do równoczesnego użycia: utwórz jeden IronTesseract na wątek, lub współdziel jedną instancję pod obciążeniem — każdy z tych wzorców działa bez dodatkowego kodu synchronizacyjnego.

Podstawowy problem

GdPicture przydziela pamięć dla każdego obrazu jako uchwyt całkowitoliczbowy zarządzany w czasie wykonywania. Każde wywołanie RenderPageToGdPictureImage w pętli przetwarzania TIFF tworzy nową alokację, która musi być uwolniona ręcznie:

// GdPicture: every frame = new integer ID = manual release required
using var pdf = new GdPicturePDF();
pdf.LoadFromFile("multi-page.tiff", false);

var frameIds = new List<int>();
try
{
    for (int i = 1; i <= pdf.GetPageCount(); i++)
    {
        pdf.SelectPage(i);
        int frameId = pdf.RenderPageToGdPictureImage(200, false); // new allocation
        if (frameId != 0) frameIds.Add(frameId);
        // ... OCR call here ...
    }
}
finally
{
    foreach (var id in frameIds) _imaging.ReleaseGdPictureImage(id); // manual per-frame release
}
// GdPicture: every frame = new integer ID = manual release required
using var pdf = new GdPicturePDF();
pdf.LoadFromFile("multi-page.tiff", false);

var frameIds = new List<int>();
try
{
    for (int i = 1; i <= pdf.GetPageCount(); i++)
    {
        pdf.SelectPage(i);
        int frameId = pdf.RenderPageToGdPictureImage(200, false); // new allocation
        if (frameId != 0) frameIds.Add(frameId);
        // ... OCR call here ...
    }
}
finally
{
    foreach (var id in frameIds) _imaging.ReleaseGdPictureImage(id); // manual per-frame release
}
Imports System.Collections.Generic

' GdPicture: every frame = new integer ID = manual release required
Using pdf As New GdPicturePDF()
    pdf.LoadFromFile("multi-page.tiff", False)

    Dim frameIds As New List(Of Integer)()
    Try
        For i As Integer = 1 To pdf.GetPageCount()
            pdf.SelectPage(i)
            Dim frameId As Integer = pdf.RenderPageToGdPictureImage(200, False) ' new allocation
            If frameId <> 0 Then frameIds.Add(frameId)
            ' ... OCR call here ...
        Next
    Finally
        For Each id In frameIds
            _imaging.ReleaseGdPictureImage(id) ' manual per-frame release
        Next
    End Try
End Using
$vbLabelText   $csharpLabel

IronOCR całkowicie eliminuje ten cykl. OcrInput obsługuje enumerację ramek i czyszczenie:

// IronOCR: the using statement handles everything
using var input = new OcrInput();
input.LoadImageFrames("multi-page.tiff");
var result = new IronTesseract().Read(input);
// IronOCR: the using statement handles everything
using var input = new OcrInput();
input.LoadImageFrames("multi-page.tiff");
var result = new IronTesseract().Read(input);
Imports IronOcr

Dim input As New OcrInput()
Using input
    input.LoadImageFrames("multi-page.tiff")
    Dim result = New IronTesseract().Read(input)
End Using
$vbLabelText   $csharpLabel

IronOCR a GdPicture.NET: porównanie funkcji

Poniższa tabela przedstawia zestawienie funkcji obu bibliotek w kontekście procesów opartych na OCR.

Funkcja GdPicture.NET IronOCR
Instalacja Wiele pakietów NuGet dotnet add package IronOcr
Aktywacja licencji LicenseManager.RegisterKEY() IronOcr.License.LicenseKey = "..."
Przestrzeń nazw w ramach dużej aktualizacji Wymaga znajdź-zastąp (GdPicture14 → następny) Niezmieniony (IronOcr)
Inicjalizacja komponentów GdPictureImaging + GdPictureOCR + GdPicturePDF new IronTesseract()
Model pamięci zasobów Ręczne śledzenie identyfikatorów całkowitoliczbowych i wydanie IDisposable / using instrukcja
Ryzyko wycieku pamięci Wysoki (brakujący ReleaseGdPictureImage) Brak (wymuszony przez kompilator przy using)
Folder zasobów zewnętrznych Wymagany (_ocr.ResourceFolder = path) Nie jest wymagane — zawarte w pakiecie
OCR obrazów Tak Tak
OCR PDF Tak (wymagana wtyczka PDF) Wbudowane, bez dodatkowej licencji
Wielostronicowy plik TIFF Ręczna pętla klatek + czyszczenie identyfikatorów poszczególnych klatek input.LoadImageFrames()
Dane wejściowe strumienia Przez przeciążenie strumienia GdPictureImaging input.LoadImage(stream)
Wynik w formacie PDF z możliwością wyszukiwania pdf.OcrPage() + pdf.SaveToFile() result.SaveAsSearchablePdf()
Przetwarzanie wstępne w celu wyeliminowania przekrzywienia Wymagana wtyczka do przetwarzania obrazów dokumentów input.Deskew() — wbudowany
Usuwanie szumów Wymagana wtyczka do przetwarzania obrazów dokumentów input.DeNoise() — wbudowany
Języki Pliki traineddata oparte na Tesseract w folderze Ponad 125 pakietów dostępnych za pośrednictwem NuGet
Wielojęzyczne OCR Tak OcrLanguage.French + OcrLanguage.German
Bezpieczeństwo wątków Ręczne zarządzanie instancjami Zabezpieczone przed współbieżnością już w fazie projektowania
Asynchroniczne OCR Nie wbudowane ReadAsync()
Odczytywanie BarCode Oddzielna wtyczka do BarCode ocr.Configuration.ReadBarCodes = true
Strukturalny wynik Dostęp do bloków/wierszy/słów oparty na indeksie Typowany .Pages, .Words, .Characters
Ocena pewności GetOCRResultConfidence(resultId) result.Confidence
Wielopłatformowe Windows, Linux, macOS Windows, Linux, macOS, Docker, AWS, Azure
Koszt wprowadzenia (OCR z plików PDF) ~8 000 USD (pakiet podstawowy + OCR + wtyczki PDF) $999–$2,999
Model cenowy Wersja wieczysta oparta na wtyczkach + 20% rocznie za utrzymanie Stała licencja bezterminowa, opcjonalne aktualizacje roczne
Wsparcie komercyjne Tak Tak

Szybki start: Migracja z GdPicture.NET do IronOCR

Krok 1: Zastąp pakiet NuGet

Usuń pakiety GdPicture:

dotnet remove package GdPicture.NET
dotnet remove package GdPicture.NET.OCR
dotnet remove package GdPicture.NET.PDF
dotnet remove package GdPicture.NET
dotnet remove package GdPicture.NET.OCR
dotnet remove package GdPicture.NET.PDF
SHELL

Zainstaluj IronOCR ze strony pakietu NuGet:

dotnet add package IronOcr

W przypadku języków innych niż angielski należy zainstalować odpowiedni pakiet językowy:

dotnet add package IronOcr.Languages.French, IronOcr.Languages.German

Krok 2: Aktualizacja przestrzeni nazw

Zastąp przestrzeń nazw GdPicture przestrzenią nazw IronOCR:

// Before (GdPicture)
using GdPicture14;

// After (IronOCR)
using IronOcr;
// Before (GdPicture)
using GdPicture14;

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

Krok 3: Inicjalizacja licencji

Umieść tę linię w Program.cs lub Startup.cs, przed jakimikolwiek wywołaniami OCR:

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

Całkowicie usuń blok rejestracji licencji GdPicture:

// Remove this
LicenseManager lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
// Remove this
LicenseManager lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
$vbLabelText   $csharpLabel

Na stronie produktu IronOCR dostępny jest bezpłatny klucz próbny, który pozwala przetestować oprogramowanie przed zakupem.

Przykłady migracji kodu

Przetwarzanie wielostronicowych ramek TIFF

Przetwarzanie wielostronicowych plików TIFF w GdPicture wymaga wybrania każdej klatki za pomocą interfejsu API PDF/obrazówania, renderowania jej do nowego identyfikatora obrazu, uruchomienia OCR i zwolnienia identyfikatora. Pojedyncza ramka, która nie jest uwolniona w bloku finally, wycieka 10-50 MB w zależności od DPI.

Podejście GdPicture.NET:

using GdPicture14;

public class GdPictureTiffProcessor
{
    private readonly GdPictureImaging _imaging;
    private readonly GdPictureOCR _ocr;

    public string ExtractTextFromTiff(string tiffPath)
    {
        var text = new StringBuilder();

        // Load TIFF through the imaging component
        int tiffId = _imaging.CreateGdPictureImageFromFile(tiffPath);

        if (tiffId == 0)
            throw new Exception($"TIFF load failed: {_imaging.GetStat()}");

        // Outer try: release the original TIFF handle
        try
        {
            int frameCount = _imaging.GetPageCount(tiffId);

            for (int i = 1; i <= frameCount; i++)
            {
                // Switch to frame — modifies the existing ID in place
                _imaging.SelectPage(tiffId, i);

                // Clone frame to a new image ID for OCR
                int frameId = _imaging.CloneImage(tiffId);

                if (frameId == 0) continue;

                // Inner try: release each cloned frame ID
                try
                {
                    _ocr.SetImage(frameId);
                    _ocr.Language = "eng";

                    string resultId = _ocr.RunOCR();

                    if (!string.IsNullOrEmpty(resultId))
                    {
                        text.AppendLine($"[Frame {i}] {_ocr.GetOCRResultText(resultId)}");
                    }
                }
                finally
                {
                    // Release cloned frame — critical for each iteration
                    _imaging.ReleaseGdPictureImage(frameId);
                }
            }
        }
        finally
        {
            // Release original TIFF handle
            _imaging.ReleaseGdPictureImage(tiffId);
        }

        return text.ToString();
    }
}
using GdPicture14;

public class GdPictureTiffProcessor
{
    private readonly GdPictureImaging _imaging;
    private readonly GdPictureOCR _ocr;

    public string ExtractTextFromTiff(string tiffPath)
    {
        var text = new StringBuilder();

        // Load TIFF through the imaging component
        int tiffId = _imaging.CreateGdPictureImageFromFile(tiffPath);

        if (tiffId == 0)
            throw new Exception($"TIFF load failed: {_imaging.GetStat()}");

        // Outer try: release the original TIFF handle
        try
        {
            int frameCount = _imaging.GetPageCount(tiffId);

            for (int i = 1; i <= frameCount; i++)
            {
                // Switch to frame — modifies the existing ID in place
                _imaging.SelectPage(tiffId, i);

                // Clone frame to a new image ID for OCR
                int frameId = _imaging.CloneImage(tiffId);

                if (frameId == 0) continue;

                // Inner try: release each cloned frame ID
                try
                {
                    _ocr.SetImage(frameId);
                    _ocr.Language = "eng";

                    string resultId = _ocr.RunOCR();

                    if (!string.IsNullOrEmpty(resultId))
                    {
                        text.AppendLine($"[Frame {i}] {_ocr.GetOCRResultText(resultId)}");
                    }
                }
                finally
                {
                    // Release cloned frame — critical for each iteration
                    _imaging.ReleaseGdPictureImage(frameId);
                }
            }
        }
        finally
        {
            // Release original TIFF handle
            _imaging.ReleaseGdPictureImage(tiffId);
        }

        return text.ToString();
    }
}
Imports GdPicture14
Imports System.Text

Public Class GdPictureTiffProcessor
    Private ReadOnly _imaging As GdPictureImaging
    Private ReadOnly _ocr As GdPictureOCR

    Public Function ExtractTextFromTiff(tiffPath As String) As String
        Dim text As New StringBuilder()

        ' Load TIFF through the imaging component
        Dim tiffId As Integer = _imaging.CreateGdPictureImageFromFile(tiffPath)

        If tiffId = 0 Then
            Throw New Exception($"TIFF load failed: {_imaging.GetStat()}")
        End If

        ' Outer try: release the original TIFF handle
        Try
            Dim frameCount As Integer = _imaging.GetPageCount(tiffId)

            For i As Integer = 1 To frameCount
                ' Switch to frame — modifies the existing ID in place
                _imaging.SelectPage(tiffId, i)

                ' Clone frame to a new image ID for OCR
                Dim frameId As Integer = _imaging.CloneImage(tiffId)

                If frameId = 0 Then Continue For

                ' Inner try: release each cloned frame ID
                Try
                    _ocr.SetImage(frameId)
                    _ocr.Language = "eng"

                    Dim resultId As String = _ocr.RunOCR()

                    If Not String.IsNullOrEmpty(resultId) Then
                        text.AppendLine($"[Frame {i}] {_ocr.GetOCRResultText(resultId)}")
                    End If
                Finally
                    ' Release cloned frame — critical for each iteration
                    _imaging.ReleaseGdPictureImage(frameId)
                End Try
            Next
        Finally
            ' Release original TIFF handle
            _imaging.ReleaseGdPictureImage(tiffId)
        End Try

        Return text.ToString()
    End Function
End Class
$vbLabelText   $csharpLabel

Podejście IronOCR:

using IronOcr;

public class IronOcrTiffProcessor
{
    public string ExtractTextFromTiff(string tiffPath)
    {
        using var input = new OcrInput();
        input.LoadImageFrames(tiffPath);  // all frames loaded, all cleanup automatic

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

        // Per-frame text is available on result.Pages
        foreach (var page in result.Pages)
            Console.WriteLine($"[Frame {page.PageNumber}] {page.Text}");

        return result.Text;
    }
}
using IronOcr;

public class IronOcrTiffProcessor
{
    public string ExtractTextFromTiff(string tiffPath)
    {
        using var input = new OcrInput();
        input.LoadImageFrames(tiffPath);  // all frames loaded, all cleanup automatic

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

        // Per-frame text is available on result.Pages
        foreach (var page in result.Pages)
            Console.WriteLine($"[Frame {page.PageNumber}] {page.Text}");

        return result.Text;
    }
}
Imports IronOcr

Public Class IronOcrTiffProcessor
    Public Function ExtractTextFromTiff(tiffPath As String) As String
        Using input As New OcrInput()
            input.LoadImageFrames(tiffPath) ' all frames loaded, all cleanup automatic

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

            ' Per-frame text is available on result.Pages
            For Each page In result.Pages
                Console.WriteLine($"[Frame {page.PageNumber}] {page.Text}")
            Next

            Return result.Text
        End Using
    End Function
End Class
$vbLabelText   $csharpLabel

LoadImageFrames ładuje każdą ramkę z wielostronicowego TIFF do potoku OcrInput. Blok using obsługuje całą pamięć związaną z każdą ramką na końcu zakresu. Brak List<int> do utrzymania, brak zagnieżdżonych bloków try/finally wymaganych. Przewodnik dotyczący plików wejściowych TIFF i GIF szczegółowo omawia wybór klatek i obsługę wielu formatów.

Wejście oparte na strumieniu zastępujące inicjalizację wtyczki

Aplikacje serwerowe często odbierają dokumenty w postaci strumieni, a nie ścieżek do plików — z przesyłania HTTP, kolejek komunikatów lub obiektów blob w bazach danych. GdPicture wymaga ładowania strumienia przez GdPictureImaging, co samo w sobie wymaga sekwencji inicjalizacji komponentu i konfiguracji folderu zasobów, które poprzedzają każdą operację.

Podejście GdPicture.NET:

using GdPicture14;

public class GdPictureStreamOcr : IDisposable
{
    private readonly GdPictureImaging _imaging;
    private readonly GdPictureOCR _ocr;

    public GdPictureStreamOcr()
    {
        // Plugin initialization required before stream loading is possible
        var lm = new LicenseManager();
        lm.RegisterKEY("GDPICTURE-LICENSE-KEY");

        _imaging = new GdPictureImaging();
        _ocr = new GdPictureOCR();
        _ocr.ResourceFolder = @"C:\GdPicture\Resources\OCR"; // path must exist at runtime
    }

    public string ExtractTextFromStream(Stream documentStream)
    {
        // Load stream into imaging component to get an image ID
        int imageId = _imaging.CreateGdPictureImageFromStream(documentStream);

        if (imageId == 0)
            throw new Exception($"Stream load failed: {_imaging.GetStat()}");

        try
        {
            _ocr.SetImage(imageId);
            _ocr.Language = "eng";

            string resultId = _ocr.RunOCR();

            if (string.IsNullOrEmpty(resultId))
                throw new Exception($"OCR failed: {_ocr.GetStat()}");

            return _ocr.GetOCRResultText(resultId);
        }
        finally
        {
            _imaging.ReleaseGdPictureImage(imageId);
        }
    }

    public void Dispose()
    {
        _ocr?.Dispose();
        _imaging?.Dispose();
    }
}
using GdPicture14;

public class GdPictureStreamOcr : IDisposable
{
    private readonly GdPictureImaging _imaging;
    private readonly GdPictureOCR _ocr;

    public GdPictureStreamOcr()
    {
        // Plugin initialization required before stream loading is possible
        var lm = new LicenseManager();
        lm.RegisterKEY("GDPICTURE-LICENSE-KEY");

        _imaging = new GdPictureImaging();
        _ocr = new GdPictureOCR();
        _ocr.ResourceFolder = @"C:\GdPicture\Resources\OCR"; // path must exist at runtime
    }

    public string ExtractTextFromStream(Stream documentStream)
    {
        // Load stream into imaging component to get an image ID
        int imageId = _imaging.CreateGdPictureImageFromStream(documentStream);

        if (imageId == 0)
            throw new Exception($"Stream load failed: {_imaging.GetStat()}");

        try
        {
            _ocr.SetImage(imageId);
            _ocr.Language = "eng";

            string resultId = _ocr.RunOCR();

            if (string.IsNullOrEmpty(resultId))
                throw new Exception($"OCR failed: {_ocr.GetStat()}");

            return _ocr.GetOCRResultText(resultId);
        }
        finally
        {
            _imaging.ReleaseGdPictureImage(imageId);
        }
    }

    public void Dispose()
    {
        _ocr?.Dispose();
        _imaging?.Dispose();
    }
}
IRON VB CONVERTER ERROR developers@ironsoftware.com
$vbLabelText   $csharpLabel

Podejście IronOCR:

using IronOcr;

public class IronOcrStreamOcr
{
    public string ExtractTextFromStream(Stream documentStream)
    {
        using var input = new OcrInput();
        input.LoadImage(documentStream);  // stream accepted directly — no imaging component

        return new IronTesseract().Read(input).Text;
    }
}
using IronOcr;

public class IronOcrStreamOcr
{
    public string ExtractTextFromStream(Stream documentStream)
    {
        using var input = new OcrInput();
        input.LoadImage(documentStream);  // stream accepted directly — no imaging component

        return new IronTesseract().Read(input).Text;
    }
}
Imports IronOcr

Public Class IronOcrStreamOcr
    Public Function ExtractTextFromStream(documentStream As Stream) As String
        Using input As New OcrInput()
            input.LoadImage(documentStream) ' stream accepted directly — no imaging component

            Return New IronTesseract().Read(input).Text
        End Using
    End Function
End Class
$vbLabelText   $csharpLabel

Podejście GdPicture wymaga utworzenia trzech obiektów i skonfigurowania ścieżki systemu plików, zanim będzie można wykorzystać pierwszy strumień.IronOCR akceptuje strumień bezpośrednio na OcrInput bez wcześniejszego przygotowania. Przewodnik po wejściu strumieniowym obejmuje tablice bajtów, MemoryStream i wzorce FileStream dla architektur przepływu, gdzie zapisywanie plików tymczasowych jest niepożądane.

Asynchroniczne OCR zastępujące odpytywanie kodów statusu

GdPicture OCR działa synchronicznie. Aplikacje przetwarzające dokumenty w punktach końcowych ASP.NET Core lub usługach w tle muszą opakować RunOCR w Task.Run, aby uniknąć blokowania wątków żądań — a następnie zarządzać cyklem życia identyfikatora obrazu przez granicę wątku.IronOCR zapewnia wsparcie dla asynchronicznego działania na najwyższym poziomie przez ReadAsync.

Podejście GdPicture.NET:

using GdPicture14;
using System.Threading.Tasks;

public class GdPictureAsyncWrapper
{
    private readonly GdPictureImaging _imaging;
    private readonly GdPictureOCR _ocr;
    private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);

    public async Task<string> ExtractTextAsync(string imagePath)
    {
        // Must acquire lock: GdPictureOCR is not thread-safe
        await _lock.WaitAsync();

        try
        {
            return await Task.Run(() =>
            {
                int imageId = _imaging.CreateGdPictureImageFromFile(imagePath);

                if (imageId == 0)
                    throw new Exception($"Load failed: {_imaging.GetStat()}");

                try
                {
                    _ocr.SetImage(imageId);
                    _ocr.Language = "eng";

                    string resultId = _ocr.RunOCR();

                    if (string.IsNullOrEmpty(resultId))
                        throw new Exception($"OCR failed: {_ocr.GetStat()}");

                    return _ocr.GetOCRResultText(resultId);
                }
                finally
                {
                    _imaging.ReleaseGdPictureImage(imageId);
                }
            });
        }
        finally
        {
            _lock.Release();
        }
    }
}
using GdPicture14;
using System.Threading.Tasks;

public class GdPictureAsyncWrapper
{
    private readonly GdPictureImaging _imaging;
    private readonly GdPictureOCR _ocr;
    private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);

    public async Task<string> ExtractTextAsync(string imagePath)
    {
        // Must acquire lock: GdPictureOCR is not thread-safe
        await _lock.WaitAsync();

        try
        {
            return await Task.Run(() =>
            {
                int imageId = _imaging.CreateGdPictureImageFromFile(imagePath);

                if (imageId == 0)
                    throw new Exception($"Load failed: {_imaging.GetStat()}");

                try
                {
                    _ocr.SetImage(imageId);
                    _ocr.Language = "eng";

                    string resultId = _ocr.RunOCR();

                    if (string.IsNullOrEmpty(resultId))
                        throw new Exception($"OCR failed: {_ocr.GetStat()}");

                    return _ocr.GetOCRResultText(resultId);
                }
                finally
                {
                    _imaging.ReleaseGdPictureImage(imageId);
                }
            });
        }
        finally
        {
            _lock.Release();
        }
    }
}
Imports GdPicture14
Imports System.Threading.Tasks

Public Class GdPictureAsyncWrapper
    Private ReadOnly _imaging As GdPictureImaging
    Private ReadOnly _ocr As GdPictureOCR
    Private ReadOnly _lock As New SemaphoreSlim(1, 1)

    Public Async Function ExtractTextAsync(imagePath As String) As Task(Of String)
        ' Must acquire lock: GdPictureOCR is not thread-safe
        Await _lock.WaitAsync()

        Try
            Return Await Task.Run(Function()
                                      Dim imageId As Integer = _imaging.CreateGdPictureImageFromFile(imagePath)

                                      If imageId = 0 Then
                                          Throw New Exception($"Load failed: {_imaging.GetStat()}")
                                      End If

                                      Try
                                          _ocr.SetImage(imageId)
                                          _ocr.Language = "eng"

                                          Dim resultId As String = _ocr.RunOCR()

                                          If String.IsNullOrEmpty(resultId) Then
                                              Throw New Exception($"OCR failed: {_ocr.GetStat()}")
                                          End If

                                          Return _ocr.GetOCRResultText(resultId)
                                      Finally
                                          _imaging.ReleaseGdPictureImage(imageId)
                                      End Try
                                  End Function)
        Finally
            _lock.Release()
        End Try
    End Function
End Class
$vbLabelText   $csharpLabel

Podejście IronOCR:

using IronOcr;

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

    public async Task<string> ExtractTextAsync(string imagePath)
    {
        // ReadAsync is natively async — no Task.Run wrapper, no lock required
        var result = await _ocr.ReadAsync(imagePath);
        return result.Text;
    }

    public async Task<string> ExtractFromStreamAsync(Stream stream)
    {
        using var input = new OcrInput();
        input.LoadImage(stream);

        var result = await _ocr.ReadAsync(input);
        return result.Text;
    }
}
using IronOcr;

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

    public async Task<string> ExtractTextAsync(string imagePath)
    {
        // ReadAsync is natively async — no Task.Run wrapper, no lock required
        var result = await _ocr.ReadAsync(imagePath);
        return result.Text;
    }

    public async Task<string> ExtractFromStreamAsync(Stream stream)
    {
        using var input = new OcrInput();
        input.LoadImage(stream);

        var result = await _ocr.ReadAsync(input);
        return result.Text;
    }
}
Imports IronOcr
Imports System.IO
Imports System.Threading.Tasks

Public Class IronOcrAsyncService
    Private ReadOnly _ocr As New IronTesseract()

    Public Async Function ExtractTextAsync(imagePath As String) As Task(Of String)
        ' ReadAsync is natively async — no Task.Run wrapper, no lock required
        Dim result = Await _ocr.ReadAsync(imagePath)
        Return result.Text
    End Function

    Public Async Function ExtractFromStreamAsync(stream As Stream) As Task(Of String)
        Using input As New OcrInput()
            input.LoadImage(stream)

            Dim result = Await _ocr.ReadAsync(input)
            Return result.Text
        End Using
    End Function
End Class
$vbLabelText   $csharpLabel

Podejście GdPicture wymaga SemaphoreSlim do serializacji dostępu do współdzielonej instancji GdPictureOCR, plus Task.Run do przenoszenia pracy blokującej synchronicznie z wątku wywołującego, plus pełen cykl życia identyfikatora obrazu wewnątrz tej lamda. ReadAsync z IronOCR jest naprawdę nieblokujący i bezpieczny w obsłudze wielowątkowej. Przewodnik po asynchronicznym OCR obejmuje integrację z oprogramowaniem pośredniczącym .NET Core oraz hostowanymi usługami działającymi w tle.

Równoległe przetwarzanie wsadowe z bezpieczeństwem wątków

Aby jak najszybciej przetworzyć folder ze skanami dokumentów, konieczne jest równoległe wykonywanie zadań. GdPicture wymaga jednej instancji GdPictureOCR na wątek — współdzielenie jednej instancji powoduje niedeterministyczne awarie. Każda instancja przypadająca na wątek wymaga również własnego komponentu GdPictureImaging i konfiguracji folderu zasobów, co czyni podejścia oparte na pulach wątków niepraktycznymi bez wzorca fabryki.

Podejście GdPicture.NET:

using GdPicture14;
using System.Collections.Concurrent;
using System.Threading.Tasks;

public class GdPictureParallelBatch
{
    private readonly string _resourceFolder = @"C:\GdPicture\Resources\OCR";

    public ConcurrentDictionary<string, string> ProcessBatch(string[] imagePaths)
    {
        var results = new ConcurrentDictionary<string, string>();

        // Each thread must have its own component instances
        Parallel.ForEach(imagePaths, imagePath =>
        {
            // Create per-thread instances — shared instances cause failures
            using var threadImaging = new GdPictureImaging();
            using var threadOcr = new GdPictureOCR();
            threadOcr.ResourceFolder = _resourceFolder;

            // Re-register license per thread (may be required depending on SDK version)
            var lm = new LicenseManager();
            lm.RegisterKEY("GDPICTURE-LICENSE-KEY");

            int imageId = threadImaging.CreateGdPictureImageFromFile(imagePath);

            if (imageId == 0)
            {
                results[imagePath] = $"ERROR: {threadImaging.GetStat()}";
                return;
            }

            try
            {
                threadOcr.SetImage(imageId);
                threadOcr.Language = "eng";

                string resultId = threadOcr.RunOCR();
                results[imagePath] = string.IsNullOrEmpty(resultId)
                    ? $"ERROR: {threadOcr.GetStat()}"
                    : threadOcr.GetOCRResultText(resultId);
            }
            finally
            {
                threadImaging.ReleaseGdPictureImage(imageId);
            }
        });

        return results;
    }
}
using GdPicture14;
using System.Collections.Concurrent;
using System.Threading.Tasks;

public class GdPictureParallelBatch
{
    private readonly string _resourceFolder = @"C:\GdPicture\Resources\OCR";

    public ConcurrentDictionary<string, string> ProcessBatch(string[] imagePaths)
    {
        var results = new ConcurrentDictionary<string, string>();

        // Each thread must have its own component instances
        Parallel.ForEach(imagePaths, imagePath =>
        {
            // Create per-thread instances — shared instances cause failures
            using var threadImaging = new GdPictureImaging();
            using var threadOcr = new GdPictureOCR();
            threadOcr.ResourceFolder = _resourceFolder;

            // Re-register license per thread (may be required depending on SDK version)
            var lm = new LicenseManager();
            lm.RegisterKEY("GDPICTURE-LICENSE-KEY");

            int imageId = threadImaging.CreateGdPictureImageFromFile(imagePath);

            if (imageId == 0)
            {
                results[imagePath] = $"ERROR: {threadImaging.GetStat()}";
                return;
            }

            try
            {
                threadOcr.SetImage(imageId);
                threadOcr.Language = "eng";

                string resultId = threadOcr.RunOCR();
                results[imagePath] = string.IsNullOrEmpty(resultId)
                    ? $"ERROR: {threadOcr.GetStat()}"
                    : threadOcr.GetOCRResultText(resultId);
            }
            finally
            {
                threadImaging.ReleaseGdPictureImage(imageId);
            }
        });

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

Public Class GdPictureParallelBatch
    Private ReadOnly _resourceFolder As String = "C:\GdPicture\Resources\OCR"

    Public Function ProcessBatch(imagePaths As String()) As ConcurrentDictionary(Of String, String)
        Dim results As New ConcurrentDictionary(Of String, String)()

        ' Each thread must have its own component instances
        Parallel.ForEach(imagePaths, Sub(imagePath)
            ' Create per-thread instances — shared instances cause failures
            Using threadImaging As New GdPictureImaging()
                Using threadOcr As New GdPictureOCR()
                    threadOcr.ResourceFolder = _resourceFolder

                    ' Re-register license per thread (may be required depending on SDK version)
                    Dim lm As New LicenseManager()
                    lm.RegisterKEY("GDPICTURE-LICENSE-KEY")

                    Dim imageId As Integer = threadImaging.CreateGdPictureImageFromFile(imagePath)

                    If imageId = 0 Then
                        results(imagePath) = $"ERROR: {threadImaging.GetStat()}"
                        Return
                    End If

                    Try
                        threadOcr.SetImage(imageId)
                        threadOcr.Language = "eng"

                        Dim resultId As String = threadOcr.RunOCR()
                        results(imagePath) = If(String.IsNullOrEmpty(resultId), $"ERROR: {threadOcr.GetStat()}", threadOcr.GetOCRResultText(resultId))
                    Finally
                        threadImaging.ReleaseGdPictureImage(imageId)
                    End Try
                End Using
            End Using
        End Sub)

        Return results
    End Function
End Class
$vbLabelText   $csharpLabel

Podejście IronOCR:

using IronOcr;
using System.Collections.Concurrent;
using System.Threading.Tasks;

public class IronOcrParallelBatch
{
    public ConcurrentDictionary<string, string> ProcessBatch(string[] imagePaths)
    {
        var results = new ConcurrentDictionary<string, string>();

        // IronTesseract is thread-safe — one instance handles all threads
        var ocr = new IronTesseract();

        Parallel.ForEach(imagePaths, imagePath =>
        {
            try
            {
                results[imagePath] = ocr.Read(imagePath).Text;
            }
            catch (Exception ex)
            {
                results[imagePath] = $"ERROR: {ex.Message}";
            }
        });

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

public class IronOcrParallelBatch
{
    public ConcurrentDictionary<string, string> ProcessBatch(string[] imagePaths)
    {
        var results = new ConcurrentDictionary<string, string>();

        // IronTesseract is thread-safe — one instance handles all threads
        var ocr = new IronTesseract();

        Parallel.ForEach(imagePaths, imagePath =>
        {
            try
            {
                results[imagePath] = ocr.Read(imagePath).Text;
            }
            catch (Exception ex)
            {
                results[imagePath] = $"ERROR: {ex.Message}";
            }
        });

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

Public Class IronOcrParallelBatch
    Public Function ProcessBatch(imagePaths As String()) As ConcurrentDictionary(Of String, String)
        Dim results As New ConcurrentDictionary(Of String, String)()

        ' IronTesseract is thread-safe — one instance handles all threads
        Dim ocr As New IronTesseract()

        Parallel.ForEach(imagePaths, Sub(imagePath)
            Try
                results(imagePath) = ocr.Read(imagePath).Text
            Catch ex As Exception
                results(imagePath) = $"ERROR: {ex.Message}"
            End Try
        End Sub)

        Return results
    End Function
End Class
$vbLabelText   $csharpLabel

Równoległa implementacja GdPicture tworzy trzy obiekty na wątek i wymaga rejestracji licencji dla każdego wątku.IronOCR obsługuje równoczesne odczyty z jednej instancji IronTesseract bez obciążenia synchronizacyjnego. Przykład wielowątkowości demonstruje testy wydajności i wzorce Parallel.ForEach dla zadań przetwarzania dużych ilości dokumentów.

Wejście tablicy bajtów i ekstrakcja ustrukturyzowanych akapitów

Aplikacje pobierające dokumenty z baz danych lub magazynów obiektów często pracują z tablicami bajtów, a nie ścieżkami do plików. GdPicture wymaga konwersji tablicy bajtów na strumień i ładowania jej przez GdPictureImaging. Wyodrębnienie ustrukturyzowanych danych na poziomie akapitu z wyniku wymaga poruszania się po hierarchii indeksu bloków/wierszy.

Podejście GdPicture.NET:

using GdPicture14;

public class GdPictureByteArrayOcr
{
    private readonly GdPictureImaging _imaging;
    private readonly GdPictureOCR _ocr;

    public List<string> ExtractParagraphsFromBytes(byte[] imageBytes)
    {
        var paragraphs = new List<string>();

        // Byte array must go through MemoryStream to reach CreateGdPictureImageFromStream
        using var ms = new MemoryStream(imageBytes);
        int imageId = _imaging.CreateGdPictureImageFromStream(ms);

        if (imageId == 0)
            throw new Exception($"Byte array load failed: {_imaging.GetStat()}");

        try
        {
            _ocr.SetImage(imageId);
            _ocr.Language = "eng";

            string resultId = _ocr.RunOCR();

            if (string.IsNullOrEmpty(resultId))
                throw new Exception($"OCR failed: {_ocr.GetStat()}");

            // Paragraph-level data requires iterating block structure
            int blockCount = _ocr.GetOCRResultBlockCount(resultId);

            for (int b = 0; b < blockCount; b++)
            {
                var blockText = new StringBuilder();
                int lineCount = _ocr.GetOCRResultBlockLineCount(resultId, b);

                for (int l = 0; l < lineCount; l++)
                {
                    int wordCount = _ocr.GetOCRResultBlockLineWordCount(resultId, b, l);

                    for (int w = 0; w < wordCount; w++)
                    {
                        blockText.Append(_ocr.GetOCRResultBlockLineWordText(resultId, b, l, w));
                        blockText.Append(" ");
                    }
                }

                string text = blockText.ToString().Trim();
                if (!string.IsNullOrEmpty(text))
                    paragraphs.Add(text);
            }
        }
        finally
        {
            _imaging.ReleaseGdPictureImage(imageId);
        }

        return paragraphs;
    }
}
using GdPicture14;

public class GdPictureByteArrayOcr
{
    private readonly GdPictureImaging _imaging;
    private readonly GdPictureOCR _ocr;

    public List<string> ExtractParagraphsFromBytes(byte[] imageBytes)
    {
        var paragraphs = new List<string>();

        // Byte array must go through MemoryStream to reach CreateGdPictureImageFromStream
        using var ms = new MemoryStream(imageBytes);
        int imageId = _imaging.CreateGdPictureImageFromStream(ms);

        if (imageId == 0)
            throw new Exception($"Byte array load failed: {_imaging.GetStat()}");

        try
        {
            _ocr.SetImage(imageId);
            _ocr.Language = "eng";

            string resultId = _ocr.RunOCR();

            if (string.IsNullOrEmpty(resultId))
                throw new Exception($"OCR failed: {_ocr.GetStat()}");

            // Paragraph-level data requires iterating block structure
            int blockCount = _ocr.GetOCRResultBlockCount(resultId);

            for (int b = 0; b < blockCount; b++)
            {
                var blockText = new StringBuilder();
                int lineCount = _ocr.GetOCRResultBlockLineCount(resultId, b);

                for (int l = 0; l < lineCount; l++)
                {
                    int wordCount = _ocr.GetOCRResultBlockLineWordCount(resultId, b, l);

                    for (int w = 0; w < wordCount; w++)
                    {
                        blockText.Append(_ocr.GetOCRResultBlockLineWordText(resultId, b, l, w));
                        blockText.Append(" ");
                    }
                }

                string text = blockText.ToString().Trim();
                if (!string.IsNullOrEmpty(text))
                    paragraphs.Add(text);
            }
        }
        finally
        {
            _imaging.ReleaseGdPictureImage(imageId);
        }

        return paragraphs;
    }
}
Imports GdPicture14
Imports System.IO
Imports System.Text

Public Class GdPictureByteArrayOcr
    Private ReadOnly _imaging As GdPictureImaging
    Private ReadOnly _ocr As GdPictureOCR

    Public Function ExtractParagraphsFromBytes(imageBytes As Byte()) As List(Of String)
        Dim paragraphs As New List(Of String)()

        ' Byte array must go through MemoryStream to reach CreateGdPictureImageFromStream
        Using ms As New MemoryStream(imageBytes)
            Dim imageId As Integer = _imaging.CreateGdPictureImageFromStream(ms)

            If imageId = 0 Then
                Throw New Exception($"Byte array load failed: {_imaging.GetStat()}")
            End If

            Try
                _ocr.SetImage(imageId)
                _ocr.Language = "eng"

                Dim resultId As String = _ocr.RunOCR()

                If String.IsNullOrEmpty(resultId) Then
                    Throw New Exception($"OCR failed: {_ocr.GetStat()}")
                End If

                ' Paragraph-level data requires iterating block structure
                Dim blockCount As Integer = _ocr.GetOCRResultBlockCount(resultId)

                For b As Integer = 0 To blockCount - 1
                    Dim blockText As New StringBuilder()
                    Dim lineCount As Integer = _ocr.GetOCRResultBlockLineCount(resultId, b)

                    For l As Integer = 0 To lineCount - 1
                        Dim wordCount As Integer = _ocr.GetOCRResultBlockLineWordCount(resultId, b, l)

                        For w As Integer = 0 To wordCount - 1
                            blockText.Append(_ocr.GetOCRResultBlockLineWordText(resultId, b, l, w))
                            blockText.Append(" "c)
                        Next
                    Next

                    Dim text As String = blockText.ToString().Trim()
                    If Not String.IsNullOrEmpty(text) Then
                        paragraphs.Add(text)
                    End If
                Next
            Finally
                _imaging.ReleaseGdPictureImage(imageId)
            End Try
        End Using

        Return paragraphs
    End Function
End Class
$vbLabelText   $csharpLabel

Podejście IronOCR:

using IronOcr;

public class IronOcrByteArrayOcr
{
    public List<string> ExtractParagraphsFromBytes(byte[] imageBytes)
    {
        using var input = new OcrInput();
        input.LoadImage(imageBytes);  // byte array accepted directly

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

        // Paragraphs are a first-class typed collection
        return result.Paragraphs
            .Select(p => p.Text)
            .Where(t => !string.IsNullOrWhiteSpace(t))
            .ToList();
    }
}
using IronOcr;

public class IronOcrByteArrayOcr
{
    public List<string> ExtractParagraphsFromBytes(byte[] imageBytes)
    {
        using var input = new OcrInput();
        input.LoadImage(imageBytes);  // byte array accepted directly

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

        // Paragraphs are a first-class typed collection
        return result.Paragraphs
            .Select(p => p.Text)
            .Where(t => !string.IsNullOrWhiteSpace(t))
            .ToList();
    }
}
Imports IronOcr

Public Class IronOcrByteArrayOcr
    Public Function ExtractParagraphsFromBytes(imageBytes As Byte()) As List(Of String)
        Using input As New OcrInput()
            input.LoadImage(imageBytes) ' byte array accepted directly

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

            ' Paragraphs are a first-class typed collection
            Return result.Paragraphs _
                .Select(Function(p) p.Text) _
                .Where(Function(t) Not String.IsNullOrWhiteSpace(t)) _
                .ToList()
        End Using
    End Function
End Class
$vbLabelText   $csharpLabel

IronOCR akceptuje byte[] bezpośrednio na LoadImage bez pośredniego MemoryStream. Wynik udostępnia .Paragraphs jako typowaną kolekcję zapytawliną LINQ — nie są wymagane potrójne pętle blok-linia-słowo. Przewodnik po wynikach odczytu obejmuje dostęp do współrzędnych, filtrowanie poziomu pewności oraz wzorce wyników strukturalnych dla procesów przetwarzania faktur i formularzy. W celu skanowania określonych obszarów dokumentu zobacz OCR oparte na regionach.

Dokumentacja API GdPicture.NET do IronOCR

GdPicture.NET Odpowiednik IronOCR
using GdPicture14; using IronOcr;
LicenseManager.RegisterKEY("key") IronOcr.License.LicenseKey = "key"
new GdPictureImaging() Nie jest wymagane — wewnętrzne dla OcrInput
new GdPictureOCR() new IronTesseract()
new GdPicturePDF() Nie jest wymagane — OcrInput.LoadPdf() obsługuje to
_ocr.ResourceFolder = path Nie jest wymagane — zasoby dołączone w pakiecie NuGet
imaging.CreateGdPictureImageFromFile(path) input.LoadImage(path)
imaging.CreateGdPictureImageFromStream(stream) input.LoadImage(stream)
imaging.CreateGdPictureImageFromBytes(bytes) input.LoadImage(bytes)
imaging.CloneImage(tiffId) na ramkę input.LoadImageFrames(tiffPath)
imaging.ReleaseGdPictureImage(imageId) using var input = new OcrInput() — automatyczne
ocr.SetImage(imageId) Nie jest wymagane — OcrInput utrzymuje obraz
ocr.Language = "eng" ocr.Language = OcrLanguage.English
ocr.RunOCR() → łańcuch resultId ocr.Read(input) → typowany OcrResult
ocr.GetOCRResultText(resultId) result.Text
ocr.GetOCRResultConfidence(resultId) result.Confidence
ocr.GetOCRResultBlockCount(resultId) result.Pages[i].Paragraphs.Count
ocr.GetOCRResultBlockLineWordText(resultId, b, l, w) result.Words[i].Text
imaging.GetStat() / ocr.GetStat() Standardowe wyjątki .NET Standard
GdPictureStatus.OK sprawdź po każdym wywołaniu Nie jest wymagane — wyjątki są propagowane
pdf.OcrPage("eng", resourcePath, "", 200) result.SaveAsSearchablePdf(outputPath)
pdf.RenderPageToGdPictureImage(200, false) Nie jest wymagane —IronOCR renderuje wewnętrznie
pdf.SelectPage(i) Nie jest wymagane — wszystkie strony przetwarzane domyślnie
pdf.GetPageCount() result.Pages.Count
Task.Run(() => ocr.RunOCR()) + SemaphoreSlim await ocr.ReadAsync(input)

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

Problem 1: Zmienne identyfikujące obrazy pozostałe w kodzie po refaktoryzacji

GdPicture.NET: Istniejący kod ogłasza int imageId na początku metod, śledzi go przez try/finally i przekazuje do wielu wywołań GdPicture. Po zastąpieniu wywołań GdPicture, te zmienne i ich wywołania ReleaseGdPictureImage stają się osieroconym martwym kodem.

Rozwiązanie: Usuń cały wzorzec identyfikatora obrazu. Zastąp deklarację, try, finally i wywołanie uwolnienia blokiem using var input = new OcrInput(). Szukaj ReleaseGdPictureImage, aby znaleźć każde wywołanie czyszczenia, które musi zostać usunięte:

grep -rn "ReleaseGdPictureImage\|imageId\|resultId" --include="*.cs" .
grep -rn "ReleaseGdPictureImage\|imageId\|resultId" --include="*.cs" .
SHELL
// Remove all of this
int imageId = _imaging.CreateGdPictureImageFromFile(path);
try
{
    _ocr.SetImage(imageId);
    string resultId = _ocr.RunOCR();
    return _ocr.GetOCRResultText(resultId);
}
finally
{
    _imaging.ReleaseGdPictureImage(imageId);
}

// Replace with
using var input = new OcrInput();
input.LoadImage(path);
return new IronTesseract().Read(input).Text;
// Remove all of this
int imageId = _imaging.CreateGdPictureImageFromFile(path);
try
{
    _ocr.SetImage(imageId);
    string resultId = _ocr.RunOCR();
    return _ocr.GetOCRResultText(resultId);
}
finally
{
    _imaging.ReleaseGdPictureImage(imageId);
}

// Replace with
using var input = new OcrInput();
input.LoadImage(path);
return new IronTesseract().Read(input).Text;
' Remove all of this
Dim imageId As Integer = _imaging.CreateGdPictureImageFromFile(path)
Try
    _ocr.SetImage(imageId)
    Dim resultId As String = _ocr.RunOCR()
    Return _ocr.GetOCRResultText(resultId)
Finally
    _imaging.ReleaseGdPictureImage(imageId)
End Try

' Replace with
Using input As New OcrInput()
    input.LoadImage(path)
    Return New IronTesseract().Read(input).Text
End Using
$vbLabelText   $csharpLabel

Problem 2: Nie znaleziono ścieżki do folderu zasobów w środowisku wdrożeniowym

GdPicture.NET: Ścieżka ustawiona w _ocr.ResourceFolder rozwiązuje się na maszynie deweloperskiej ale zawodzi w produkcji. Typowymi objawami są ciche niepowodzenia OCR zwracające puste wyniki lub ogólne błędy GdPictureStatus, które nie wskazują brakującego pliku.

Rozwiązanie: Całkowicie usuń przypisanie ResourceFolder. Obsługa języka angielskiego jest wbudowana w pakiet IronOCR NuGet. Dodatkowe języki instaluje się jako pakiety NuGet. Nie ma ścieżki systemu plików do skonfigurowania ani wdrożenia:

# Remove the filesystem folder from deployment artifacts
# Add language NuGet packages to the project instead
:InstallCmd dotnet add package IronOcr.Languages.French, IronOcr.Languages.German
# Remove the filesystem folder from deployment artifacts
# Add language NuGet packages to the project instead
:InstallCmd dotnet add package IronOcr.Languages.French, IronOcr.Languages.German
SHELL

Problem 3: Obsługa błędów GdPictureStatus zastąpiona przez wyjątki

GdPicture.NET: Każda operacja zwraca lub ustawia wartość enum GdPictureStatus, którą trzeba natychmiast sprawdzić. Kod jest zagęszczony ochronami if (status != GdPictureStatus.OK). Niektóre kody statusu są ogólne (np. Error, InvalidParameter) i wymagają konsultacji z dokumentacją w celu ustalenia pierwotnej przyczyny.

Rozwiązanie:IronOCR generuje typowe wyjątki .NET. Zastąp sprawdzanie statusu blokami try/catch. Standardowe IOException i FileNotFoundException obejmują niepowodzenia wejściowe; IronOcr.Exceptions.OcrException obejmuje błędy specyficzne dla OCR. Zobacz przewodnik konfiguracji IronTesseract, aby zapoznać się z zalecanymi wzorcami obsługi błędów:

try
{
    var result = new IronTesseract().Read(imagePath);
    return result.Text;
}
catch (FileNotFoundException ex)
{
    _logger.LogError("Input file missing: {Path}", ex.FileName);
    throw;
}
catch (IronOcr.Exceptions.OcrException ex)
{
    _logger.LogError("OCR processing failed: {Message}", ex.Message);
    throw;
}
try
{
    var result = new IronTesseract().Read(imagePath);
    return result.Text;
}
catch (FileNotFoundException ex)
{
    _logger.LogError("Input file missing: {Path}", ex.FileName);
    throw;
}
catch (IronOcr.Exceptions.OcrException ex)
{
    _logger.LogError("OCR processing failed: {Message}", ex.Message);
    throw;
}
Imports IronOcr
Imports System.IO

Try
    Dim result = New IronTesseract().Read(imagePath)
    Return result.Text
Catch ex As FileNotFoundException
    _logger.LogError("Input file missing: {Path}", ex.FileName)
    Throw
Catch ex As IronOcr.Exceptions.OcrException
    _logger.LogError("OCR processing failed: {Message}", ex.Message)
    Throw
End Try
$vbLabelText   $csharpLabel

Problem 4: Przestrzeń nazw GdPicture14 w wielu plikach

GdPicture.NET: Numer wersji w przestrzeni nazw oznacza, że projektowa dyrektywa using GdPicture14; istnieje w dziesiątkach plików. Po migracji wszystkie te muszą być zastąpione przez using IronOcr;.

Rozwiązanie: Użyj globalnej funkcji znajdź-i-zastąp w całych plikach .cs, a następnie zweryfikuj, że żadne odniesienia do GdPicture nie pozostają:

# Find all files with GdPicture namespace
grep -rln "using GdPicture" --include="*.cs" .

# After replacing, verify nothing remains
grep -rn "GdPicture14\|GdPictureOCR\|GdPictureImaging\|GdPicturePDF" --include="*.cs" .
# Find all files with GdPicture namespace
grep -rln "using GdPicture" --include="*.cs" .

# After replacing, verify nothing remains
grep -rn "GdPicture14\|GdPictureOCR\|GdPictureImaging\|GdPicturePDF" --include="*.cs" .
SHELL

Problem 5: Logika liczenia klatek TIFF

GdPicture.NET: Kod iterujący ramki TIFF często miesza wywołania pomiędzy GdPictureImaging.GetPageCount(imageId) a GdPicturePDF.GetPageCount() w zależności od tego, jak plik został załadowany. Indeks ramki zaczyna się od 1.

Rozwiązanie: input.LoadImageFrames(path) obsługuje wszystkie ramki automatycznie. Jeśli Twój istniejący kod przetwarza tylko konkretne ramki, użyj input.LoadImageFrames(path, frameNumbers) z tablicą indeksów opartą na zerze. Dostęp do wyników dla poszczególnych ramek poprzez result.Pages, które również są indeksowane od zera. W instrukcji dotyczącej plików wejściowych TIFF wyraźnie opisano zachowanie indeksu.

Problem 6: Przetwarzanie wstępne wymaga wtyczki do obrazówania dokumentów

GdPicture.NET: Operacje deskew i despeckle należą do wtyczki GdPictureDocumentImaging, która wymaga oddzielnego zakupu licencji. Zespoły, które pominęły tę wtyczkę, często borykają się z problemami z dokładnością OCR w przypadku zeskanowanych dokumentów o przekrzywionych lub zaszumionych stronach.

Rozwiązanie: Metody przetwarzania wstępnego IronOCRsą częścią pakietu podstawowego. Dodaj Deskew() i DeNoise() bezpośrednio na OcrInput. Przewodnik po korekcji jakości obrazu obejmuje wszystkie dostępne filtry, w tym Contrast(), Sharpen(), Binarize(), i DeepCleanBackgroundNoise() dla skanów o poważnie zniszczonej jakości:

using var input = new OcrInput();
input.LoadImage("scanned-document.tiff");
input.Deskew();        // no separate plugin license
input.DeNoise();
input.Contrast();

var result = new IronTesseract().Read(input);
using var input = new OcrInput();
input.LoadImage("scanned-document.tiff");
input.Deskew();        // no separate plugin license
input.DeNoise();
input.Contrast();

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

Using input As New OcrInput()
    input.LoadImage("scanned-document.tiff")
    input.Deskew() ' no separate plugin license
    input.DeNoise()
    input.Contrast()

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

Lista kontrolna migracji GdPicture.NET

Przed migracją

Przed wprowadzeniem zmian należy przeprowadzić audyt kodu źródłowego w celu zlokalizowania wszystkich zależności GdPicture:

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

# Find all image ID creation points
grep -rn "CreateGdPictureImageFromFile\|CreateGdPictureImageFromStream\|RenderPageToGdPictureImage\|CloneImage" --include="*.cs" .

# Find all release calls — these map to using block boundaries
grep -rn "ReleaseGdPictureImage" --include="*.cs" .

# Find all resource folder assignments
grep -rn "ResourceFolder" --include="*.cs" .

# Find all OCR result ID accesses
grep -rn "RunOCR\|GetOCRResult\|resultId" --include="*.cs" .

# Find all GdPictureStatus checks
grep -rn "GdPictureStatus\|GetStat()" --include="*.cs" .

# Count GdPicture-dependent files
grep -rln "GdPicture14" --include="*.cs" . | wc -l
# Find all GdPicture namespace imports
grep -rn "using GdPicture" --include="*.cs" .

# Find all image ID creation points
grep -rn "CreateGdPictureImageFromFile\|CreateGdPictureImageFromStream\|RenderPageToGdPictureImage\|CloneImage" --include="*.cs" .

# Find all release calls — these map to using block boundaries
grep -rn "ReleaseGdPictureImage" --include="*.cs" .

# Find all resource folder assignments
grep -rn "ResourceFolder" --include="*.cs" .

# Find all OCR result ID accesses
grep -rn "RunOCR\|GetOCRResult\|resultId" --include="*.cs" .

# Find all GdPictureStatus checks
grep -rn "GdPictureStatus\|GetStat()" --include="*.cs" .

# Count GdPicture-dependent files
grep -rln "GdPicture14" --include="*.cs" . | wc -l
SHELL

Przed modyfikacją kodu należy udokumentować następujące elementy:

  • Które pliki zawierają odniesienia do GdPictureImaging, GdPictureOCR, i GdPicturePDF
  • Ile istnieje różnych par tworzenia/wydania identyfikatorów obrazów
  • Czy używana jest wtyczka do przetwarzania obrazów dokumentów (korekcja krzywizny, usuwanie plam)
  • W jakim języku pliki traineddata znajdują się w folderze zasobów
  • Które skrypty wdrożeniowe lub pliki Docker odwołują się do ścieżki folderu zasobów

Migracja kodu

  1. Usuń wszystkie pakiety GdPicture NuGet z pliku projektu
  2. Zainstaluj IronOcr przez NuGet
  3. Zainstaluj pakiety IronOcr.Languages.* dla każdego języka wcześniej w folderze zasobów
  4. Dodaj IronOcr.License.LicenseKey = "..." do Program.cs lub Startup.cs
  5. Usuń wywołanie LicenseManager.RegisterKEY() oraz obiekt menedżera licencji
  6. Usuń wszystkie deklaracje pól GdPictureImaging i inicjalizację konstruktora
  7. Usuń wszystkie deklaracje pól GdPictureOCR oraz przypisanie ResourceFolder
  8. Usuń wszystkie deklaracje pól GdPicturePDF używane do zadań OCR
  9. Zastąp każdy blok int imageId = _imaging.CreateGdPictureImage*(...) poprzez using var input = new OcrInput(); input.Load*(...)
  10. Zastąp każde _ocr.SetImage(imageId); _ocr.Language = "..."; string resultId = _ocr.RunOCR(); with var result = new IronTesseract().Read(input);
  11. Zamień _ocr.GetOCRResultText(resultId) na result.Text
  12. Usuń wszystkie wywołania _imaging.ReleaseGdPictureImage(imageId)
  13. Zamień sprawdzenia GdPictureStatus na bloki try/catch
  14. Zamień pętle ramki TIFF na input.LoadImageFrames(path)
  15. Zamień pdf.OcrPage(...) + pdf.SaveToFile(...) na result.SaveAsSearchablePdf(outputPath)
  16. Usuń ścieżkę do folderu zasobów ze skryptów wdrażania i obrazów Docker
  17. Zaktualizuj using GdPicture14; do using IronOcr; we wszystkich dotkniętych plikach

Po migracji

Po wprowadzeniu wszystkich zmian w kodzie, przed wdrożeniem do środowiska produkcyjnego sprawdź, czy:

  • Jedno OCR obrazu zwraca oczekiwany tekst bez NullReferenceException z usuniętych komponentów
  • Przetwarzanie wielu stron TIFF obejmuje wszystkie ramki i generuje tekst dla każdej strony przez result.Pages
  • OCR plików PDF przetwarza wszystkie strony bez zwiększania zużycia pamięci w partii ponad 50 dokumentów
  • Wejście strumienia akceptuje MemoryStream i FileStream bez pośrednich zapisów plików
  • Async OCR integruje się z modułami obsługi żądań ASP.NET Core bez blokowania puli wątków
  • Równoległe przetwarzanie wsadowe z Parallel.ForEach generuje dokładne wyniki we wszystkich wątkach
  • Wszystkie pakiety językowe (francuski, niemiećki itp.) aktywują się poprawnie poprzez pakiety NuGet
  • Filtry przetwarzania wstępnego (prostowanie, usuwanie szumów) poprawiają dokładność skanowanych dokumentów
  • Wygenerowany plik PDF z możliwością wyszukiwania jest odczytywany przez przeglądarki PDF i aplikacje do wyszukiwania tekstu
  • Żadne odniesienia do przestrzeni nazw GdPicture nie pozostają w żadnym pliku .cs po migracji
  • Profil pamięci wykazuje stabilne zużycie przy długotrwałym obciążeniu (brak wycieków alokacji obrazów)
  • Wdrożenie przebiega pomyślnie na systemach Linux i Docker bez błędów ścieżki systemu plików

Kluczowe korzyści z migracji do IronOCR

Bezpieczeństwo pamięci wymuszane przez kompilator. Cykl życia identyfikatorów obrazów, który GdPicture wymaga od programistów ręcznego utrzymywania, całkowicie znika. OcrInput implementuje IDisposable, a bloki using wymuszają czyszczenie na końcu każdego zakresu — łącznie ze ścieżkami wyjątków. Brak List<int> do utrzymania, brak bloków finally do zapamiętania, brak incydentów pamięci produkcyjnej z powodu pominiętych wywołań uwolnienia.

Pojedynczy pakiet dla każdego scenariusza OCR. Image OCR, PDF OCR, przetwarzanie wielostronicowych TIFF, generowanie przeszukiwalnych PDF, filtry wstępnego przetwarzania, odczyt kodów kreskowych, i ponad 125 pakietów językowych są wszystkie dostępne z pakietu NuGet IronOcr i jego towarzyszy językowych. Nie ma żadnych ograniczeń, które wymagałyby zakupu drugiej lub trzeciej licencji, zanim możliwe będzie korzystanie ze standardowego przepływu pracy. Pełną listę funkcji można znaleźć w przeglądzie funkcji IronOCR.

Natywna asynchronizacja i bezpieczeństwo wątkowe. ReadAsync jest prawdziwą metodą asynchroniczną, nie osłoną Task.Run. IronTesseract może być używany bezpiecznie na wszystkich wątkach bez synchronizacji. Usługi przetwarzania dokumentów, które wcześniej wymagały inicjalizacji komponentów na wątek i serializacji z semaforem, redukują się do pojedynczej współdzielonej instancji z Parallel.ForEach. Przewodnik po asynchronicznym OCR obejmuje zarówno ASP.NET Core, jak i wzorce usług hostowanych.

Zero konfiguracji wdrożeniowej.IronOCR nie wymaga żadnych ścieżek w systemie plików, zewnętrznych plików językowych, natywnych plików binarnych ani skryptów wdrożeniowych. Etap przywracania NuGet zapewnia wszystko, czego potrzebuje aplikacja. Obrazy Docker, wdrożenia usługi Azure App Service i serwery Linux działają identycznie jak maszyna programistyczna. Przewodnik po wdrażaniu Docker oraz przewodnik po wdrażaniu Azure przedstawiają konfiguracje gotowe do produkcji.

Stabilna przestrzeń nazw wersji. using IronOcr nie uległ zmianie w głównych wydaniach wersji. Numery wersji NuGet obsługują wersjonowanie poprzez standardowy model zarządzania pakietami. Przyszłe większe aktualizacje nie wymagają przeszukiwania i zamiany importu przestrzeni nazw w całym kodzie źródłowym.

Przewidywalne licencjonowanie wieczyste.IronOCR kosztuje $999 (Lite), $1 499 (Plus), $2 999 (Professional) i $5 999 (Unlimited) — jednorazowe zakupy licencji wieczystej obejmujące rok aktualizacji, z opcjonalnym odnowieniem co rok. Wszystkie funkcje są dostępne na każdym poziomie. Nie ma żadnych wtyczek dla poszczególnych funkcji, żadnych kosztów za stronę ani żadnych zobowiązań dotyczących konserwacji po pierwszym roku. Zespoły, które wcześniej poniosły koszty licencji wtyczek GdPicture w wysokości ponad 8 000 USD w ramach procesu opartego wyłącznie na OCR, odzyskują tę różnicę w ciągu pierwszego cyklu licencyjnego. Pełne informacje o cenach są dostępne na stronie licencyjnej IronOCR.

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

Często Zadawane Pytania

Dlaczego warto przejść z GdPicture.NET OCR na IronOCR?

Typowe czynniki motywujące to eliminacja złożoności interoperacyjności COM, zastąpienie zarządzania licencjami opartego na plikach, uniknięcie rozliczeń za stronę, umożliwienie wdrażania w Dockerze/kontenerach oraz przyjęcie natywnego dla NuGet przepływu pracy, który integruje się ze standardowymi narzędziami .NET.

Jakie są główne zmiany w kodzie podczas migracji z GdPicture.NET OCR do IronOCR?

Zastąp sekwencje inicjalizacji GdPicture.NET instancjonowaniem IronTesseract, usuń zarządzanie cyklem życia COM (jawnymi wzorcami Create/Load/Close) i zaktualizuj nazwy właściwości wyników. W rezultacie znacznie zmniejsza się liczba powtarzających się linii kodu.

Jak zainstalować IronOCR, aby rozpocząć migrację?

Uruchom polecenie „Install-Package IronOcr” w konsoli menedżera pakietów lub „dotnet add package IronOcr” w interfejsie CLI. Pakiety językowe są oddzielnymi pakietami: na przykład „dotnet add package IronOcr.Languages.French” dla języka francuskiego.

Czy IronOCR dorównuje dokładnością OCR GdPicture.NET OCR w przypadku standardowych dokumentów biznesowych?

IronOCR zapewnia wysoką dokładność w przypadku standardowych treści biznesowych, w tym faktur, umów, paragonów i formularzy wypełnionych na komputerze. Filtry przetwarzania wstępnego obrazu (prostowanie, usuwanie szumów, wzmacnianie kontrastu) dodatkowo poprawiają rozpoznawanie w przypadku pogorszonej jakości danych wejściowych.

W jaki sposób IronOCR obsługuje dane językowe, które GdPicture.NET OCR instaluje oddzielnie?

Dane językowe w IronOCR są dystrybuowane jako pakiety NuGet. Polecenie „dotnet add package IronOcr.Languages.German” instaluje obsługę języka niemiećkiego. Nie wymaga to ręcznego umieszczania plików ani podawania ścieżek katalogów.

Czy migracja z GdPicture.NET OCR do IronOCR wymaga zmian w infrastrukturze wdrożeniowej?

IronOCR wymaga mniej zmian w infrastrukturze niż GdPicture.NET OCR. Nie ma ścieżek binarnych SDK, lokalizacji plików licencyjnych ani konfiguracji serwerów licencyjnych. Pakiet NuGet zawiera kompletny silnik OCR, a klucz licencyjny jest ciągiem znaków ustawionym w kodzie aplikacji.

Jak skonfigurować licencjonowanie IronOCR po migracji?

W kodzie uruchamiającym aplikację przypisz IronOcr.License.LicenseKey = „YOUR-KEY”. W Dockerze lub Kubernetesie zapisz klucz jako zmienną środowiskową i odczytaj go podczas uruchamiania. Użyj License.IsValidLicense do sprawdzenia ważności przed przyjęciem ruchu.

Czy IronOCR może przetwarzać pliki PDF w taki sam sposób jak GdPicture.NET?

Tak. IronOCR odczytuje zarówno natywne, jak i zeskanowane pliki PDF. Należy utworzyć instancję IronTesseract, wywołać ocr.Read(input), gdzie input jest ścieżką do pliku PDF lub obiektem OcrPdfInput, a następnie iterować strony OcrResult. Nie jest wymagany oddzielny proces renderowania plików PDF.

W jaki sposób IronOCR radzi sobie z wątkami podczas przetwarzania dużych ilości danych?

IronTesseract można bezpiecznie instancjonować dla każdego wątku. Uruchom jedną instancję na wątek w Parallel.ForEach lub puli zadań, uruchom OCR równolegle i usuń każdą instancję po zakończeniu. Nie jest wymagany żaden stan globalny ani blokowanie.

Jakie formaty wyjściowe obsługuje IronOCR po wyodrębnieniu tekstu?

IronOCR zwraca ustrukturyzowane wyniki, w tym tekst, współrzędne słów, wyniki pewności i strukturę strony. Opcje eksportu obejmują zwykły tekst, PDF z możliwością wyszukiwania oraz obiekty wyników ustrukturyzowanych do dalszego przetwarzania.

Czy ceny IronOCR są bardziej przewidywalne niż ceny GdPicture.NET OCR w przypadku skalowania obciążeń?

IronOCR stosuje licencję wieczystą z opłatą ryczałtową, bez opłat za stronę lub wolumen. Niezależnie od tego, czy przetwarzasz 10 000, czy 10 milionów stron, koszt licencji pozostaje stały. Opcje licencji wolumenowych i zespołowych znajdują się na stronie z cennikiem IronOCR.

Co stanie się z moimi istniejącymi testami po migracji z GdPicture.NET OCR do IronOCR?

Testy sprawdzające wyodrębnioną treść tekstową powinny nadal przechodzić pomyślnie po migracji. Testy weryfikujące wzorce wywołań API lub cykl życia obiektów COM będą wymagały aktualizacji, aby odzwierciedlały prostszy model inicjalizacji i wyników IronOCR.

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

Zespół wsparcia Iron

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