Przejdź do treści stopki
FILMY

Jak naprawić kolory obrazu do odczytu w C#

Ten przewodnik przeprowadza programistów .NET przez proces zastąpienia integracji REST API OCR.space biblioteką IronOCR, natywną biblioteką .NET dostarczaną jako pojedynczy pakiet NuGet. Obejmuje to zamianę pakietów, czyszczenie przestrzeni nazw oraz cztery konkretne scenariusze migracji kodu, które są specyficzne dla przejścia z REST na lokalny: eliminację przesyłania wieloczęściowego, usunięcie kodowania base64, wymianę silnika OCR oraz ekstrakcję danych strukturalnych. Programiści, którzy przeczytali artykuł porównawczy z fazy 1, zauważą, że niniejszy przewodnik skupia się na mechanicznych etapach samej migracji, a nie na porównaniu funkcji.

Dlaczego warto przejść z OCR.space

OCR.space wypełnia prawdziwą niszę: bezpłatne eksperymenty dla programistów, którzy chcą przetestować OCR w ciągu jednego popołudnia bez konieczności instalowania czegokolwiek. Problem polega na tym, że bezpłatny poziom jest przeznaczony do tworzenia prototypów, a nie do produkcji. Gdy aplikacja .NET zaczyna obsługiwać duże ilości dokumentów, musi spełniać wymogi zgodności lub jest tworzona przez zespół, wszystkie cechy integracji OCR.space działają na niekorzyść aplikacji.

Brak pakietu NuGet oznacza brak SDK i IntelliSense. OCR.space udostępnia punkt końcowy REST oraz dokumentację. Integracja z platformą .NET — tworzenie klienta HTTP, serializacja żądań, deserializacja odpowiedzi, obsługa błędów i logika ponownych prób — leży całkowicie w gestii programisty. Nie jest to drobna niedogodność. Minimalny klient to ponad 80 linii kodu infrastruktury przed napisaniem pierwszej metody logiki biznesowej. Kod ten jest niezmienny we wszystkich integracjach OCR.space w każdym kodzie źródłowym .NET, co z czasem powoduje gromadzenie się błędów i obciążenie związane z utrzymaniem.

Limity szybkości nakładają sztuczne ograniczenia na aplikacje produkcyjne. W ramach bezpłatnego planu obowiązuje limit 60 żądań na minutę i 500 żądań dziennie na adres IP. Oba ograniczenia są nieprzekraczalne. Aplikacja, która przekroczy 500 żądań między północą a następną północą, otrzymuje odpowiedzi o błędzie do momentu zresetowania licznika. Systemy produkcyjne działające w wspólnych sieciach biurowych lub wspólnych środowiskach CI/CD mogą wyczerpać dzienny limit jeszcze przed końcem godzin pracy.

Dokumenty opuszczają infrastrukturę użytkownika przy każdym wywołaniu. OCR.space nie oferuje opcji wdrożenia lokalnego. Każde zgłoszenie przekazuje dokument — faktury, dokumentację medyczną, umowy, dokumenty tożsamości — do serwerów w chmurze OCR.space. HIPAA, RODO oraz wewnętrzne zasady klasyfikacji danych, które zabraniają przekazywania poufnych dokumentów stronom trzecim, sprawiają, że OCR.space jest architektonicznie niekompatybilny, niezależnie od kontroli umownych.

Wersja bezpłatna generuje pliki PDF z możliwością wyszukiwania, opatrzone znakiem wodnym. Aplikacje, których wynikiem jest plik PDF z możliwością wyszukiwania — systemy archiwizacji dokumentów, platformy zapewniające zgodność z przepisami, portale dokumentów dla klientów — nie mogą korzystać z bezpłatnej wersji OCR.space w tym celu. Znak wodny jest osadzony w pliku PDF i nie można go usunąć bez wykupienia płatnego planu.

**Ceny subskrypcji rosną wraz z wielkością; poziom PRO OCR.space przy $144 rocznie przekracza wieczysta cene wejscia IronOCR$999 przed szesnastym rokiem. Zespoly, ktore przewiduja wzrost wolumenu dokumentow poza progiem poziomu darmowego, stoja w obliczu narastajacych kosztow subskrypcji w porownaniu z stala wieczysta licencja. Licencja $999 Lite pokrywa jednego dewelopera i jedno miejsce wdrozenia bez oplat za zadanie przy dowolnym wolumenie. Szczegóły dotyczące poziomów licencji można znaleźć na stronie licencyjnej IronOCR.

Podstawowy problem

OCR.space wymaga zbudowania kompletnego klienta HTTP przed przetworzeniem pojedynczego dokumentu:

// OCR.space: 80+ lines of infrastructure before business logic
public class OcrSpaceApiClient : IDisposable
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;
    private readonly SemaphoreSlim _rateLimiter; // You implement this

    public OcrSpaceApiClient(string apiKey)
    {
        _httpClient = new HttpClient();
        _httpClient.Timeout = TimeSpan.FromSeconds(120);
        _rateLimiter = new SemaphoreSlim(60, 60); // Free tier: 60/min
    }
    // ... 70+ more lines of HTTP plumbing follow
}
// OCR.space: 80+ lines of infrastructure before business logic
public class OcrSpaceApiClient : IDisposable
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;
    private readonly SemaphoreSlim _rateLimiter; // You implement this

    public OcrSpaceApiClient(string apiKey)
    {
        _httpClient = new HttpClient();
        _httpClient.Timeout = TimeSpan.FromSeconds(120);
        _rateLimiter = new SemaphoreSlim(60, 60); // Free tier: 60/min
    }
    // ... 70+ more lines of HTTP plumbing follow
}
Imports System
Imports System.Net.Http
Imports System.Threading

' OCR.space: 80+ lines of infrastructure before business logic
Public Class OcrSpaceApiClient
    Implements IDisposable

    Private ReadOnly _httpClient As HttpClient
    Private ReadOnly _apiKey As String
    Private ReadOnly _rateLimiter As SemaphoreSlim ' You implement this

    Public Sub New(apiKey As String)
        _httpClient = New HttpClient()
        _httpClient.Timeout = TimeSpan.FromSeconds(120)
        _rateLimiter = New SemaphoreSlim(60, 60) ' Free tier: 60/min
    End Sub
    ' ... 70+ more lines of HTTP plumbing follow

    Public Sub Dispose() Implements IDisposable.Dispose
        _httpClient.Dispose()
        _rateLimiter.Dispose()
    End Sub
End Class
$vbLabelText   $csharpLabel

IronOCR to pakiet NuGet. Cały tekst zlecenia został już napisany:

// IronOCR: no client to build, no rate limiter to manage
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("document.jpg");
Console.WriteLine(result.Text);
// IronOCR: no client to build, no rate limiter to manage
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("document.jpg");
Console.WriteLine(result.Text);
Imports IronOcr

' IronOCR: no client to build, no rate limiter to manage
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim result = New IronTesseract().Read("document.jpg")
Console.WriteLine(result.Text)
$vbLabelText   $csharpLabel

IronOCR vs OCR.space: Porównanie funkcji

Poniższa tabela przedstawia bezpośrednie odpowiedniki pojęć i ograniczeń OCR.space w IronOCR.

