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
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
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
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
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"
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");
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
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
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
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
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
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
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
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
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
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
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" .
// 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
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
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
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" .
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
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
Przed modyfikacją kodu należy udokumentować następujące elementy:
- Które pliki zawierają odniesienia do
GdPictureImaging,GdPictureOCR, iGdPicturePDF - 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
traineddataznajdują się w folderze zasobów - Które skrypty wdrożeniowe lub pliki Docker odwołują się do ścieżki folderu zasobów
Migracja kodu
- Usuń wszystkie pakiety GdPicture NuGet z pliku projektu
- Zainstaluj
IronOcrprzez NuGet - Zainstaluj pakiety
IronOcr.Languages.*dla każdego języka wcześniej w folderze zasobów - Dodaj
IronOcr.License.LicenseKey = "..."doProgram.cslubStartup.cs - Usuń wywołanie
LicenseManager.RegisterKEY()oraz obiekt menedżera licencji - Usuń wszystkie deklaracje pól
GdPictureImagingi inicjalizację konstruktora - Usuń wszystkie deklaracje pól
GdPictureOCRoraz przypisanieResourceFolder - Usuń wszystkie deklaracje pól
GdPicturePDFużywane do zadań OCR - Zastąp każdy blok
int imageId = _imaging.CreateGdPictureImage*(...)poprzezusing var input = new OcrInput();input.Load*(...) - Zastąp każde
_ocr.SetImage(imageId); _ocr.Language = "..."; string resultId = _ocr.RunOCR();withvar result = new IronTesseract().Read(input); - Zamień
_ocr.GetOCRResultText(resultId)naresult.Text - Usuń wszystkie wywołania
_imaging.ReleaseGdPictureImage(imageId) - Zamień sprawdzenia
GdPictureStatusna bloki try/catch - Zamień pętle ramki TIFF na
input.LoadImageFrames(path) - Zamień
pdf.OcrPage(...)+pdf.SaveToFile(...)naresult.SaveAsSearchablePdf(outputPath) - Usuń ścieżkę do folderu zasobów ze skryptów wdrażania i obrazów Docker
- Zaktualizuj
using GdPicture14;dousing 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
NullReferenceExceptionz 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
MemoryStreamiFileStreambez 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.ForEachgeneruje 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
.cspo 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.
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.