Funkcja OCR.space IronOCR
Pakiet NuGet Brak — tylko REST API IronOcr — natywny .NET
SDK / IntelliSense Brak — ręczny JSON Pełne — typowane API
Wymagane są modele niestandardowe Nie Nie
Miejsce przetwarzania Serwery w chmurze OCR.space Lokalne — w trakcie realizacji
Zależność od Internetu Wymagane dla każdego wywołania None
Wdrożenie w środowisku izolowanym Nieobsługiwane W pełni obsługiwane
Limity szybkości 60/min, 500/dzień (bezpłatnie) None
Limit rozmiaru pliku 5 MB (poziom bezpłatny) Dostępna pamięć
Plik wejściowy PDF Tak (ograniczony, 5 MB) Tak — tłumaczenie wykonane przez native speakera, bez ograniczeń co do objętości
Wynik w formacie PDF z możliwością wyszukiwania Znaczek wodny w wersji darmowej Czysty wynik, wszystkie poziomy
Automatyczne przetwarzanie wstępne Po stronie serwera, bez kontroli programisty Wyrównanie, usuwanie szumów, kontrast, binarizacja, wyostrzanie
Obsługa języków ~25 języków Ponad 125 pakietów językowych dostępnych za pośrednictwem NuGet
Wiele języków w jednym dokumencie Nieobsługiwane Tak — OcrLanguage.French + OcrLanguage.German
Struktura wyjściowa (słowa, wiersze) Tylko zwykły tekst Strony, akapity, wiersze, słowa z współrzędnymi
Wskaźniki pewności na poziomie słów Niedostępne Tak — word.Confidence
OCR oparte na regionie Nieobsługiwane Tak — CropRectangle
Odczytywanie BarCode Nieobsługiwane Tak — ReadBarCodes = true
Generowanie plików PDF z możliwością wyszukiwania Z znakiem wodnym (bezpłatna), bez znaku wodnego (płatna) Czysty wynik — wszystkie poziomy licencji
Zgodność z HIPAA / RODO Ryzyko — dane przesyłane na zewnątrz Tak — brak zewnętrznej transmisji danych
Model cenowy Miesięczna subskrypcja Jednorazowa, wieczysta
Cena wywoławcza 12 USD/miesiąc (144 USD/rok) $999 jednorazowy
Kompatybilność z platformą .NET HttpClient — dowolny .NET .NET 4.6.2+, .NET 5/6/7/8/9
Wdrażanie na wielu platformach Wymagane połączenie z Internetem Windows, Linux, macOS, Docker, Azure, AWS

Szybki start: Migracja z OCR.space do IronOCR

Krok 1: Zastąp pakiet NuGet

OCR.space nie posiada pakietu NuGet, który można odinstalować. Usun caly kod infrastrukturalny zwiazany z OCR.space z projektu: klase opakowujaca HttpClient, ograniczenie szybkosci SemaphoreSlim, modele wynikow niestandardowych i typy wyjatkow niestandardowych. Wszystkie one sa zastepowane przez pakiet NuGet IronOCR.

Zainstaluj IronOCR ze strony IronOCR NuGet:

dotnet add package IronOcr

Krok 2: Aktualizacja przestrzeni nazw

Usuń przestrzenie nazw OCR.space HTTP i JSON. Dodaj przestrzeń nazw IronOCR:

// Before (OCR.space — manually written infrastructure)
using System.Net.Http;
using System.Text.Json;
using System.Threading;

// After (IronOCR)
using IronOcr;
// Before (OCR.space — manually written infrastructure)
using System.Net.Http;
using System.Text.Json;
using System.Threading;

// After (IronOCR)
using IronOcr;
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading

Imports IronOcr
$vbLabelText   $csharpLabel

Krok 3: Inicjalizacja licencji

Dodaj inicjalizację licencji raz podczas uruchamiania aplikacji — nie przy każdym żądaniu:

// Program.cs or application startup
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
// Program.cs or application startup
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
Imports IronOcr

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

Przykłady migracji kodu

Zastąpienie przesyłania plików za pomocą MultipartFormDataContent

OCR.space wymaga budowania MultipartFormDataContent z bajtami pliku i kluczem API, nastepnie wysylania POST-em do koncowego punktu w chmurze. Dokument opuszcza infrastrukturę przy każdym wywołaniu.

Podejście OCR.space:

// MultipartFormDataContent: file upload to cloud on every request
public async Task<string> UploadAndExtract(string imagePath)
{
    using var content = new MultipartFormDataContent();
    var imageBytes = File.ReadAllBytes(imagePath);

    // Document is transmitted to OCR.space servers here
    content.Add(new ByteArrayContent(imageBytes), "file", Path.GetFileName(imagePath));
    content.Add(new StringContent(_apiKey), "apikey");
    content.Add(new StringContent("eng"), "language");
    content.Add(new StringContent("2"), "OCREngine"); // Select Engine 2

    var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
    response.EnsureSuccessStatusCode();

    string json = await response.Content.ReadAsStringAsync();
    using var doc = JsonDocument.Parse(json);

    // Navigate JSON tree manually — no typed result
    return doc.RootElement
        .GetProperty("ParsedResults")[0]
        .GetProperty("ParsedText")
        .GetString() ?? string.Empty;
}
// MultipartFormDataContent: file upload to cloud on every request
public async Task<string> UploadAndExtract(string imagePath)
{
    using var content = new MultipartFormDataContent();
    var imageBytes = File.ReadAllBytes(imagePath);

    // Document is transmitted to OCR.space servers here
    content.Add(new ByteArrayContent(imageBytes), "file", Path.GetFileName(imagePath));
    content.Add(new StringContent(_apiKey), "apikey");
    content.Add(new StringContent("eng"), "language");
    content.Add(new StringContent("2"), "OCREngine"); // Select Engine 2

    var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
    response.EnsureSuccessStatusCode();

    string json = await response.Content.ReadAsStringAsync();
    using var doc = JsonDocument.Parse(json);

    // Navigate JSON tree manually — no typed result
    return doc.RootElement
        .GetProperty("ParsedResults")[0]
        .GetProperty("ParsedText")
        .GetString() ?? string.Empty;
}
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks

Public Class YourClassName
    Private _apiKey As String
    Private _httpClient As HttpClient

    Public Async Function UploadAndExtract(imagePath As String) As Task(Of String)
        Using content As New MultipartFormDataContent()
            Dim imageBytes = File.ReadAllBytes(imagePath)

            ' Document is transmitted to OCR.space servers here
            content.Add(New ByteArrayContent(imageBytes), "file", Path.GetFileName(imagePath))
            content.Add(New StringContent(_apiKey), "apikey")
            content.Add(New StringContent("eng"), "language")
            content.Add(New StringContent("2"), "OCREngine") ' Select Engine 2

            Dim response = Await _httpClient.PostAsync("https://api.ocr.space/parse/image", content)
            response.EnsureSuccessStatusCode()

            Dim json As String = Await response.Content.ReadAsStringAsync()
            Using doc = JsonDocument.Parse(json)
                ' Navigate JSON tree manually — no typed result
                Return doc.RootElement _
                    .GetProperty("ParsedResults")(0) _
                    .GetProperty("ParsedText") _
                    .GetString() OrElse String.Empty
            End Using
        End Using
    End Function
End Class
$vbLabelText   $csharpLabel

Podejście IronOCR:

// OcrInput replaces the entire upload + JSON pipeline
public string ExtractFromFile(string imagePath)
{
    using var input = new OcrInput();
    input.LoadImage(imagePath); // Stays local — no network call

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

    return result.Text; // Typed property — no JSON navigation
}
// OcrInput replaces the entire upload + JSON pipeline
public string ExtractFromFile(string imagePath)
{
    using var input = new OcrInput();
    input.LoadImage(imagePath); // Stays local — no network call

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

    return result.Text; // Typed property — no JSON navigation
}
Imports IronTesseract

Public Function ExtractFromFile(ByVal imagePath As String) As String
    Using input As New OcrInput()
        input.LoadImage(imagePath) ' Stays local — no network call

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

        Return result.Text ' Typed property — no JSON navigation
    End Using
End Function
$vbLabelText   $csharpLabel

OcrInput to lokalny zastepnik dla MultipartFormDataContent. Obsługuje ścieżki plików, tablice bajtów, strumienie i wielostronicowe pliki TIFF za pośrednictwem spójnego interfejsu API. HttpClient, wstawianie kluczy API i nawigacja po JSON zupelnie znikaja. Instrukcja dotycząca wprowadzania obrazów obejmuje wszystkie obsługiwane formaty wejściowe.

Eliminacja kodowania Base64

Kiedy integracje OCR.space uzywaja parametru base64Image w formularzu zamiast parametru przesylania pliku, kod czyta plik do bajtow, koduje do Base64, buduje lancuch URI danych i osadza go w FormUrlEncodedContent.IronOCR akceptuje surowe bajty bezpośrednio, bez etapu kodowania.

Podejście OCR.space:

// base64Image parameter: read → encode → embed in form → POST → parse
public async Task<string> ExtractViaBase64(string imagePath)
{
    byte[] imageBytes = await File.ReadAllBytesAsync(imagePath);
    string base64Image = Convert.ToBase64String(imageBytes); // Mandatory encoding step

    // Embed as data URI — adds 33% overhead to payload size
    string mimeType = "image/png";
    string dataUri = $"data:{mimeType};base64,{base64Image}";

    var formContent = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("apikey", _apiKey),
        new KeyValuePair<string, string>("base64Image", dataUri),
        new KeyValuePair<string, string>("language", "eng"),
        new KeyValuePair<string, string>("isOverlayRequired", "false")
    });

    var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", formContent);
    string json = await response.Content.ReadAsStringAsync();

    using var doc = JsonDocument.Parse(json);
    return doc.RootElement
        .GetProperty("ParsedResults")[0]
        .GetProperty("ParsedText")
        .GetString() ?? string.Empty;
}
// base64Image parameter: read → encode → embed in form → POST → parse
public async Task<string> ExtractViaBase64(string imagePath)
{
    byte[] imageBytes = await File.ReadAllBytesAsync(imagePath);
    string base64Image = Convert.ToBase64String(imageBytes); // Mandatory encoding step

    // Embed as data URI — adds 33% overhead to payload size
    string mimeType = "image/png";
    string dataUri = $"data:{mimeType};base64,{base64Image}";

    var formContent = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("apikey", _apiKey),
        new KeyValuePair<string, string>("base64Image", dataUri),
        new KeyValuePair<string, string>("language", "eng"),
        new KeyValuePair<string, string>("isOverlayRequired", "false")
    });

    var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", formContent);
    string json = await response.Content.ReadAsStringAsync();

    using var doc = JsonDocument.Parse(json);
    return doc.RootElement
        .GetProperty("ParsedResults")[0]
        .GetProperty("ParsedText")
        .GetString() ?? string.Empty;
}
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks

Public Class ImageProcessor
    Private _apiKey As String
    Private _httpClient As HttpClient

    Public Sub New(apiKey As String, httpClient As HttpClient)
        _apiKey = apiKey
        _httpClient = httpClient
    End Sub

    ' base64Image parameter: read → encode → embed in form → POST → parse
    Public Async Function ExtractViaBase64(imagePath As String) As Task(Of String)
        Dim imageBytes As Byte() = Await File.ReadAllBytesAsync(imagePath)
        Dim base64Image As String = Convert.ToBase64String(imageBytes) ' Mandatory encoding step

        ' Embed as data URI — adds 33% overhead to payload size
        Dim mimeType As String = "image/png"
        Dim dataUri As String = $"data:{mimeType};base64,{base64Image}"

        Dim formContent As New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
            New KeyValuePair(Of String, String)("apikey", _apiKey),
            New KeyValuePair(Of String, String)("base64Image", dataUri),
            New KeyValuePair(Of String, String)("language", "eng"),
            New KeyValuePair(Of String, String)("isOverlayRequired", "false")
        })

        Dim response As HttpResponseMessage = Await _httpClient.PostAsync("https://api.ocr.space/parse/image", formContent)
        Dim json As String = Await response.Content.ReadAsStringAsync()

        Using doc As JsonDocument = JsonDocument.Parse(json)
            Return doc.RootElement _
                .GetProperty("ParsedResults")(0) _
                .GetProperty("ParsedText") _
                .GetString() OrElse String.Empty
        End Using
    End Function
End Class
$vbLabelText   $csharpLabel

Podejście IronOCR:

// LoadImage(bytes): raw bytes accepted directly — no encoding
public string ExtractFromBytes(byte[] imageBytes)
{
    using var input = new OcrInput();
    input.LoadImage(imageBytes); // Nie Base64, no data URI, no overhead

    var result = new IronTesseract().Read(input);
    return result.Text;
}
// LoadImage(bytes): raw bytes accepted directly — no encoding
public string ExtractFromBytes(byte[] imageBytes)
{
    using var input = new OcrInput();
    input.LoadImage(imageBytes); // Nie Base64, no data URI, no overhead

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

Public Function ExtractFromBytes(imageBytes As Byte()) As String
    Using input As New OcrInput()
        input.LoadImage(imageBytes) ' Nie Base64, no data URI, no overhead

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

Etap kodowania Base64 nie występuje w IronOCR, ponieważ nie ma warstwy transportowej HTTP. Surowe bajty trafiaja bezposrednio do OcrInput.LoadImage(). Znika również obciążenie związane z URI danych — kodowanie Base64 zwiększa rozmiar ładunku o około 33%. Przewodnik po wejsciu strumieniowym pokazuje ten sam wzorzec dla wejsci Stream, co jest przydatne, gdy bajty pochodza z obslugi przesylan lub bufora pamieci, zamiast z pliku.

Zastąpienie wyboru silnika OCR przetwarzaniem wstępnym obrazu

OCR.space udostepnia dwa silniki OCR poprzez parametr formularza OCREngine: Silnik 1 jest szybszy, ale z nizsza dokladnoscia przy zlozonych schematach; Silnik 2 działa wolniej, ale zapewnia wyższą dokładność w przypadku większości typów dokumentów. Programiści wybierają silnik dla każdego wywołania w oparciu o cechy dokumentu.IronOCR korzysta z jednego zoptymalizowanego silnika Tesseract 5, ale udostępnia wyraźne filtry przetwarzania wstępnego, które zajmują się podstawową przyczyną — jakością dokumentu — zamiast przełączać się między trybami silnika.

Podejście OCR.space:

// OCREngine parameter: binary choice, no control over why accuracy differs
public async Task<string> ExtractWithEngineSelection(
    string imagePath,
    bool useHighAccuracyEngine = true)
{
    byte[] imageBytes = await File.ReadAllBytesAsync(imagePath);
    string base64 = Convert.ToBase64String(imageBytes);

    var formContent = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("apikey", _apiKey),
        new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}"),
        new KeyValuePair<string, string>("language", "eng"),
        // Engine 1 = faster, Engine 2 = higher accuracy — binary choice only
        new KeyValuePair<string, string>("OCREngine", useHighAccuracyEngine ? "2" : "1"),
        new KeyValuePair<string, string>("scale", "true"),
        new KeyValuePair<string, string>("detectOrientation", "true")
    });

    var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", formContent);
    string json = await response.Content.ReadAsStringAsync();

    using var doc = JsonDocument.Parse(json);
    return doc.RootElement
        .GetProperty("ParsedResults")[0]
        .GetProperty("ParsedText")
        .GetString() ?? string.Empty;
}
// OCREngine parameter: binary choice, no control over why accuracy differs
public async Task<string> ExtractWithEngineSelection(
    string imagePath,
    bool useHighAccuracyEngine = true)
{
    byte[] imageBytes = await File.ReadAllBytesAsync(imagePath);
    string base64 = Convert.ToBase64String(imageBytes);

    var formContent = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("apikey", _apiKey),
        new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}"),
        new KeyValuePair<string, string>("language", "eng"),
        // Engine 1 = faster, Engine 2 = higher accuracy — binary choice only
        new KeyValuePair<string, string>("OCREngine", useHighAccuracyEngine ? "2" : "1"),
        new KeyValuePair<string, string>("scale", "true"),
        new KeyValuePair<string, string>("detectOrientation", "true")
    });

    var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", formContent);
    string json = await response.Content.ReadAsStringAsync();

    using var doc = JsonDocument.Parse(json);
    return doc.RootElement
        .GetProperty("ParsedResults")[0]
        .GetProperty("ParsedText")
        .GetString() ?? string.Empty;
}
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks

Public Class OCRService
    Private _apiKey As String
    Private _httpClient As HttpClient

    Public Sub New(apiKey As String, httpClient As HttpClient)
        _apiKey = apiKey
        _httpClient = httpClient
    End Sub

    ' OCREngine parameter: binary choice, no control over why accuracy differs
    Public Async Function ExtractWithEngineSelection(imagePath As String, Optional useHighAccuracyEngine As Boolean = True) As Task(Of String)
        Dim imageBytes As Byte() = Await File.ReadAllBytesAsync(imagePath)
        Dim base64 As String = Convert.ToBase64String(imageBytes)

        Dim formContent As New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
            New KeyValuePair(Of String, String)("apikey", _apiKey),
            New KeyValuePair(Of String, String)("base64Image", $"data:image/png;base64,{base64}"),
            New KeyValuePair(Of String, String)("language", "eng"),
            ' Engine 1 = faster, Engine 2 = higher accuracy — binary choice only
            New KeyValuePair(Of String, String)("OCREngine", If(useHighAccuracyEngine, "2", "1")),
            New KeyValuePair(Of String, String)("scale", "true"),
            New KeyValuePair(Of String, String)("detectOrientation", "true")
        })

        Dim response As HttpResponseMessage = Await _httpClient.PostAsync("https://api.ocr.space/parse/image", formContent)
        Dim json As String = Await response.Content.ReadAsStringAsync()

        Using doc As JsonDocument = JsonDocument.Parse(json)
            Return doc.RootElement _
                .GetProperty("ParsedResults")(0) _
                .GetProperty("ParsedText") _
                .GetString() OrElse String.Empty
        End Using
    End Function
End Class
$vbLabelText   $csharpLabel

Podejście IronOCR:

// Preprocessing pipeline: fix the document, not the engine selection
public string ExtractWithPreprocessing(string imagePath)
{
    using var input = new OcrInput();
    input.LoadImage(imagePath);

    // Apply filters that match the document's specific quality issues
    input.Deskew();         // Correct rotation — replaces detectOrientation
    input.DeNoise();        // Remove noise from fax/photocopier artifacts
    input.Contrast();       // Enhance contrast on low-quality scans
    input.Scale(200);       // Upscale small or low-DPI images

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

    Console.WriteLine($"Confidence: {result.Confidence}%"); // Nie equivalent in OCR.space
    return result.Text;
}
// Preprocessing pipeline: fix the document, not the engine selection
public string ExtractWithPreprocessing(string imagePath)
{
    using var input = new OcrInput();
    input.LoadImage(imagePath);

    // Apply filters that match the document's specific quality issues
    input.Deskew();         // Correct rotation — replaces detectOrientation
    input.DeNoise();        // Remove noise from fax/photocopier artifacts
    input.Contrast();       // Enhance contrast on low-quality scans
    input.Scale(200);       // Upscale small or low-DPI images

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

    Console.WriteLine($"Confidence: {result.Confidence}%"); // Nie equivalent in OCR.space
    return result.Text;
}
Imports IronOcr

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

        ' Apply filters that match the document's specific quality issues
        input.Deskew()         ' Correct rotation — replaces detectOrientation
        input.DeNoise()        ' Remove noise from fax/photocopier artifacts
        input.Contrast()       ' Enhance contrast on low-quality scans
        input.Scale(200)       ' Upscale small or low-DPI images

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

        Console.WriteLine($"Confidence: {result.Confidence}%") ' Nie equivalent in OCR.space
        Return result.Text
    End Using
End Function
$vbLabelText   $csharpLabel

Parametr OCREngine OCR.space jest proxy dla jakosci dokumentu — gdy Silnik 1 zawiedzie na dokumencie, deweloperzy przechodza na Silnik 2, majac nadzieje, ze inny algorytm zrekompensuje. Przetwarzanie wstepne IronOCR rozwiazuje problem jakosci bezposrednio: Deskew() koryguje przechylone skany, DeNoise() radzi sobie z artefaktami faksowymi, a Contrast() odzyskuje tekst z kserokopii o niskim kontraście. Wlasciwosc Confidence wynikow kwantyfikuje jakosc ekstrakcji, co OCREngine przelaczanie nie moze zapewnic. Przewodnik po korekcji jakosci obrazu i asystent filtrow dokumentuja wplyw kazdego filtru na rozne typy dokumentow.

Wielojęzyczne OCR bez konieczności zmiany języka przy każdym wywołaniu

OCR.space akceptuje jeden parametr language na kazde wywolanie API. Dokumenty zawierające różne języki wymagają oddzielnych zleceń dla każdego języka, a wyniki należy połączyć ręcznie.IronOCR przetwarza jednoczesnie wiele jezykow w jednej operacji odczytu uzywajac operatora + na wartosciach OcrLanguage.

Podejście OCR.space:

// OCR.space: one language per call — multi-language requires multiple requests
public async Task<string> ExtractMultiLanguage(string imagePath)
{
    // First pass: English
    string englishText = await ExtractWithLanguage(imagePath, "eng");

    // Second pass: French (consumes another rate-limit slot, another API call)
    string frenchText = await ExtractWithLanguage(imagePath, "fre");

    // Manually merge results — no way to know which text belongs to which language
    return $"{englishText}\n{frenchText}";
}

private async Task<string> ExtractWithLanguage(string imagePath, string langCode)
{
    byte[] imageBytes = await File.ReadAllBytesAsync(imagePath);
    string base64 = Convert.ToBase64String(imageBytes);

    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("apikey", _apiKey),
        new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}"),
        new KeyValuePair<string, string>("language", langCode) // One language per call
    });

    var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
    string json = await response.Content.ReadAsStringAsync();

    using var doc = JsonDocument.Parse(json);
    return doc.RootElement
        .GetProperty("ParsedResults")[0]
        .GetProperty("ParsedText")
        .GetString() ?? string.Empty;
}
// OCR.space: one language per call — multi-language requires multiple requests
public async Task<string> ExtractMultiLanguage(string imagePath)
{
    // First pass: English
    string englishText = await ExtractWithLanguage(imagePath, "eng");

    // Second pass: French (consumes another rate-limit slot, another API call)
    string frenchText = await ExtractWithLanguage(imagePath, "fre");

    // Manually merge results — no way to know which text belongs to which language
    return $"{englishText}\n{frenchText}";
}

private async Task<string> ExtractWithLanguage(string imagePath, string langCode)
{
    byte[] imageBytes = await File.ReadAllBytesAsync(imagePath);
    string base64 = Convert.ToBase64String(imageBytes);

    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("apikey", _apiKey),
        new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}"),
        new KeyValuePair<string, string>("language", langCode) // One language per call
    });

    var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
    string json = await response.Content.ReadAsStringAsync();

    using var doc = JsonDocument.Parse(json);
    return doc.RootElement
        .GetProperty("ParsedResults")[0]
        .GetProperty("ParsedText")
        .GetString() ?? string.Empty;
}
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks

Public Class OCRSpace
    Private _apiKey As String
    Private _httpClient As HttpClient

    Public Async Function ExtractMultiLanguage(imagePath As String) As Task(Of String)
        ' First pass: English
        Dim englishText As String = Await ExtractWithLanguage(imagePath, "eng")

        ' Second pass: French (consumes another rate-limit slot, another API call)
        Dim frenchText As String = Await ExtractWithLanguage(imagePath, "fre")

        ' Manually merge results — no way to know which text belongs to which language
        Return $"{englishText}{vbLf}{frenchText}"
    End Function

    Private Async Function ExtractWithLanguage(imagePath As String, langCode As String) As Task(Of String)
        Dim imageBytes As Byte() = Await File.ReadAllBytesAsync(imagePath)
        Dim base64 As String = Convert.ToBase64String(imageBytes)

        Dim content = New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
            New KeyValuePair(Of String, String)("apikey", _apiKey),
            New KeyValuePair(Of String, String)("base64Image", $"data:image/png;base64,{base64}"),
            New KeyValuePair(Of String, String)("language", langCode) ' One language per call
        })

        Dim response = Await _httpClient.PostAsync("https://api.ocr.space/parse/image", content)
        Dim json As String = Await response.Content.ReadAsStringAsync()

        Using doc = JsonDocument.Parse(json)
            Return doc.RootElement _
                .GetProperty("ParsedResults")(0) _
                .GetProperty("ParsedText") _
                .GetString() OrElse String.Empty
        End Using
    End Function
End Class
$vbLabelText   $csharpLabel

Podejście IronOCR:

// IronOCR: multiple languages in a single read — one pass, correct output
public string ExtractMultiLanguage(string imagePath)
{
    var ocr = new IronTesseract();

    // Combine languages with + operator — processed simultaneously
    ocr.Language = OcrLanguage.English + OcrLanguage.French + OcrLanguage.German;

    var result = ocr.Read(imagePath);
    return result.Text; // Correctly interleaved multilingual output
}
// IronOCR: multiple languages in a single read — one pass, correct output
public string ExtractMultiLanguage(string imagePath)
{
    var ocr = new IronTesseract();

    // Combine languages with + operator — processed simultaneously
    ocr.Language = OcrLanguage.English + OcrLanguage.French + OcrLanguage.German;

    var result = ocr.Read(imagePath);
    return result.Text; // Correctly interleaved multilingual output
}
Imports IronOcr

Public Function ExtractMultiLanguage(imagePath As String) As String
    Dim ocr As New IronTesseract()

    ' Combine languages with + operator — processed simultaneously
    ocr.Language = OcrLanguage.English + OcrLanguage.French + OcrLanguage.German

    Dim result = ocr.Read(imagePath)
    Return result.Text ' Correctly interleaved multilingual output
End Function
$vbLabelText   $csharpLabel

Ograniczenie OCR.space dotyczące jednego języka na wywołanie zmusza programistów do wykonania N wywołań API dla dokumentu w N językach i zgadywania, jak pogodzić wyniki.IronOCRłączy modele językowe w jednym przebiegu silnika, co pozwala uzyskać poprawnie przeplatane dane wyjściowe bez konieczności przetwarzania końcowego. Paczkie jezykowe instaluja sie jako pakiety NuGet — IronOcr.Languages.French, IronOcr.Languages.German i tak dalej — i dzialaja offline. Jak-to "wiele jezykow" obejmuje instalacje paczek i skladnie operatora + dla wszystkich 125+ obsługiwanych jezykow.

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

OCR.space zwraca zwykly tekst z ParsedResults[0].ParsedText. Brak danych na poziomie WORD, ramek ograniczających, granic wierszy oraz wyników pewności dla poszczególnych elementów. Aplikacje, które muszą zlokalizować konkretne pola — datę w prawym górnym rogu faktury, sumę w prawej dolnej komórce tabeli — nie mają ustrukturyzowanej podstawy, na której mogłyby się oprzeć, korzystając z odpowiedzi OCR.space.IronOCR zapewnia pełną hierarchię dokumentu: strony, akapity, wiersze, słowa i znaki, z których każdy posiada współrzędne pikselowe i wskaźniki pewności.

Podejście OCR.space:

// OCR.space: plain text only — no structure, no coordinates
public async Task<string> ExtractInvoiceFields(string invoicePath)
{
    byte[] invoiceBytes = await File.ReadAllBytesAsync(invoicePath);
    string base64 = Convert.ToBase64String(invoiceBytes);

    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("apikey", _apiKey),
        new KeyValuePair<string, string>("base64Image", $"data:application/pdf;base64,{base64}"),
        new KeyValuePair<string, string>("filetype", "PDF"),
        new KeyValuePair<string, string>("language", "eng"),
        // isOverlayRequired=true returns word boxes, but only as raw JSON coordinates
        new KeyValuePair<string, string>("isOverlayRequired", "true")
    });

    var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
    string json = await response.Content.ReadAsStringAsync();

    // Navigate deeply-nested JSON to find word boxes — no typed models
    using var doc = JsonDocument.Parse(json);
    var overlay = doc.RootElement
        .GetProperty("ParsedResults")[0]
        .GetProperty("TextOverlay");

    // Parse word coordinate arrays manually — fragile JSON path traversal
    var wordData = new List<(string word, int x, int y)>();
    foreach (var line in overlay.GetProperty("Lines").EnumerateArray())
    {
        foreach (var word in line.GetProperty("Words").EnumerateArray())
        {
            string wordText = word.GetProperty("WordText").GetString() ?? "";
            int left = word.GetProperty("Left").GetInt32();
            int top = word.GetProperty("Top").GetInt32();
            wordData.Add((wordText, left, top));
        }
    }

    // Reconstruct full text from raw JSON — still no typed result
    return string.Join(" ", wordData.Select(w => w.word));
}
// OCR.space: plain text only — no structure, no coordinates
public async Task<string> ExtractInvoiceFields(string invoicePath)
{
    byte[] invoiceBytes = await File.ReadAllBytesAsync(invoicePath);
    string base64 = Convert.ToBase64String(invoiceBytes);

    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("apikey", _apiKey),
        new KeyValuePair<string, string>("base64Image", $"data:application/pdf;base64,{base64}"),
        new KeyValuePair<string, string>("filetype", "PDF"),
        new KeyValuePair<string, string>("language", "eng"),
        // isOverlayRequired=true returns word boxes, but only as raw JSON coordinates
        new KeyValuePair<string, string>("isOverlayRequired", "true")
    });

    var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
    string json = await response.Content.ReadAsStringAsync();

    // Navigate deeply-nested JSON to find word boxes — no typed models
    using var doc = JsonDocument.Parse(json);
    var overlay = doc.RootElement
        .GetProperty("ParsedResults")[0]
        .GetProperty("TextOverlay");

    // Parse word coordinate arrays manually — fragile JSON path traversal
    var wordData = new List<(string word, int x, int y)>();
    foreach (var line in overlay.GetProperty("Lines").EnumerateArray())
    {
        foreach (var word in line.GetProperty("Words").EnumerateArray())
        {
            string wordText = word.GetProperty("WordText").GetString() ?? "";
            int left = word.GetProperty("Left").GetInt32();
            int top = word.GetProperty("Top").GetInt32();
            wordData.Add((wordText, left, top));
        }
    }

    // Reconstruct full text from raw JSON — still no typed result
    return string.Join(" ", wordData.Select(w => w.word));
}
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks

Public Class InvoiceProcessor
    Private _apiKey As String
    Private _httpClient As HttpClient

    Public Sub New(apiKey As String, httpClient As HttpClient)
        _apiKey = apiKey
        _httpClient = httpClient
    End Sub

    Public Async Function ExtractInvoiceFields(invoicePath As String) As Task(Of String)
        Dim invoiceBytes As Byte() = Await File.ReadAllBytesAsync(invoicePath)
        Dim base64 As String = Convert.ToBase64String(invoiceBytes)

        Dim content As New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
            New KeyValuePair(Of String, String)("apikey", _apiKey),
            New KeyValuePair(Of String, String)("base64Image", $"data:application/pdf;base64,{base64}"),
            New KeyValuePair(Of String, String)("filetype", "PDF"),
            New KeyValuePair(Of String, String)("language", "eng"),
            New KeyValuePair(Of String, String)("isOverlayRequired", "true")
        })

        Dim response As HttpResponseMessage = Await _httpClient.PostAsync("https://api.ocr.space/parse/image", content)
        Dim json As String = Await response.Content.ReadAsStringAsync()

        Using doc As JsonDocument = JsonDocument.Parse(json)
            Dim overlay = doc.RootElement _
                .GetProperty("ParsedResults")(0) _
                .GetProperty("TextOverlay")

            Dim wordData As New List(Of (word As String, x As Integer, y As Integer))()
            For Each line In overlay.GetProperty("Lines").EnumerateArray()
                For Each word In line.GetProperty("Words").EnumerateArray()
                    Dim wordText As String = word.GetProperty("WordText").GetString() OrElse ""
                    Dim left As Integer = word.GetProperty("Left").GetInt32()
                    Dim top As Integer = word.GetProperty("Top").GetInt32()
                    wordData.Add((wordText, left, top))
                Next
            Next

            Return String.Join(" ", wordData.Select(Function(w) w.word))
        End Using
    End Function
End Class
$vbLabelText   $csharpLabel

Podejście IronOCR:

// IronOCR: full document hierarchy — typed, no JSON, no coordinates parsing
public void ExtractInvoiceFields(string invoicePath)
{
    var ocr = new IronTesseract();
    var result = ocr.Read(invoicePath);

    // Access the full document hierarchy — all strongly typed
    foreach (var page in result.Pages)
    {
        foreach (var paragraph in page.Paragraphs)
        {
            Console.WriteLine($"Paragraph at ({paragraph.X}, {paragraph.Y}): {paragraph.Text}");
        }

        foreach (var word in page.Words)
        {
            // Word-level confidence — identify low-quality extractions
            if (word.Confidence < 70)
                Console.WriteLine($"Low confidence word '{word.Text}' at ({word.X}, {word.Y})");
        }
    }

    // Or use region-based OCR to target specific invoice zones directly
    var totalRegion = new CropRectangle(400, 700, 200, 50); // Bottom-right total field
    using var input = new OcrInput();
    input.LoadImage(invoicePath, totalRegion);
    string totalText = ocr.Read(input).Text;
    Console.WriteLine($"Invoice total: {totalText}");
}
// IronOCR: full document hierarchy — typed, no JSON, no coordinates parsing
public void ExtractInvoiceFields(string invoicePath)
{
    var ocr = new IronTesseract();
    var result = ocr.Read(invoicePath);

    // Access the full document hierarchy — all strongly typed
    foreach (var page in result.Pages)
    {
        foreach (var paragraph in page.Paragraphs)
        {
            Console.WriteLine($"Paragraph at ({paragraph.X}, {paragraph.Y}): {paragraph.Text}");
        }

        foreach (var word in page.Words)
        {
            // Word-level confidence — identify low-quality extractions
            if (word.Confidence < 70)
                Console.WriteLine($"Low confidence word '{word.Text}' at ({word.X}, {word.Y})");
        }
    }

    // Or use region-based OCR to target specific invoice zones directly
    var totalRegion = new CropRectangle(400, 700, 200, 50); // Bottom-right total field
    using var input = new OcrInput();
    input.LoadImage(invoicePath, totalRegion);
    string totalText = ocr.Read(input).Text;
    Console.WriteLine($"Invoice total: {totalText}");
}
Imports IronOcr

Public Sub ExtractInvoiceFields(invoicePath As String)
    Dim ocr As New IronTesseract()
    Dim result = ocr.Read(invoicePath)

    ' Access the full document hierarchy — all strongly typed
    For Each page In result.Pages
        For Each paragraph In page.Paragraphs
            Console.WriteLine($"Paragraph at ({paragraph.X}, {paragraph.Y}): {paragraph.Text}")
        Next

        For Each word In page.Words
            ' Word-level confidence — identify low-quality extractions
            If word.Confidence < 70 Then
                Console.WriteLine($"Low confidence word '{word.Text}' at ({word.X}, {word.Y})")
            End If
        Next
    Next

    ' Or use region-based OCR to target specific invoice zones directly
    Dim totalRegion As New CropRectangle(400, 700, 200, 50) ' Bottom-right total field
    Using input As New OcrInput()
        input.LoadImage(invoicePath, totalRegion)
        Dim totalText As String = ocr.Read(input).Text
        Console.WriteLine($"Invoice total: {totalText}")
    End Using
End Sub
$vbLabelText   $csharpLabel

Flaga isOverlayRequired=true OCR.space zwraca wspolrzedne slow w formacie JSON, lecz struktura odpowiedzi wymaga nawigacji po zagniezdzonych tablicach JSON z dostepem do wlasciwosci o kluczach lancuchowych — brak modelu typowanego, brak IntelliSense i krucha nawigacja sciezki, ktora lamie sie, jesli struktura odpowiedzi sie zmienia.IronOCRresult.Pages, result.Words i result.Lines sa typowanymi obiektami .NET. Podejscie CropRectangle skupia sie na bezposrednim docieraniu do specyficznych regionow dokumentu, zamiast wyodrebniac caly dokument i pożniej filtrowac przez wspolrzedne. Przewodnik dotyczący wyników odczytu oraz przewodnik po OCR opartym na regionach szczegółowo omawiają oba wzorce.

Dokumentacja API OCR.space do IronOCR

Koncepcja OCR.space Odpowiednik IronOCR
Brak pakietu NuGet dotnet add package IronOcr
Konstrukcja HttpClient Nie jest potrzebne — brak warstwy HTTP
Ogranicznik szybkosci SemaphoreSlim Nie jest wymagane — brak limitów stawek
FormUrlEncodedContent / MultipartFormDataContent OcrInput
Parametr URI danych base64Image input.LoadImage(bytes)
Parametr przesyłania file input.LoadImage(path)
Naglowek / pole formularza apikey IronOcr.License.LicenseKey (raz przy uruchomieniu)
Parametr language (jeden na wywolanie) ocr.Language = OcrLanguage.English + OcrLanguage.French
OCREngine=1 (szybko) Domyślny silnik (zoptymalizowany Tesseract 5)
OCREngine=2 (wysoka dokladnosc) input.Deskew(); input.DeNoise(); input.Contrast();
Parametr scale=true input.Scale(200)
Parametr detectOrientation=true input.Deskew()
Parametr isOverlayRequired=true result.Pages[n].Words (zawsze dostepne, typowane)
Parametr isCreateSearchablePdf=true result.SaveAsSearchablePdf("output.pdf")
Parametr filetype=PDF input.LoadPdf(path)
ParsedResults[0].ParsedText result.Text
ParsedResults[n] (tekst na strone) result.Pages[n].Text
TextOverlay.Lines[n].Words[n].WordText result.Pages[n].Words[n].Text
TextOverlay.Lines[n].Words[n].Left/Top result.Pages[n].Words[n].X / .Y
Flaga JSON IsErroredOnProcessing Standard Exception z wiadomoscia
Flaga na strone FileParseExitCode Standard Exception z wiadomoscia
HTTP 429 Zbyt wiele żądań Nie dotyczy — brak limitów stawek
Niestandardowy OcrResult POCO (zdefiniowane przez uzytkownika) IronOcr.OcrResult (dostarczane przez NuGet)
Niestandardowy OcrSpaceException (zdefiniowane przez uzytkownika) Standardowe typy wyjątków .NET Standard

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

Problem 1: Kod asynchroniczny, który istniał wyłącznie dla HTTP

OCR.space: Kazde wywolanie OCR jest async, poniewaz obejmuje okrazenie HTTP do chmury. Metody usług, akcje kontrolerów i zadania w tle zostały zrealizowane asynchronicznie, aby uniknąć blokowania wątku podczas oczekiwania na sieć.

Rozwiazanie: Metoda Read()IronOCR jest synchroniczna. Usun await z metod, ktore byly asynchroniczne tylko dlatego, ze wymagalo tego OCR.space. W kontekstach ASP.NET Core, gdzie blokowanie nie ma znaczenia, owin wywolanie synchroniczne w Task.Run() lub uzyj wzorcow asynchronicznych udokumentowanych w przewodniku po OCR asynchronicznym. Nie dodawaj odruchowo await do wywolan IronOCR— nie jest to wymagane i dodaje niepotrzebne obciazenie w kontekstach innych niz webowe.

// Before: async because OCR.space required network I/O
public async Task<string> ProcessDocumentAsync(string path)
{
    return await _ocrSpaceClient.ExtractTextAsync(path); // Network wait
}

// After: synchronous — no network, no async needed
public string ProcessDocument(string path)
{
    return _ocr.Read(path).Text; // Local execution
}
// Before: async because OCR.space required network I/O
public async Task<string> ProcessDocumentAsync(string path)
{
    return await _ocrSpaceClient.ExtractTextAsync(path); // Network wait
}

// After: synchronous — no network, no async needed
public string ProcessDocument(string path)
{
    return _ocr.Read(path).Text; // Local execution
}
Imports System.Threading.Tasks

' Before: async because OCR.space required network I/O
Public Async Function ProcessDocumentAsync(path As String) As Task(Of String)
    Return Await _ocrSpaceClient.ExtractTextAsync(path) ' Network wait
End Function

' After: synchronous — no network, no async needed
Public Function ProcessDocument(path As String) As String
    Return _ocr.Read(path).Text ' Local execution
End Function
$vbLabelText   $csharpLabel

Problem 2: Infrastruktura przechowywania i rotacji kluczy API

OCR.space: Klucz API musi byc wstrzykiwany do kazdego zadania. Zespoly zwykle przechowuja go w appsettings.json lub zmiennych srodowiskowych, wstrzykuja przez IOptions<t> lub przez wstrzykiwanie konstruktora i rotacja, gdy zostanie ujawniony. Rotacja kluczy wymaga aktualizacji każdego środowiska wdrożeniowego i ponownego uruchomienia aplikacji.

Rozwiązanie: Klucz licencyjny IronOCR jest ustawiany jednorazowo podczas uruchamiania i nie jest już nigdy więcej wykorzystywany podczas działania programu. Usuń wzorzec wstrzykiwania klucza na żądanie. Usun klase konfiguracji IOptions<OcrSpaceSettings>. Kluczowy wzorzec inicjalizacji to jedna linia:

// Startup.cs or Program.cs — once only
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY");
// Startup.cs or Program.cs — once only
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY");
Imports System

' Startup.vb or Program.vb — once only
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
$vbLabelText   $csharpLabel

Nie ma wstrzykiwania poświadczeń na żądanie, nie ma procedury rotacji kluczy i nie ma ryzyka przypadkowego zarejestrowania klucza w śladach żądań.

Problem 3: Logika wstępnej walidacji rozmiaru pliku

OCR.space: Wersja darmowa odrzuca pliki większe niż 5 MB, wyświetlając komunikat o błędzie. Kod produkcyjny dodaje sprawdzanie rozmiaru pliku przed każdym żądaniem, aby uniknąć marnowania limitu wywołań na żądanie, które zakończy się niepowodzeniem:

// OCR.space: pre-validation to avoid wasting quota on large files
var fileInfo = new FileInfo(filePath);
if (fileInfo.Length > 5 * 1024 * 1024)
    throw new InvalidOperationException("File exceeds 5MB free tier limit.");
// OCR.space: pre-validation to avoid wasting quota on large files
var fileInfo = new FileInfo(filePath);
if (fileInfo.Length > 5 * 1024 * 1024)
    throw new InvalidOperationException("File exceeds 5MB free tier limit.");
Dim fileInfo As New FileInfo(filePath)
If fileInfo.Length > 5 * 1024 * 1024 Then
    Throw New InvalidOperationException("File exceeds 5MB free tier limit.")
End If
$vbLabelText   $csharpLabel

Rozwiązanie: Całkowicie usunąć tę kontrolę.IronOCROcrInput.LoadPdf() i OcrInput.LoadImage() nie maja limitu rozmiaru poza dostepna pamiecia systemowa. Sztuczny limit 5 MB istnieje wyłącznie dlatego, że bezpłatny plan OCR.space nakłada go ze względu na pojemność serwerów. Skanowany plik PDF o rozmiarze 50 MB ładuje się tak samo jak plik o rozmiarze 500 KB.

Problem 4: Niestabilność nawigacji w odpowiedzi JSON

OCR.space: Parsowanie odpowiedzi opiera sie na nawigacji JsonDocument z dostepem do wlasciwosci o kluczach lancuchowych. Kod taki jak doc.RootElement.GetProperty("ParsedResults")[0].GetProperty("ParsedText") zglasza KeyNotFoundException, jezeli ksztalt odpowiedzi ulegnie zmianie i IndexOutOfRangeException, jezeli ParsedResults jest puste. Oba wymagają stosowania instrukcji try-catch lub sprawdzania wartości null w całym tekście.

Rozwiazanie:IronOCR zwraca typowany obiekt OcrResult. Wlasciwosc .Text jest zawsze string — nigdy nie null, nigdy nie brak. Jezeli OCR nie generuje wyjscia (pusta strona, nieczytelny obraz), result.Text jest pustym lancuchem. Nie ma JSON-u do nawigacji i zadnej kruchosci sciezki wlasciwosci do ochrony. Dla filtrowania opartego na pewnosci result.Confidence zwraca double, ktory mozesz porownac bezposrednio:

// IronOCR: typed result — no JSON path fragility
var result = new IronTesseract().Read("document.jpg");

if (result.Confidence < 50)
    Console.WriteLine("Low confidence — consider preprocessing");
else
    Console.WriteLine(result.Text);
// IronOCR: typed result — no JSON path fragility
var result = new IronTesseract().Read("document.jpg");

if (result.Confidence < 50)
    Console.WriteLine("Low confidence — consider preprocessing");
else
    Console.WriteLine(result.Text);
Imports IronOcr

Dim result = New IronTesseract().Read("document.jpg")

If result.Confidence < 50 Then
    Console.WriteLine("Low confidence — consider preprocessing")
Else
    Console.WriteLine(result.Text)
End If
$vbLabelText   $csharpLabel

Wskazówki dotyczące oceny pewności obejmują progi pewności dla poszczególnych słów i dokumentów.

Problem 5: Wyczerpanie limitu szybkości dla współdzielonego adresu IP w CI/CD

OCR.space: Potoki CI/CD, które przeprowadzają testy integracyjne na OCR.space, korzystają z tego samego adresu IP wychodzącego, co sieć biura programistów. Konta w wersji bezpłatnej mają limit 500 żądań dziennie na adres IP. Potok przetwarzający 200 dokumentów testowych na jeden przebieg może wyczerpać dzienny limit, zanim pierwszy programista przeprowadzi test ręczny. Zespoły radzą sobie z tym problemem, symulując odpowiedzi OCR.space w testach, co jednak mija się z celem testów integracyjnych.

Rozwiązanie:IronOCR przetwarza dane lokalnie. Zestaw testowy wywoluje new IronTesseract().Read(testImagePath).Text bezposrednio — bez potrzeby makingu, bez wyczerpywania limitu, bez zaleznosci sieciowych. Testy integracyjne są uruchamiane w CI/CD z wykorzystaniem tych samych rzeczywistych wyników OCR, co w środowisku produkcyjnym, bez zarządzania limitami szybkości ani wzorców izolacji testów.

Problem 6: IDisposable Wzorzec w zarzadzaniu HttpClient

OCR.space: Klasa owijajaca HttpClient implementuje IDisposable, aby zwolnic pule polaczen HTTP. Kazdy uzytkownik uslugi OCR musi albo wstrzyknac singleton, uzyc using, albo zarejestrowac z cyklem zycia usuwania w pojemniku DI. Zapomnienie o zwolnieniu zasobów powoduje wyczerpanie gniazd przy dużym obciążeniu.

Rozwiazanie: IronTesseract nie zarzadza polaczeniami sieciowymi. Nie implementuje IDisposable. Stworz jeden instancje na watek (lub na zadanie w ASP.NET), wywolaj .Read() i pozwol GC ja zebrac. Klasa OcrInput implementuje IDisposable i powinna byc owijana w using, gdy stosuje się przetwarzanie wstępne, ale glowna klasa IronTesseract nie wymaga zarzadzania cyklem zycia. Usun implementacje IDisposable z uslugi opakowania OCR i uprosc rejestracje DI z zakresu/transient z usuwaniem do prostej fabryki lub singleton.

Lista kontrolna migracji OCR.space

Zadania przed migracją

Przeprowadź audyt kodu źródłowego w celu zidentyfikowania wszystkich punktów integracji OCR.space:

# Find all OCR.space HTTP calls
grep -rn "ocr.space" --include="*.cs" .

# Find all base64 encoding related to OCR
grep -rn "base64Image\|Convert.ToBase64String" --include="*.cs" .

# Find rate limiter and retry logic
grep -rn "SemaphoreSlim\|TooManyRequests\|exponential" --include="*.cs" .

# Find JSON parsing for OCR responses
grep -rn "ParsedResults\|IsErroredOnProcessing\|FileParseExitCode" --include="*.cs" .

# Find custom OCR models and exception types
grep -rn "OcrSpaceException\|OcrResult\b" --include="*.cs" .

# Find API key configuration
grep -rn "apikey\|OcrSpaceApiKey\|ocr_space" --include="*.cs" --include="*.json" .
# Find all OCR.space HTTP calls
grep -rn "ocr.space" --include="*.cs" .

# Find all base64 encoding related to OCR
grep -rn "base64Image\|Convert.ToBase64String" --include="*.cs" .

# Find rate limiter and retry logic
grep -rn "SemaphoreSlim\|TooManyRequests\|exponential" --include="*.cs" .

# Find JSON parsing for OCR responses
grep -rn "ParsedResults\|IsErroredOnProcessing\|FileParseExitCode" --include="*.cs" .

# Find custom OCR models and exception types
grep -rn "OcrSpaceException\|OcrResult\b" --include="*.cs" .

# Find API key configuration
grep -rn "apikey\|OcrSpaceApiKey\|ocr_space" --include="*.cs" --include="*.json" .
SHELL

Sporządź listę plików zawierających kod OCR.space. Zauwaz, ktore metody sa async tylko z powodu uzaleznienia HTTP OCR.space — po migracji moga byc zrobione synchroniczne.

Zadania związane z aktualizacją kodu

  1. Zainstaluj pakiet NuGet IronOcr: dotnet add package IronOcr
  2. Dodaj IronOcr.License.LicenseKey = "..." do uruchamiania aplikacji
  3. Usun klase OcrSpaceApiClient i cala wspierajaca infrastrukture
  4. Usun niestandardowy OcrResult POCO (zastepowany przez IronOcr.OcrResult)
  5. Usun niestandardowa klase OcrSpaceException (zastepowana przez standardowe wyjatki .NET)
  6. Usun ogranicznik szybkosci SemaphoreSlim i powiazana logike Task.Delay
  7. Usun wszystkie wywolania Convert.ToBase64String() uzywane do kodowania obrazu OCR
  8. Zastap konstrukcje FormUrlEncodedContent / MultipartFormDataContent przez OcrInput
  9. Zastap wywolania _httpClient.PostAsync(...) przez new IronTesseract().Read(input)
  10. Zastap JsonDocument parsowanie ParsedResults[0].ParsedText przez result.Text
  11. Zastap TextOverlay parsowanie wspolrzednych JSON przez result.Pages[n].Words
  12. Zastap przelaczanie parametru OCREngine przez odpowiednie filtry wstepne
  13. Zastap lancuchy parametrow language przez wartosci wyliczenia OcrLanguage
  14. Usunąć wstępne sprawdzanie rozmiaru pliku (limit 5 MB nie obowiązuje już)
  15. Przeksztalcaj metody OCR async Task<string> na synchroniczne string tam, gdzie HTTP bylo jedynym powodem asynchronicznym
  16. Usuń klucz API OCR.space z plików konfiguracyjnych i ustawień zmiennych środowiskowych

Testy po migracji

  • Sprawdź, czy ekstrakcja tekstu zapewnia taką samą lub wyższą dokładność w przypadku tych samych dokumentów testowych
  • Sprawdź, czy przetwarzanie dużych plików (ponad 5 MB) przebiega bez błędów
  • Testuj dokumenty wielojezykowe z OcrLanguage.English + OcrLanguage.French i weryfikuj wyjscie przelotne
  • Uruchom potok CI/CD z rzeczywistymi wywołaniami OCR — upewnij się, że nie występują błędy związane z limitami szybkości przy żadnej objętości dokumentów
  • Sprawdź, czy plik PDF z możliwością wyszukiwania nie zawiera znaków wodnych
  • Sprawdź, czy akcje kontrolera, które wcześniej były asynchroniczne, nadal działają poprawnie po konwersji na synchroniczne
  • Sprawdź, czy środowiska wdrożeniowe odizolowane lub ograniczone sieciowo przetwarzają dokumenty bez błędów
  • Potwierdz, ze wartosci result.Confidence sa akceptowalne dla dokumentow, ktore wczesniej wymagaly OCREngine=2
  • Zweryfikuj, ze wspolrzedne result.Pages[n].Words pasuja do oczekiwanych pozycji pol w dokumentach strukturalnych
  • Sprawdź, czy inicjalizacja licencji przy uruchomieniu aplikacji przebiega pomyślnie przed pierwszym wywołaniem funkcji OCR

Kluczowe korzyści z migracji do IronOCR

Znikają koszty związane z infrastrukturą obejmującą ponad 80 linii kodu. Każda integracja OCR.space zawiera klienta HTTP, ogranicznik szybkości, deserializator JSON, niestandardowe typy wyjątków oraz niestandardowe modele wyników. Żaden z tych kodów nie wykonuje czynności faktycznie potrzebnych aplikacji — istnieje on w celu zrekompensowania braku SDK w OCR.space. Po migracji kod ten jest usuwany. Powierzchnia OCR w bazie kodu kurczy sie do new IronTesseract().Read(path).Text w miejscu wywolania i jednej linii inicjalizacji licencji przy starcie.

Szybkość przetwarzania dokumentów zależy od lokalnego sprzętu. OCR.space uwzględnia opóźnienia sieciowe, głębokość kolejki serwera OCR.space oraz czas przesyłu w obie strony w każdej operacji przetwarzania.IronOCR działa w trybie wbudowanym. Lokalna stacja robocza przetwarza dokumenty szybciej niż jakikolwiek interfejs API w chmurze przy dowolnej przepustowości, bez ograniczenia do 60 żądań na minutę, które powoduje sekwencyjność przetwarzania wsadowego. Przetwarzanie rownolegle z Parallel.ForEach w wielu instancjach IronTesseract skaluje sie z rdzeniami CPU — zobacz przyklad na rownoleglenia.

Dokumenty wrażliwe pozostają na stałe w Twojej infrastrukturze. Po migracji dokumentacja medyczna, dokumenty finansowe, umowy prawne i dokumenty tożsamości nigdy nie opuszczają serwera aplikacji. W ramach przeglądów zgodności z HIPAA, RODO, SOC 2 oraz wewnętrznymi zasadami klasyfikacji danych nie ma już potrzeby uwzględniania praktyk OCR.space w zakresie przetwarzania danych. Zakres audytu ogranicza się do Twojej własnej infrastruktury. Przewodnik wdrażania Docker oraz przewodnik wdrażania Azure obejmują wdrażanie IronOCR w środowiskach kontenerowych i chmurowych, które wymagają zgodności z przepisami dotyczącymi lokalizacji danych.

Strukturalne wyjscie umozliwia aplikacje inteligencji dokumentu. ParsedText lancuch OCR.space to koniec drogi dla analizy dokumentow.IronOCRresult.Pages, result.Words i result.Lines ze wspolrzednymi i ocenami pewnosci po slowie umozliwia aplikacje do lokalizowania specyficznych pol, weryfikowania jakosci ekstrakcji, wydobywania danych tabelarycznych i budowania dolnych rzek dokumentow z inteligencja. Funkcje, które wymagały tworzenia niestandardowej analizy układu na podstawie zwykłego tekstu generowanego przez OCR.space, stały się bezpośrednimi wywołaniami API. Przewodnik po ekstrakcji tabel oraz przewodnik po przetwarzaniu zeskanowanych dokumentów pokazują, jakie możliwości daje ta ustrukturyzowana podstawa.

Koszt jest stały i przewidywalny niezależnie od wielkości zlecenia. Bezpłatny pakiet OCR.space obejmuje 25 000 zapytań miesięcznie. Ponadto koszty subskrypcji są uzależnione od intensywności użytkowania. Wieczysta licencja $999 Lite IronOCR nie wiąze sie z oplatami za dokument przy dowolnym wolumenie. Zespół przetwarzający 100 000 dokumentów miesięcznie płaci taką samą opłatę licencyjną jak zespół przetwarzający 1 000 dokumentów miesięcznie. Prognozowanie budżetu dla aplikacji do przetwarzania dokumentów staje się stałym kosztem rocznym, a nie zmienną pozycją, która rośnie wraz z sukcesem biznesowym. Strona produktu IronOCR zawiera bezpłatną wersję próbną, która pozwala zespołom sprawdzić dokładność działania na konkretnych typach dokumentów przed zakupem.

Zwróć uwagęOCR.space i Tesseract są zarejestrowanymi znakami towarowymi ich właścicieli. Ta strona nie jest powiązana, zatwierdzana ani sponsorowana przez Google ani OCR.space. 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 API OCR.space 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 API OCR.space do IronOCR?

Zastąp sekwencje inicjalizacji OCR.space instancjonowaniem IronTesseract, usuń zarządzanie cyklem życia COM (jawne wzorce 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 API OCR.space 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 API OCR.space 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 API OCR.space do IronOCR wymaga zmian w infrastrukturze wdrożeniowej?

IronOCR wymaga mniej zmian w infrastrukturze niż API OCR.space. 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 OCR.space?

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ż API OCR.space 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 API OCR.space 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