Zum Fußzeileninhalt springen
MIT ANDEREN KOMPONENTEN VERGLEICHEN

ABBYY FineReader vs Tesseract: OCR-Vergleich

OCR.space verfügt über kein NuGet Paket. Diese einzelne Tatsache definiert jede Integrationsentscheidung, die ein .NET-Entwickler bei der Auswahl trifft: Sie schreiben einen benutzerdefinierten HttpClient, codieren Ihre Dokumente in Base64, senden sie per POST an https://api.ocr.space/parse/image, parsen die JSON-Antwort manuell, fangen HTTP 429er ab, implementieren exponentielles Backoff, erstellen eine Buchführung für die Ratenbegrenzung und definieren Ihre eigenen Ausnahmearten – und das alles bevor ein einziges Zeichen Text Ihre Anwendung erreicht. Dieser Artikel untersucht, welche Kosten der Eigenentwicklungsaufwand tatsächlich in Bezug auf Code, Zeit und Zuverlässigkeit in der Produktion verursacht, und vergleicht ihn direkt mit IronOCR , einer nativen .NET -Bibliothek, die als einzelnes NuGet Paket bereitgestellt wird.

OCR verstehen

OCR.space ist eine Freemium-REST-API, die Bilder und PDFs auf von OCR.space betriebenen Remote-Servern verarbeitet. Der Dienst positioniert sich um sein kostenloses Angebot herum: 25.000 Anfragen pro Monat ohne Kreditkarte erforderlich, was Entwickler anspricht, die mit OCR experimentieren oder persönliche Projekte realisieren. Es gibt kein NuGet Paket, kein offizielles .NET SDK und keine stark typisierte Clientbibliothek in irgendeiner Sprache. Jede .NET-Integration wird manuell auf HttpClient aufgebaut.

Die architektonische Realität for .NET -Entwickler:

  • Kein NuGet Paket: Null erstklassige .NET Unterstützung. Kein IntelliSense, keine typisierten Modelle, keine integrierte Fehlerbehandlung.
  • Verarbeitung ausschließlich in der Cloud: Jedes Dokument verlässt Ihre Infrastruktur. Es gibt keine Option für die Installation vor Ort und keinen selbstgehosteten Bereitstellungspfad.
  • Manuelle REST-Integration: Entwickler codieren Dateien in Base64, konstruieren FormUrlEncodedContent oder MultipartFormDataContent und parsen rohe JSON-Antworten.
  • Individuelle Ratenbegrenzung: Im kostenlosen Tarif sind 60 Anfragen pro Minute und 500 Anfragen pro Tag und IP-Adresse beschränkt. Produktionsanwendungen müssen diese Logik selbst implementieren.
  • Dateigrößenbeschränkungen: Im kostenlosen Tarif werden Dateien über 5 MB abgelehnt. Mehrseitige PDFs überschreiten diese Grenze häufig.
  • PDF-Ausgabe mit Wasserzeichen: Die Erstellung durchsuchbarer PDFs in der kostenlosen Version bettet OCR.space-Wasserzeichen ein, wodurch die Ausgabe für die Kundenauslieferung oder die Dokumentenarchivierung unbrauchbar wird.
  • Keine Service-Level-Vereinbarung: Im kostenlosen Tarif gibt es keine Verfügbarkeitsgarantie. Bezahlte Tarife bieten höhere Limits, unterliegen aber denselben architektonischen Beschränkungen.

Die REST-Integrationsentwickler, die schreiben müssen

Die Dokumentation von OCR.space verweist die Entwickler auf den REST-Endpunkt und überlässt es ihnen, alles Weitere selbst zu entwickeln. Der minimal funktionsfähige .NET Client – ​​bevor jegliche Überlegungen zur Produktionshärtung angestellt werden – sieht folgendermaßen aus:

// OCR.space: what you build before the first line of your actual application
public class OcrSpaceApiClient : IDisposable
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;
    private readonly SemaphoreSlim _rateLimiter;
    private const string ApiEndpoint = "https://api.ocr.space/parse/image";
    private const int MaxFileSizeFree = 5 * 1024 * 1024; // 5MB — hard limit on free tier
    private const int RateLimitPerMinute = 60;

    public OcrSpaceApiClient(string apiKey)
    {
        _apiKey = apiKey ?? throw new ArgumentNullException(nameof(apiKey));
        _httpClient = new HttpClient();
        _httpClient.Timeout = TimeSpan.FromSeconds(120);
        _rateLimiter = new SemaphoreSlim(RateLimitPerMinute, RateLimitPerMinute);
    }

    public async Task<OcrResult> ExtractTextFromFileAsync(
        string filePath,
        string language = "eng",
        CancellationToken cancellationToken = default)
    {
        if (!File.Exists(filePath))
            throw new FileNotFoundException("Image file not found", filePath);

        var fileInfo = new FileInfo(filePath);
        if (fileInfo.Length > MaxFileSizeFree)
        {
            throw new InvalidOperationException(
                $"File size {fileInfo.Length / 1024 / 1024}MB exceeds free tier limit of 5MB.");
        }

        await _rateLimiter.WaitAsync(cancellationToken);

        try
        {
            // Document leaves your infrastructure here
            byte[] imageBytes = await File.ReadAllBytesAsync(filePath, cancellationToken);
            string base64Image = Convert.ToBase64String(imageBytes);
            string mimeType = GetMimeType(filePath);

            var formContent = new FormUrlEncodedContent(new[]
            {
                new KeyValuePair<string, string>("apikey", _apiKey),
                new KeyValuePair<string, string>("base64Image", $"data:{mimeType};base64,{base64Image}"),
                new KeyValuePair<string, string>("language", language),
                new KeyValuePair<string, string>("isOverlayRequired", "false"),
                new KeyValuePair<string, string>("filetype", Path.GetExtension(filePath).TrimStart('.')),
                new KeyValuePair<string, string>("detectOrientation", "true"),
                new KeyValuePair<string, string>("scale", "true"),
                new KeyValuePair<string, string>("OCREngine", "2")
            });

            var response = await _httpClient.PostAsync(ApiEndpoint, formContent, cancellationToken);

            if (!response.IsSuccessStatusCode)
            {
                string errorBody = await response.Content.ReadAsStringAsync(cancellationToken);
                throw new OcrSpaceException(
                    $"OCR.space API returned {response.StatusCode}: {errorBody}",
                    (int)response.StatusCode);
            }

            string jsonResponse = await response.Content.ReadAsStringAsync(cancellationToken);
            return ParseOcrResponse(jsonResponse);
        }
        finally
        {
            _ = Task.Run(async () =>
            {
                await Task.Delay(60000 / RateLimitPerMinute);
                _rateLimiter.Release();
            });
        }
    }

    private OcrResult ParseOcrResponse(string jsonResponse)
    {
        using var doc = JsonDocument.Parse(jsonResponse);
        var root = doc.RootElement;

        if (root.TryGetProperty("IsErroredOnProcessing", out var isErrored) && isErrored.GetBoolean())
        {
            string errorMessage = "OCR processing failed";
            if (root.TryGetProperty("ErrorMessage", out var errorMessages))
            {
                // Parse the array of error strings manually
                var messages = new List<string>();
                if (errorMessages.ValueKind == JsonValueKind.Array)
                {
                    foreach (var msg in errorMessages.EnumerateArray())
                        messages.Add(msg.GetString() ?? "Unknown error");
                }
                errorMessage = string.Join("; ", messages);
            }
            throw new OcrSpaceException(errorMessage, 500);
        }

        var result = new OcrResult();

        if (root.TryGetProperty("ParsedResults", out var parsedResults))
        {
            foreach (var parsedResult in parsedResults.EnumerateArray())
            {
                if (parsedResult.TryGetProperty("ParsedText", out var parsedText))
                    result.ParsedText += parsedText.GetString();

                if (parsedResult.TryGetProperty("FileParseExitCode", out var exitCode))
                    result.ExitCodes.Add(exitCode.GetInt32());
            }
        }

        return result;
    }

    private string GetMimeType(string filePath) =>
        Path.GetExtension(filePath).ToLowerInvariant() switch
        {
            ".png"          => "image/png",
            ".jpg" or ".jpeg" => "image/jpeg",
            ".bmp"          => "image/bmp",
            ".tiff" or ".tif" => "image/tiff",
            ".pdf"          => "application/pdf",
            _               => "application/octet-stream"
        };

    public void Dispose()
    {
        _httpClient?.Dispose();
        _rateLimiter?.Dispose();
    }
}

// Custom models — no SDK means you define these yourself
public class OcrResult
{
    public string ParsedText { get; set; } = string.Empty;
    public List<string> Warnings { get; } = new();
    public List<int> ExitCodes { get; } = new();
}

public class OcrSpaceException : Exception
{
    public int StatusCode { get; }
    public OcrSpaceException(string message, int statusCode) : base(message)
        => StatusCode = statusCode;
}
// OCR.space: what you build before the first line of your actual application
public class OcrSpaceApiClient : IDisposable
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;
    private readonly SemaphoreSlim _rateLimiter;
    private const string ApiEndpoint = "https://api.ocr.space/parse/image";
    private const int MaxFileSizeFree = 5 * 1024 * 1024; // 5MB — hard limit on free tier
    private const int RateLimitPerMinute = 60;

    public OcrSpaceApiClient(string apiKey)
    {
        _apiKey = apiKey ?? throw new ArgumentNullException(nameof(apiKey));
        _httpClient = new HttpClient();
        _httpClient.Timeout = TimeSpan.FromSeconds(120);
        _rateLimiter = new SemaphoreSlim(RateLimitPerMinute, RateLimitPerMinute);
    }

    public async Task<OcrResult> ExtractTextFromFileAsync(
        string filePath,
        string language = "eng",
        CancellationToken cancellationToken = default)
    {
        if (!File.Exists(filePath))
            throw new FileNotFoundException("Image file not found", filePath);

        var fileInfo = new FileInfo(filePath);
        if (fileInfo.Length > MaxFileSizeFree)
        {
            throw new InvalidOperationException(
                $"File size {fileInfo.Length / 1024 / 1024}MB exceeds free tier limit of 5MB.");
        }

        await _rateLimiter.WaitAsync(cancellationToken);

        try
        {
            // Document leaves your infrastructure here
            byte[] imageBytes = await File.ReadAllBytesAsync(filePath, cancellationToken);
            string base64Image = Convert.ToBase64String(imageBytes);
            string mimeType = GetMimeType(filePath);

            var formContent = new FormUrlEncodedContent(new[]
            {
                new KeyValuePair<string, string>("apikey", _apiKey),
                new KeyValuePair<string, string>("base64Image", $"data:{mimeType};base64,{base64Image}"),
                new KeyValuePair<string, string>("language", language),
                new KeyValuePair<string, string>("isOverlayRequired", "false"),
                new KeyValuePair<string, string>("filetype", Path.GetExtension(filePath).TrimStart('.')),
                new KeyValuePair<string, string>("detectOrientation", "true"),
                new KeyValuePair<string, string>("scale", "true"),
                new KeyValuePair<string, string>("OCREngine", "2")
            });

            var response = await _httpClient.PostAsync(ApiEndpoint, formContent, cancellationToken);

            if (!response.IsSuccessStatusCode)
            {
                string errorBody = await response.Content.ReadAsStringAsync(cancellationToken);
                throw new OcrSpaceException(
                    $"OCR.space API returned {response.StatusCode}: {errorBody}",
                    (int)response.StatusCode);
            }

            string jsonResponse = await response.Content.ReadAsStringAsync(cancellationToken);
            return ParseOcrResponse(jsonResponse);
        }
        finally
        {
            _ = Task.Run(async () =>
            {
                await Task.Delay(60000 / RateLimitPerMinute);
                _rateLimiter.Release();
            });
        }
    }

    private OcrResult ParseOcrResponse(string jsonResponse)
    {
        using var doc = JsonDocument.Parse(jsonResponse);
        var root = doc.RootElement;

        if (root.TryGetProperty("IsErroredOnProcessing", out var isErrored) && isErrored.GetBoolean())
        {
            string errorMessage = "OCR processing failed";
            if (root.TryGetProperty("ErrorMessage", out var errorMessages))
            {
                // Parse the array of error strings manually
                var messages = new List<string>();
                if (errorMessages.ValueKind == JsonValueKind.Array)
                {
                    foreach (var msg in errorMessages.EnumerateArray())
                        messages.Add(msg.GetString() ?? "Unknown error");
                }
                errorMessage = string.Join("; ", messages);
            }
            throw new OcrSpaceException(errorMessage, 500);
        }

        var result = new OcrResult();

        if (root.TryGetProperty("ParsedResults", out var parsedResults))
        {
            foreach (var parsedResult in parsedResults.EnumerateArray())
            {
                if (parsedResult.TryGetProperty("ParsedText", out var parsedText))
                    result.ParsedText += parsedText.GetString();

                if (parsedResult.TryGetProperty("FileParseExitCode", out var exitCode))
                    result.ExitCodes.Add(exitCode.GetInt32());
            }
        }

        return result;
    }

    private string GetMimeType(string filePath) =>
        Path.GetExtension(filePath).ToLowerInvariant() switch
        {
            ".png"          => "image/png",
            ".jpg" or ".jpeg" => "image/jpeg",
            ".bmp"          => "image/bmp",
            ".tiff" or ".tif" => "image/tiff",
            ".pdf"          => "application/pdf",
            _               => "application/octet-stream"
        };

    public void Dispose()
    {
        _httpClient?.Dispose();
        _rateLimiter?.Dispose();
    }
}

// Custom models — no SDK means you define these yourself
public class OcrResult
{
    public string ParsedText { get; set; } = string.Empty;
    public List<string> Warnings { get; } = new();
    public List<int> ExitCodes { get; } = new();
}

public class OcrSpaceException : Exception
{
    public int StatusCode { get; }
    public OcrSpaceException(string message, int statusCode) : base(message)
        => StatusCode = statusCode;
}
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading
Imports System.Threading.Tasks

' OCR.space: what you build before the first line of your actual application
Public Class OcrSpaceApiClient
    Implements IDisposable

    Private ReadOnly _httpClient As HttpClient
    Private ReadOnly _apiKey As String
    Private ReadOnly _rateLimiter As SemaphoreSlim
    Private Const ApiEndpoint As String = "https://api.ocr.space/parse/image"
    Private Const MaxFileSizeFree As Integer = 5 * 1024 * 1024 ' 5MB — hard limit on free tier
    Private Const RateLimitPerMinute As Integer = 60

    Public Sub New(apiKey As String)
        _apiKey = If(apiKey, Throw New ArgumentNullException(NameOf(apiKey)))
        _httpClient = New HttpClient()
        _httpClient.Timeout = TimeSpan.FromSeconds(120)
        _rateLimiter = New SemaphoreSlim(RateLimitPerMinute, RateLimitPerMinute)
    End Sub

    Public Async Function ExtractTextFromFileAsync(filePath As String, Optional language As String = "eng", Optional cancellationToken As CancellationToken = Nothing) As Task(Of OcrResult)
        If Not File.Exists(filePath) Then
            Throw New FileNotFoundException("Image file not found", filePath)
        End If

        Dim fileInfo = New FileInfo(filePath)
        If fileInfo.Length > MaxFileSizeFree Then
            Throw New InvalidOperationException($"File size {fileInfo.Length \ 1024 \ 1024}MB exceeds free tier limit of 5MB.")
        End If

        Await _rateLimiter.WaitAsync(cancellationToken)

        Try
            ' Document leaves your infrastructure here
            Dim imageBytes As Byte() = Await File.ReadAllBytesAsync(filePath, cancellationToken)
            Dim base64Image As String = Convert.ToBase64String(imageBytes)
            Dim mimeType As String = GetMimeType(filePath)

            Dim formContent = New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
                New KeyValuePair(Of String, String)("apikey", _apiKey),
                New KeyValuePair(Of String, String)("base64Image", $"data:{mimeType};base64,{base64Image}"),
                New KeyValuePair(Of String, String)("language", language),
                New KeyValuePair(Of String, String)("isOverlayRequired", "false"),
                New KeyValuePair(Of String, String)("filetype", Path.GetExtension(filePath).TrimStart("."c)),
                New KeyValuePair(Of String, String)("detectOrientation", "true"),
                New KeyValuePair(Of String, String)("scale", "true"),
                New KeyValuePair(Of String, String)("OCREngine", "2")
            })

            Dim response = Await _httpClient.PostAsync(ApiEndpoint, formContent, cancellationToken)

            If Not response.IsSuccessStatusCode Then
                Dim errorBody = Await response.Content.ReadAsStringAsync(cancellationToken)
                Throw New OcrSpaceException($"OCR.space API returned {response.StatusCode}: {errorBody}", CInt(response.StatusCode))
            End If

            Dim jsonResponse = Await response.Content.ReadAsStringAsync(cancellationToken)
            Return ParseOcrResponse(jsonResponse)
        Finally
            _ = Task.Run(Async Function()
                             Await Task.Delay(60000 \ RateLimitPerMinute)
                             _rateLimiter.Release()
                         End Function)
        End Try
    End Function

    Private Function ParseOcrResponse(jsonResponse As String) As OcrResult
        Using doc = JsonDocument.Parse(jsonResponse)
            Dim root = doc.RootElement

            If root.TryGetProperty("IsErroredOnProcessing", ByRef isErrored) AndAlso isErrored.GetBoolean() Then
                Dim errorMessage As String = "OCR processing failed"
                If root.TryGetProperty("ErrorMessage", ByRef errorMessages) Then
                    ' Parse the array of error strings manually
                    Dim messages = New List(Of String)()
                    If errorMessages.ValueKind = JsonValueKind.Array Then
                        For Each msg In errorMessages.EnumerateArray()
                            messages.Add(msg.GetString() OrElse "Unknown error")
                        Next
                    End If
                    errorMessage = String.Join("; ", messages)
                End If
                Throw New OcrSpaceException(errorMessage, 500)
            End If

            Dim result = New OcrResult()

            If root.TryGetProperty("ParsedResults", ByRef parsedResults) Then
                For Each parsedResult In parsedResults.EnumerateArray()
                    If parsedResult.TryGetProperty("ParsedText", ByRef parsedText) Then
                        result.ParsedText &= parsedText.GetString()
                    End If

                    If parsedResult.TryGetProperty("FileParseExitCode", ByRef exitCode) Then
                        result.ExitCodes.Add(exitCode.GetInt32())
                    End If
                Next
            End If

            Return result
        End Using
    End Function

    Private Function GetMimeType(filePath As String) As String
        Return Path.GetExtension(filePath).ToLowerInvariant() Select Case
            Case ".png" : Return "image/png"
            Case ".jpg", ".jpeg" : Return "image/jpeg"
            Case ".bmp" : Return "image/bmp"
            Case ".tiff", ".tif" : Return "image/tiff"
            Case ".pdf" : Return "application/pdf"
            Case Else : Return "application/octet-stream"
        End Select
    End Function

    Public Sub Dispose() Implements IDisposable.Dispose
        _httpClient?.Dispose()
        _rateLimiter?.Dispose()
    End Sub
End Class

' Custom models — no SDK means you define these yourself
Public Class OcrResult
    Public Property ParsedText As String = String.Empty
    Public ReadOnly Property Warnings As New List(Of String)()
    Public ReadOnly Property ExitCodes As New List(Of Integer)()
End Class

Public Class OcrSpaceException
    Inherits Exception

    Public ReadOnly Property StatusCode As Integer

    Public Sub New(message As String, statusCode As Integer)
        MyBase.New(message)
        Me.StatusCode = statusCode
    End Sub
End Class
$vbLabelText   $csharpLabel

Hierbei handelt es sich um mehr als 90 Zeilen Infrastrukturcode, die vor der ersten Methode der Geschäftslogik existieren. Jede OCR.space-Integration liefert dieses Boilerplate (oder etwas Äquivalentes) aus, und jedes Team, das es schreibt, löst dasselbe Problem, das OCR.space ihnen nicht zur Lösung vornimmt.

IronOCR verstehen

IronOCR ist eine kommerzielle OCR-Bibliothek für .NET, die über das NuGet-Paket IronOcr installiert wird. Es beinhaltet eine optimierte Tesseract 5-Engine mit automatischer Vorverarbeitung, nativer PDF-Eingabe, durchsuchbarer PDF-Ausgabe, über 125 Sprachpaketen und strukturierter Ergebnisextraktion – alles ohne Cloud-Abhängigkeit, API-Schlüssel oder Gebühren pro Anfrage. Die Dokumente werden lokal auf der Infrastruktur verarbeitet, auf der die .NET -Anwendung ausgeführt wird.

Hauptmerkmale:

  • Einzelnes NuGet-Paket: dotnet add package IronOcr ist die vollständige Installation. Keine nativen Binärdateien zu verwalten, keine Tessdata-Verzeichnisse, keine Umgebungsvariablen.
  • Stark typisierte API: IronTesseract, OcrInput, OcrResult und CropRectangle sind erstklassige .NET-Typen mit vollständiger IntelliSense-Unterstützung.
  • Automatische Vorverarbeitung: Filter wie Deskew, DeNoise, Contrast, Binarize und EnhanceResolution werden automatisch oder bei Bedarf ohne externe Bibliotheken angewendet.
  • Native PDF-Unterstützung: Sowohl das Lesen gescannter PDFs als auch das Generieren durchsuchbarer PDFs sind integriert. Über den verfügbaren Speicher hinaus gibt es keine Größenbeschränkungen.
  • Lokale Ausführung: Während der OCR findet kein Netzwerkaufruf statt. Die Bibliothek läuft vollständig innerhalb desselben Prozesses. Air-Gap-Umgebungen funktionieren ohne Konfigurationsänderungen.
  • Thread-sicher: Mehrere IronTesseract-Instanzen laufen gleichzeitig ohne Sperren oder Contention-Management.
  • Unbefristete Lizenzierung: $999 Lite / $1,499 Plus / $2,999 Professional. Keine Gebühren pro Anfrage, unabhängig vom Umfang.

Funktionsvergleich

Feature OCR.space IronOCR
NuGet -Paket Keine – nur REST-API IronOcr — volle .NET-Unterstützung
Verarbeitungsort OCR.space Cloud-Server Lokal – Ihre Infrastruktur
Kostenloses Tarif 25.000 Stück/Monat (begrenzt) Testversion verfügbar
Bezahlte Preise Kontaktieren Sie OCR.space für aktuelle Preise. $2,999 Einmal unbefristet
Ratenbegrenzung 60 Anfragen/Minute, 500/Tag (kostenlos) None
Offline-Fähigkeit Nein Ja – vollständig luftisoliert
Datenschutz An Dritte gesendete Dokumente Die Dokumente verbleiben auf Ihren Servern.

Detaillierter Funktionsvergleich

Feature OCR.space IronOCR
Integration
NuGet-Paket None Ja (IronOcr)
Stark typisierte Modelle Keine — manuelle JSON-Analyse Ja (OcrResult, OcrInput)
IntelliSense-Unterstützung None Voll
Benutzerdefinierte Ausnahmetypen Du musst dich selbst definieren Eingebaut
Wiederholungslogik Muss man selbst bauen Eingebaut
Verarbeitung
Verarbeitungsort Cloud-Server Lokaler laufender Prozess
Internet erforderlich Ja – jeder Anruf Nein
Luftspaltunterstützung Nein Ja
Ratenbegrenzungen Ja – alle Pläne None
Dateigrößenbeschränkungen 5 MB (kostenlose Version) Nur Speicher
OCR-Fähigkeit
Bild-OCR Ja Ja
PDF-OCR Ja (mit Einschränkungen) Ja (nativ, keine Größenbeschränkung)
Durchsuchbare PDF-Ausgabe Im kostenlosen Tarif mit Wasserzeichen versehen Saubere Ausgabe, keine Wasserzeichen
Automatische Vorverarbeitung Keine (nur serverseitig) Entzerren, Rauschen entfernen, Kontrast, Binarisieren, Auflösung verbessern
Manuelle Vorverarbeitungssteuerung None Vollständige Filter-API
Sprachunterstützung ~25 Sprachen Über 125 Sprachpakete via NuGet
Mehrsprachigkeit pro Dokument Beschränkt Ja – Primär- und Sekundärsprachen
Strukturierte Ausgabe (Wörter/Zeilen/Seiten) Nein – nur Klartext Ja – Seiten, Absätze, Zeilen, Wörter mit Koordinaten
Konfidenzwerte Nein Ja – pro Ergebnis und pro Wort
Regionsbasierte OCR Nein Ja — CropRectangle
Barcode-Lesung während der OCR Nein Ja — ReadBarCodes = true
Einsatz
Docker Erfordert ausgehende Internetverbindung Funktioniert out of the box
Linux Erfordert ausgehende Internetverbindung Vollständig unterstützt
HIPAA-konform Risiko – Dokumente verlassen Ihre Kontrolle Ja – keine externe Übertragung
DSGVO-konform Risiko – Unklarer EU-Datentransfer Ja – keine Datenverarbeitung durch Dritte
Preisgestaltung
Preismodell Monatliches Abonnement Einmalige unbefristete Lizenz
Einstiegspreis Kontaktieren Sie OCR.space für aktuelle Preise. $999 einmalig
Kosten pro Dokument im großen Maßstab Ja None
SLA Keine (kostenlos), variabel (kostenpflichtig) Enterprise SLAverfügbar

SDK-Tiefe vs. rohes HTTP: Die Integrationskosten

Der Unterschied zwischen OCR.space und einem nativen SDK ist keine Frage der Stilpräferenz. Gemessen wird dies in Entwicklungsstunden, Angriffsfläche für Fehler und Wartungsaufwand bei jeder Bereitstellung.

OCR.space-Ansatz

Jeder .NET Entwickler, der OCR.space verwendet, schreibt seinen eigenen HTTP-Client. Die einfachste mögliche Implementierung – grundlegende Textextraktion ohne Wiederholungslogik, ohne Stapelverarbeitung und ohne Vorverarbeitung – umfasst immer noch mehr als 50 Zeilen:

// OCR.space: minimum viable extraction — 50+lines before business logic
public class OcrSpaceBasicExtraction
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;

    public OcrSpaceBasicExtraction(string apiKey)
    {
        _apiKey = apiKey;
        _httpClient = new HttpClient();
    }

    public async Task<string> ExtractText(string imagePath)
    {
        // Read file and encode — document leaves your infrastructure
        byte[] imageBytes = File.ReadAllBytes(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", "eng")
        });

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

        if (!response.IsSuccessStatusCode)
            throw new Exception($"API error: {response.StatusCode}");

        // Parse JSON manually — no typed models
        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: minimum viable extraction — 50+lines before business logic
public class OcrSpaceBasicExtraction
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;

    public OcrSpaceBasicExtraction(string apiKey)
    {
        _apiKey = apiKey;
        _httpClient = new HttpClient();
    }

    public async Task<string> ExtractText(string imagePath)
    {
        // Read file and encode — document leaves your infrastructure
        byte[] imageBytes = File.ReadAllBytes(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", "eng")
        });

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

        if (!response.IsSuccessStatusCode)
            throw new Exception($"API error: {response.StatusCode}");

        // Parse JSON manually — no typed models
        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.Net.Http
Imports System.IO
Imports System.Threading.Tasks
Imports System.Collections.Generic
Imports System.Text.Json

Public Class OcrSpaceBasicExtraction
    Private ReadOnly _httpClient As HttpClient
    Private ReadOnly _apiKey As String

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

    Public Async Function ExtractText(imagePath As String) As Task(Of String)
        ' Read file and encode — document leaves your infrastructure
        Dim imageBytes As Byte() = File.ReadAllBytes(imagePath)
        Dim base64 As String = Convert.ToBase64String(imageBytes)

        Dim content 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")
        })

        ' Send to cloud
        Dim response As HttpResponseMessage = Await _httpClient.PostAsync("https://api.ocr.space/parse/image", content)

        If Not response.IsSuccessStatusCode Then
            Throw New Exception($"API error: {response.StatusCode}")
        End If

        ' Parse JSON manually — no typed models
        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

Fügt man die Stapelverarbeitung hinzu, steigt die Zeilenanzahl auf über 80, da für jede Stapelanfrage eine Ratenbegrenzung, eine Wiederholungslogik pro Anfrage mit exponentiellem Backoff und eine explizite Behandlung von HTTP 429-Antworten erforderlich sind. Fügt man die PDF-Verarbeitung hinzu, wird vor jedem Anruf eine Dateigrößenprüfung von 5 MB durchgeführt, da die kostenlose Version größere Dateien mit einer Fehlermeldung anstatt einer Warnung ablehnt.

IronOCR-Ansatz

Die Installation von IronOCR über NuGet ersetzt den gesamten Infrastrukturcode durch bereits vorhandene Methodenaufrufe:

// IronOCR: complete implementation
var result = new IronTesseract().Read("invoice.png");
Console.WriteLine(result.Text);
// IronOCR: complete implementation
var result = new IronTesseract().Read("invoice.png");
Console.WriteLine(result.Text);
$vbLabelText   $csharpLabel

Die IronTesseract-Klasse verwaltet das Dateilesen, die Vorverarbeitung, die Ausführung der Engine und die Ergebnisbildung. Kein HTTP-Client. Keine Base64-Kodierung. Keine JSON-Analyse. Kein API-Schlüssel. Keine Ratenbegrenzung. Das Tutorial zum Lesen von Text aus Bildern behandelt verschiedene Eingabemethoden – Dateipfade, Byte-Arrays, Datenströme, Bitmaps –, die alle dem gleichen Ein-Aufruf-Muster folgen.

Die aus den Quelldateien ermittelten Komplexitätszahlen des Codes sind nicht hypothetisch:

Szenario OCR.Leerzeichen Zeilen IronOCR Leitungen
Basisextraktion 50+ 3
PDF-Verarbeitung 60+ 12
Stapelverarbeitung 80+ 15
Fehlerbehandlung 70+ 15
Vollständige Client-Infrastruktur Mehr als 200 0 (Wird von NuGet bereitgestellt)

Stapelverarbeitung und Ratenbegrenzungen

Das Problem der Ratenbegrenzung ist der Punkt, an dem die kostenlose Version von OCR.space unter realen Produktionsbedingungen am deutlichsten versagt.

OCR.space-Ansatz

Stapelverarbeitung unter OCR.space erfordert den Aufbau und die Verwaltung eines SemaphoreSlim, um innerhalb der 60-Anfragen-pro-Minute-Beschränkung zu bleiben, plus exponentielles Backoff-Wiederholungslogik für HTTP 429-Antworten, die eintreffen, wenn die Semaphorlogik unpräzise ist oder wenn geteilte Infrastruktur eine IP-Adresse teilt:

// OCR.space batch: 80+lines of rate-limit plumbing before actual work
public class OcrSpaceBatchProcessing
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;
    private readonly SemaphoreSlim _rateLimiter;

    public OcrSpaceBatchProcessing(string apiKey)
    {
        _apiKey = apiKey;
        _httpClient = new HttpClient();
        _rateLimiter = new SemaphoreSlim(60, 60); // Free tier: 60/minute
    }

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

        foreach (var imagePath in imagePaths)
        {
            await _rateLimiter.WaitAsync();

            try
            {
                string text = await ProcessWithRetry(imagePath);
                results[imagePath] = text;
            }
            finally
            {
                _ = Task.Run(async () =>
                {
                    await Task.Delay(1000); // 1 second spacing
                    _rateLimiter.Release();
                });
            }
        }

        return results;
    }

    private async Task<string> ProcessWithRetry(string imagePath, int maxRetries = 3)
    {
        for (int attempt = 0; attempt < maxRetries; attempt++)
        {
            try
            {
                byte[] imageBytes = File.ReadAllBytes(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", "eng")
                });

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

                if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
                {
                    // Rate limited — exponential backoff
                    await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
                    continue;
                }

                response.EnsureSuccessStatusCode();

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

                return doc.RootElement
                    .GetProperty("ParsedResults")[0]
                    .GetProperty("ParsedText")
                    .GetString() ?? string.Empty;
            }
            catch (HttpRequestException) when (attempt < maxRetries - 1)
            {
                await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
            }
        }

        throw new Exception($"Failed to process {imagePath} after {maxRetries} attempts");
    }
}
// OCR.space batch: 80+lines of rate-limit plumbing before actual work
public class OcrSpaceBatchProcessing
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;
    private readonly SemaphoreSlim _rateLimiter;

    public OcrSpaceBatchProcessing(string apiKey)
    {
        _apiKey = apiKey;
        _httpClient = new HttpClient();
        _rateLimiter = new SemaphoreSlim(60, 60); // Free tier: 60/minute
    }

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

        foreach (var imagePath in imagePaths)
        {
            await _rateLimiter.WaitAsync();

            try
            {
                string text = await ProcessWithRetry(imagePath);
                results[imagePath] = text;
            }
            finally
            {
                _ = Task.Run(async () =>
                {
                    await Task.Delay(1000); // 1 second spacing
                    _rateLimiter.Release();
                });
            }
        }

        return results;
    }

    private async Task<string> ProcessWithRetry(string imagePath, int maxRetries = 3)
    {
        for (int attempt = 0; attempt < maxRetries; attempt++)
        {
            try
            {
                byte[] imageBytes = File.ReadAllBytes(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", "eng")
                });

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

                if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
                {
                    // Rate limited — exponential backoff
                    await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
                    continue;
                }

                response.EnsureSuccessStatusCode();

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

                return doc.RootElement
                    .GetProperty("ParsedResults")[0]
                    .GetProperty("ParsedText")
                    .GetString() ?? string.Empty;
            }
            catch (HttpRequestException) when (attempt < maxRetries - 1)
            {
                await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
            }
        }

        throw new Exception($"Failed to process {imagePath} after {maxRetries} attempts");
    }
}
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading
Imports System.Threading.Tasks

Public Class OcrSpaceBatchProcessing
    Private ReadOnly _httpClient As HttpClient
    Private ReadOnly _apiKey As String
    Private ReadOnly _rateLimiter As SemaphoreSlim

    Public Sub New(apiKey As String)
        _apiKey = apiKey
        _httpClient = New HttpClient()
        _rateLimiter = New SemaphoreSlim(60, 60) ' Free tier: 60/minute
    End Sub

    Public Async Function ProcessBatch(imagePaths As String()) As Task(Of Dictionary(Of String, String))
        Dim results As New Dictionary(Of String, String)()

        For Each imagePath In imagePaths
            Await _rateLimiter.WaitAsync()

            Try
                Dim text As String = Await ProcessWithRetry(imagePath)
                results(imagePath) = text
            Finally
                _ = Task.Run(Async Function()
                                 Await Task.Delay(1000) ' 1 second spacing
                                 _rateLimiter.Release()
                             End Function)
            End Try
        Next

        Return results
    End Function

    Private Async Function ProcessWithRetry(imagePath As String, Optional maxRetries As Integer = 3) As Task(Of String)
        For attempt As Integer = 0 To maxRetries - 1
            Try
                Dim imageBytes As Byte() = File.ReadAllBytes(imagePath)
                Dim base64 As String = Convert.ToBase64String(imageBytes)

                Dim content 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")
                })

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

                If response.StatusCode = System.Net.HttpStatusCode.TooManyRequests Then
                    ' Rate limited — exponential backoff
                    Await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)))
                    Continue For
                End If

                response.EnsureSuccessStatusCode()

                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
            Catch ex As HttpRequestException When attempt < maxRetries - 1
                Await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)))
            End Try
        Next

        Throw New Exception($"Failed to process {imagePath} after {maxRetries} attempts")
    End Function
End Class
$vbLabelText   $csharpLabel

Die Begrenzung auf 500 Anfragen pro Tag im kostenlosen Tarif schränkt dies zusätzlich ein. Eine Anwendung, die 600 Dokumente an einem einzigen Tag verarbeitet, wird mittendrin abbrechen, ohne dass eine automatische Fortsetzung bis zum nächsten Kalendertag erfolgt.

IronOCR-Ansatz

IronOCR hat keine Ratenbegrenzung. Die Stapelverarbeitung ist eine Schleife:

//IronOCR batch: 8 lines, no rate limits, no retries needed
public Dictionary<string, string> ProcessBatch(string[] imagePaths)
{
    var results = new Dictionary<string, string>();
    var ocr = new IronTesseract();

    foreach (var imagePath in imagePaths)
    {
        //Neinrate limits, no retries, no cloud dependency
        var result = ocr.Read(imagePath);
        results[imagePath] = result.Text;
    }

    return results;
}
//IronOCR batch: 8 lines, no rate limits, no retries needed
public Dictionary<string, string> ProcessBatch(string[] imagePaths)
{
    var results = new Dictionary<string, string>();
    var ocr = new IronTesseract();

    foreach (var imagePath in imagePaths)
    {
        //Neinrate limits, no retries, no cloud dependency
        var result = ocr.Read(imagePath);
        results[imagePath] = result.Text;
    }

    return results;
}
Imports System.Collections.Generic

Public Function ProcessBatch(imagePaths As String()) As Dictionary(Of String, String)
    Dim results As New Dictionary(Of String, String)()
    Dim ocr As New IronTesseract()

    For Each imagePath In imagePaths
        'Neinrate limits, no retries, no cloud dependency
        Dim result = ocr.Read(imagePath)
        results(imagePath) = result.Text
    Next

    Return results
End Function
$vbLabelText   $csharpLabel

Die Wiederverwendung der IronTesseract-Instanz über die Charge hinweg vermeidet die wiederholte Initialisierung der Engine. Die Verarbeitungsgeschwindigkeit wird durch die lokale CPU und die Festplatten-E/A begrenzt, nicht durch die Drosselungsrichtlinie einer entfernten API. Das Beispiel zur Multithreading-Architektur zeigt, wie man die Verarbeitung über mehrere Kerne parallelisiert, wenn es auf den Durchsatz ankommt.

PDF-Verarbeitung

Die Unterstützung von PDF verdeutlicht die zweite große strukturelle Lücke.

OCR.space-Ansatz

OCR.space akzeptiert PDF-Uploads in kostenpflichtigen Tarifen und teilweise auch in der kostenlosen Version, allerdings schließt die Beschränkung der Dateigröße auf 5 MB bei kostenlosen Konten die meisten mehrseitigen Dokumente aus. Die kostenlose, durchsuchbare PDF-Ausgabe enthält ein Wasserzeichen. Die Implementierung erfordert die Extraktion der Ergebnisse pro Seite und die explizite Überprüfung der Dateigröße vor jeder Anfrage:

// OCR.space PDF: size check required, watermarks on free tier
public async Task<List<string>> ExtractTextFromPdf(string pdfPath)
{
    var pageTexts = new List<string>();

    // Reject before wasting a request quota entry
    var fileInfo = new FileInfo(pdfPath);
    if (fileInfo.Length > 5 * 1024 * 1024)
        throw new InvalidOperationException("File exceeds 5MB free tier limit. Upgrade or split PDF.");

    byte[] pdfBytes = File.ReadAllBytes(pdfPath);
    string base64 = Convert.ToBase64String(pdfBytes);

    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>("language", "eng"),
        new KeyValuePair<string, string>("filetype", "PDF"),
        new KeyValuePair<string, string>("isCreateSearchablePdf", "false") // Watermarked on free tier
    });

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

    if (!response.IsSuccessStatusCode)
        throw new Exception($"API error: {response.StatusCode}");

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

    if (doc.RootElement.TryGetProperty("ParsedResults", out var results))
    {
        foreach (var result in results.EnumerateArray())
        {
            if (result.TryGetProperty("ParsedText", out var text))
                pageTexts.Add(text.GetString() ?? string.Empty);
        }
    }

    return pageTexts;
}
// OCR.space PDF: size check required, watermarks on free tier
public async Task<List<string>> ExtractTextFromPdf(string pdfPath)
{
    var pageTexts = new List<string>();

    // Reject before wasting a request quota entry
    var fileInfo = new FileInfo(pdfPath);
    if (fileInfo.Length > 5 * 1024 * 1024)
        throw new InvalidOperationException("File exceeds 5MB free tier limit. Upgrade or split PDF.");

    byte[] pdfBytes = File.ReadAllBytes(pdfPath);
    string base64 = Convert.ToBase64String(pdfBytes);

    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>("language", "eng"),
        new KeyValuePair<string, string>("filetype", "PDF"),
        new KeyValuePair<string, string>("isCreateSearchablePdf", "false") // Watermarked on free tier
    });

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

    if (!response.IsSuccessStatusCode)
        throw new Exception($"API error: {response.StatusCode}");

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

    if (doc.RootElement.TryGetProperty("ParsedResults", out var results))
    {
        foreach (var result in results.EnumerateArray())
        {
            if (result.TryGetProperty("ParsedText", out var text))
                pageTexts.Add(text.GetString() ?? string.Empty);
        }
    }

    return pageTexts;
}
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks

Public Class OCRSpaceClient
    Private ReadOnly _apiKey As String
    Private ReadOnly _httpClient As HttpClient

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

    Public Async Function ExtractTextFromPdf(pdfPath As String) As Task(Of List(Of String))
        Dim pageTexts As New List(Of String)()

        ' Reject before wasting a request quota entry
        Dim fileInfo As New FileInfo(pdfPath)
        If fileInfo.Length > 5 * 1024 * 1024 Then
            Throw New InvalidOperationException("File exceeds 5MB free tier limit. Upgrade or split PDF.")
        End If

        Dim pdfBytes As Byte() = File.ReadAllBytes(pdfPath)
        Dim base64 As String = Convert.ToBase64String(pdfBytes)

        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)("language", "eng"),
            New KeyValuePair(Of String, String)("filetype", "PDF"),
            New KeyValuePair(Of String, String)("isCreateSearchablePdf", "false") ' Watermarked on free tier
        })

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

        If Not response.IsSuccessStatusCode Then
            Throw New Exception($"API error: {response.StatusCode}")
        End If

        Dim json As String = Await response.Content.ReadAsStringAsync()
        Using doc As JsonDocument = JsonDocument.Parse(json)
            If doc.RootElement.TryGetProperty("ParsedResults", results) Then
                For Each result In results.EnumerateArray()
                    If result.TryGetProperty("ParsedText", text) Then
                        pageTexts.Add(If(text.GetString(), String.Empty))
                    End If
                Next
            End If
        End Using

        Return pageTexts
    End Function
End Class
$vbLabelText   $csharpLabel

IronOCR-Ansatz

IronOCR liest PDFs nativ. Die Grenze ist der verfügbare Speicher, nicht eine willkürliche Dateigrößenschwelle. Die durchsuchbare PDF-Ausgabe erzeugt saubere Dateien ohne Wasserzeichen, unabhängig von der Lizenzstufe:

//IronOCR PDF: native support, no size limit, no watermarks
public List<string> ExtractTextFromPdf(string pdfPath)
{
    var pageTexts = new List<string>();

    using var input = new OcrInput();
    input.LoadPdf(pdfPath); //Nein5MB ceiling

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

    foreach (var page in result.Pages)
        pageTexts.Add(page.Text);

    return pageTexts;
}

// Generate searchable PDF — no watermarks
public void CreateSearchablePdf(string inputPath, string outputPath)
{
    var result = new IronTesseract().Read(inputPath);
    result.SaveAsSearchablePdf(outputPath);
}
//IronOCR PDF: native support, no size limit, no watermarks
public List<string> ExtractTextFromPdf(string pdfPath)
{
    var pageTexts = new List<string>();

    using var input = new OcrInput();
    input.LoadPdf(pdfPath); //Nein5MB ceiling

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

    foreach (var page in result.Pages)
        pageTexts.Add(page.Text);

    return pageTexts;
}

// Generate searchable PDF — no watermarks
public void CreateSearchablePdf(string inputPath, string outputPath)
{
    var result = new IronTesseract().Read(inputPath);
    result.SaveAsSearchablePdf(outputPath);
}
Imports System.Collections.Generic

'IronOCR PDF: native support, no size limit, no watermarks
Public Function ExtractTextFromPdf(pdfPath As String) As List(Of String)
    Dim pageTexts As New List(Of String)()

    Using input As New OcrInput()
        input.LoadPdf(pdfPath) 'Nein5MB ceiling

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

        For Each page In result.Pages
            pageTexts.Add(page.Text)
        Next
    End Using

    Return pageTexts
End Function

' Generate searchable PDF — no watermarks
Public Sub CreateSearchablePdf(inputPath As String, outputPath As String)
    Dim result = New IronTesseract().Read(inputPath)
    result.SaveAsSearchablePdf(outputPath)
End Sub
$vbLabelText   $csharpLabel

Passwortgeschützte PDFs sind ein einzelner Parameter: input.LoadPdf("encrypted.pdf", Password: "secret"). Die PDF-OCR-Anleitung behandelt die Auswahl des Seitenbereichs und den Umgang mit Passwörtern im Detail. Für Muster zur Erstellung durchsuchbarer PDFs siehe die Anleitung zur Erstellung durchsuchbarer PDFs .

Fehlerbehandlung

OCR.space liefert Fehler über zwei separate Kanäle: HTTP-Statuscodes und ein JSON-IsErroredOnProcessing-Flag. Produktionscode muss beides prüfen, das JSON-Fehlerarray parsen, wenn IsErroredOnProcessing wahr ist, und pro-Seite-FileParseExitCode-Werte für Teilfehler inspizieren. Nichts davon ist typisiert – alles ist String-Parsing von JsonDocument:

// OCR.space: two error layers, all string-based
public async Task<string> SafeExtract(HttpClient client, string apiKey, string imagePath)
{
    try
    {
        byte[] imageBytes = File.ReadAllBytes(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}")
        });

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

        if (!response.IsSuccessStatusCode)
        {
            switch (response.StatusCode)
            {
                case System.Net.HttpStatusCode.Unauthorized:
                    throw new Exception("Invalid API key");
                case System.Net.HttpStatusCode.TooManyRequests:
                    throw new Exception("Rate limit exceeded — wait and retry");
                case System.Net.HttpStatusCode.PaymentRequired:
                    throw new Exception("Quota exceeded — upgrade plan");
                default:
                    throw new Exception($"API error: {response.StatusCode}");
            }
        }

        // Second error layer: JSON-level failures
        string json = await response.Content.ReadAsStringAsync();
        using var doc = JsonDocument.Parse(json);

        if (doc.RootElement.TryGetProperty("IsErroredOnProcessing", out var isError)
            && isError.GetBoolean())
        {
            var messages = new List<string>();
            if (doc.RootElement.TryGetProperty("ErrorMessage", out var errors))
            {
                foreach (var e in errors.EnumerateArray())
                    messages.Add(e.GetString() ?? "Unknown error");
            }
            throw new Exception(string.Join("; ", messages));
        }

        // Third layer: per-page exit codes
        if (doc.RootElement.TryGetProperty("ParsedResults", out var results))
        {
            foreach (var result in results.EnumerateArray())
            {
                if (result.TryGetProperty("FileParseExitCode", out var exitCode)
                    && exitCode.GetInt32() != 1)
                    throw new Exception($"Page parse failed: exit code {exitCode.GetInt32()}");
            }
        }

        return doc.RootElement
            .GetProperty("ParsedResults")[0]
            .GetProperty("ParsedText")
            .GetString() ?? string.Empty;
    }
    catch (JsonException ex)
    {
        throw new Exception($"Invalid API response: {ex.Message}");
    }
    catch (HttpRequestException ex)
    {
        throw new Exception($"Network error: {ex.Message}");
    }
}
// OCR.space: two error layers, all string-based
public async Task<string> SafeExtract(HttpClient client, string apiKey, string imagePath)
{
    try
    {
        byte[] imageBytes = File.ReadAllBytes(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}")
        });

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

        if (!response.IsSuccessStatusCode)
        {
            switch (response.StatusCode)
            {
                case System.Net.HttpStatusCode.Unauthorized:
                    throw new Exception("Invalid API key");
                case System.Net.HttpStatusCode.TooManyRequests:
                    throw new Exception("Rate limit exceeded — wait and retry");
                case System.Net.HttpStatusCode.PaymentRequired:
                    throw new Exception("Quota exceeded — upgrade plan");
                default:
                    throw new Exception($"API error: {response.StatusCode}");
            }
        }

        // Second error layer: JSON-level failures
        string json = await response.Content.ReadAsStringAsync();
        using var doc = JsonDocument.Parse(json);

        if (doc.RootElement.TryGetProperty("IsErroredOnProcessing", out var isError)
            && isError.GetBoolean())
        {
            var messages = new List<string>();
            if (doc.RootElement.TryGetProperty("ErrorMessage", out var errors))
            {
                foreach (var e in errors.EnumerateArray())
                    messages.Add(e.GetString() ?? "Unknown error");
            }
            throw new Exception(string.Join("; ", messages));
        }

        // Third layer: per-page exit codes
        if (doc.RootElement.TryGetProperty("ParsedResults", out var results))
        {
            foreach (var result in results.EnumerateArray())
            {
                if (result.TryGetProperty("FileParseExitCode", out var exitCode)
                    && exitCode.GetInt32() != 1)
                    throw new Exception($"Page parse failed: exit code {exitCode.GetInt32()}");
            }
        }

        return doc.RootElement
            .GetProperty("ParsedResults")[0]
            .GetProperty("ParsedText")
            .GetString() ?? string.Empty;
    }
    catch (JsonException ex)
    {
        throw new Exception($"Invalid API response: {ex.Message}");
    }
    catch (HttpRequestException ex)
    {
        throw new Exception($"Network error: {ex.Message}");
    }
}
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks

Public Class OCRSpace
    Public Async Function SafeExtract(client As HttpClient, apiKey As String, imagePath As String) As Task(Of String)
        Try
            Dim imageBytes As Byte() = File.ReadAllBytes(imagePath)
            Dim base64 As String = Convert.ToBase64String(imageBytes)

            Dim content 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}")
            })

            Dim response As HttpResponseMessage = Await client.PostAsync("https://api.ocr.space/parse/image", content)

            If Not response.IsSuccessStatusCode Then
                Select Case response.StatusCode
                    Case System.Net.HttpStatusCode.Unauthorized
                        Throw New Exception("Invalid API key")
                    Case System.Net.HttpStatusCode.TooManyRequests
                        Throw New Exception("Rate limit exceeded — wait and retry")
                    Case System.Net.HttpStatusCode.PaymentRequired
                        Throw New Exception("Quota exceeded — upgrade plan")
                    Case Else
                        Throw New Exception($"API error: {response.StatusCode}")
                End Select
            End If

            ' Second error layer: JSON-level failures
            Dim json As String = Await response.Content.ReadAsStringAsync()
            Using doc As JsonDocument = JsonDocument.Parse(json)
                If doc.RootElement.TryGetProperty("IsErroredOnProcessing", ByRef isError) AndAlso isError.GetBoolean() Then
                    Dim messages As New List(Of String)()
                    If doc.RootElement.TryGetProperty("ErrorMessage", ByRef errors) Then
                        For Each e In errors.EnumerateArray()
                            messages.Add(e.GetString() ?? "Unknown error")
                        Next
                    End If
                    Throw New Exception(String.Join("; ", messages))
                End If

                ' Third layer: per-page exit codes
                If doc.RootElement.TryGetProperty("ParsedResults", ByRef results) Then
                    For Each result In results.EnumerateArray()
                        If result.TryGetProperty("FileParseExitCode", ByRef exitCode) AndAlso exitCode.GetInt32() <> 1 Then
                            Throw New Exception($"Page parse failed: exit code {exitCode.GetInt32()}")
                        End If
                    Next
                End If

                Return doc.RootElement _
                    .GetProperty("ParsedResults")(0) _
                    .GetProperty("ParsedText") _
                    .GetString() ?? String.Empty
            End Using
        Catch ex As JsonException
            Throw New Exception($"Invalid API response: {ex.Message}")
        Catch ex As HttpRequestException
            Throw New Exception($"Network error: {ex.Message}")
        End Try
    End Function
End Class
$vbLabelText   $csharpLabel

IronOCR löst standardmäßige .NET Ausnahmen mit klaren Fehlermeldungen aus. Kein JSON-Parsing, kein HTTP-Statuscode-Switching, keine mehrstufige Fehlerbehandlung:

// IronOCR: standard .NET exceptions
public string SafeExtract(string imagePath)
{
    try
    {
        var result = new IronTesseract().Read(imagePath);
        return result.Text;
    }
    catch (FileNotFoundException)
    {
        throw; // File not found — straightforward
    }
    catch (Exception ex) when (ex.Message.Contains("corrupt"))
    {
        throw new Exception($"Image file is corrupt: {imagePath}");
    }
    //NeinJSON errors.NeinHTTP errors.Neinrate limit errors.
}
// IronOCR: standard .NET exceptions
public string SafeExtract(string imagePath)
{
    try
    {
        var result = new IronTesseract().Read(imagePath);
        return result.Text;
    }
    catch (FileNotFoundException)
    {
        throw; // File not found — straightforward
    }
    catch (Exception ex) when (ex.Message.Contains("corrupt"))
    {
        throw new Exception($"Image file is corrupt: {imagePath}");
    }
    //NeinJSON errors.NeinHTTP errors.Neinrate limit errors.
}
Imports IronOcr

Public Function SafeExtract(imagePath As String) As String
    Try
        Dim result = New IronTesseract().Read(imagePath)
        Return result.Text
    Catch ex As FileNotFoundException
        Throw ' File not found — straightforward
    Catch ex As Exception When ex.Message.Contains("corrupt")
        Throw New Exception($"Image file is corrupt: {imagePath}")
    End Try
End Function
$vbLabelText   $csharpLabel

Die IronTesseract API-Referenz und die OcrResult API-Referenz dokumentieren die typisierte Ergebnisstruktur, einschließlich der Confidence-Eigenschaft zur Bewertung der Extraktionsqualität.

API-Mapping-Referenz

OCR.space-Konzept IronOCR-Äquivalent
POST https://api.ocr.space/parse/image new IronTesseract().Read(path)
FormUrlEncodedContent / MultipartFormDataContent OcrInput
base64Image Parameter input.LoadImage(path) oder input.LoadImage(bytes)
language Parameter ocr.Language = OcrLanguage.English
OCREngine Parameter Motor wird intern verwaltet
isOverlayRequired Parameter result.Words / result.Lines (immer verfügbar)
isCreateSearchablePdf Parameter result.SaveAsSearchablePdf(outputPath)
filetype=PDF Parameter input.LoadPdf(path)
ParsedResults[0].ParsedText result.Text
ParsedResults[n] (pro Seite) result.Pages[n].Text
IsErroredOnProcessing JSON-Flag Standard- .NET -Ausnahme
FileParseExitCode Pro-Seite-Flag Standard- .NET -Ausnahme
ProcessingTimeInMilliseconds Implizit – keine zusätzliche Analyse erforderlich
HTTP 429 / Ratenbegrenzung Nicht anwendbar – keine Ratenbegrenzungen
Benutzerdefinierter OcrResult POCO (benutzerdefiniert) IronOcr.OcrResult (bereitgestellt von NuGet)
Benutzerdefinierter OcrSpaceException (benutzerdefiniert) Standard .NET -Ausnahmetypen
SemaphoreSlim Ratenbegrenzer (selbst gebaut) Nicht erforderlich
API-Schlüssel in jeder Anfrage IronOcr.License.LicenseKey (einmal beim Start)

Wenn Teams einen Wechsel von OCR.space zu IronOCR erwägen

Erreichen des kostenlosen Tarifs während der Lasttests

Die Obergrenze von 500 Anfragen pro Tag und IP-Adresse bei OCR.space ist eine feste Grenze, keine unverbindliche Empfehlung. Teams entdecken dies während Lasttests oder Staging-Bereitstellungen, bei denen mehrere Entwickler eine Büro-IP-Adresse gemeinsam nutzen. Eine gemeinsam genutzte CI/CD-Pipeline, die Integrationstests gegen OCR.space durchführt, wird das Tageskontingent vor Ende des Geschäftstages aufbrauchen. An diesem Punkt schlägt die Anwendung fehl, nicht weil der Code fehlerhaft ist, sondern weil ein Zähler eines Drittanbieters einen willkürlichen Schwellenwert erreicht hat. Durch die Umstellung auf lokale Verarbeitung wird diese Fehlerkategorie vollständig eliminiert – es gibt kein Kontingent, das erschöpft werden könnte, da die Verarbeitung im Prozess selbst erfolgt.

Überprüfungen der Einhaltung von Vorschriften und der Datenklassifizierung

Sicherheitsüberprüfungen, die Dokumente als PII, PHI oder finanziell sensible Daten einstufen, stellen OCR.space vor ein binäres Problem: Der Dienst hat keinen On-Premise-Bereitstellungspfad. Es gibt keine entsprechende Dokumentation für HIPAA-Szenarien, die einer Vereinbarung mit Geschäftspartnern (Business Associate Agreement, BAA) entspricht, keine Struktur für eine Datenverarbeitungsvereinbarung, die EU-Datentransfers gemäß DSGVO eindeutig regelt, und keinen technischen Mechanismus, um die Übermittlung von Dokumenten selbst bei vorhandenen vertraglichen Kontrollen zu verhindern. Teams, die Beanstandungen bezüglich der Einhaltung von Vorschriften bei der Verarbeitung von Dokumenten über die Cloud erhalten, benötigen eine lokale Lösung.IronOCR erfüllt diese Anforderung durch seine Konstruktion – die Dokumente verlassen niemals den Anwendungsserver. Auf der Lizenzierungsseite wird die Struktur der unbefristeten Lizenz dokumentiert, was auch die Compliance-Dokumentation vereinfacht, da die Überprüfung durch den Cloud-Anbieter entfällt.

Aufwand für die Integrationscode-Wartung

Die Integrationen von OCR.space häufen technische Schulden im Verhältnis zu ihren Funktionsanforderungen an. Der anfängliche HTTP-Client ist handhabbar. Anschließend fügt das Team eine Wiederholungslogik hinzu. Dann fügen sie noch die Ratenbegrenzung pro IP-Adresse hinzu, da sich mehrere Dienste eine Adresse teilen. Dann fügen sie einen Schritt zur Vorvalidierung der Dateigröße hinzu. Anschließend stellen sie fest, dass sich die JSON-Fehlerstruktur der Antworten von Engine 1 und Engine 2 unterscheidet, und fügen einen Zweig hinzu. Sechs Monate später besteht die OCR.space-Integration aus 400 Zeilen Infrastrukturcode, den jedes neue Teammitglied verstehen muss, bevor es irgendetwas mit OCR zu tun hat. Wenn dieses Team die Wartungskosten mit einer $999-einmaligen IronOCR-Lizenz vergleicht, ist die Rechnung einfach.

Strukturierte Ausgabeanforderungen

OCR.space gibt reinen Text zurück, nichts anderes. Die Antwort enthält keine Wortkoordinaten, keine Zeilenumbrüche, keine Absatzsegmentierung und keine Konfidenzwerte pro Wort. Anwendungen, die bestimmte Felder aus Rechnungen extrahieren müssen – z. B. den Namen des Lieferanten in einer bekannten Region oder den Gesamtbetrag in der unteren rechten Ecke –, haben keine strukturierte Grundlage, auf der sie mit der Ausgabe von OCR.space aufbauen können.IronOCR stellt eine vollständige Dokumentenhierarchie mit Koordinaten und Konfidenzwerten auf jeder Granularitätsebene dar: Seiten, Absätze, Zeilen und einzelne Wörter. Die regionenbasierte Verarbeitung ermöglicht die gezielte Bearbeitung bestimmter Dokumentbereiche, ohne die gesamte Seitenausgabe nachbearbeiten zu müssen. Die Anleitung zur Leseoptimierung und der regionsbasierte OCR-Leitfaden behandeln diese Muster im Detail.

Volumenwachstum über das kostenlose Angebot hinaus

Bei 25.000 Anfragen pro Monat ist das kostenlose Kontingent für Szenarien mit geringem Anfrageaufkommen ausreichend. Darüber hinaus gelten bezahlte Ebenen — kontaktieren Sie OCR.space für aktuelle Preise. Diese Kosten summieren sich über die Zeit, bis der Vergleich mit einer $999-dauerhaften Lizenz gemacht wird, bei der niemals pro Anfrage Gebühren anfallen. Für Teams, die mit einem Dokumentenvolumenwachstum von über 25.000 Dokumenten pro Monat rechnen, ist die Kostenfrage für die lokale Verarbeitung eindeutig.

Gemeinsame Überlegungen zur Migration

Ersetzen des HTTP-Clients durch einen Methodenaufruf

Die Migration von OCR.space zu IronOCR ist architektonisch einfach, da beide Systeme Text aus Dokumenten zurückgeben. Der HTTP-Client, der JSON-Parser, der Ratenbegrenzer und die benutzerdefinierten Ausnahmetypen werden entfernt. An ihre Stelle tritt ein einzelnes NuGet Paket mit entsprechenden Methodenaufrufen. Der Vorher/Nachher-Vergleich an der Servicegrenze:

// Before: OCR.space service (simplified — actual implementation is Mehr als 200 lines)
public class OcrSpaceService : IDisposable
{
    private readonly HttpClient _client = new();
    private readonly string _apiKey;

    public OcrSpaceService(string apiKey) => _apiKey = apiKey;

    public async Task<string> ProcessDocument(string path)
    {
        byte[] bytes = File.ReadAllBytes(path);
        string base64 = Convert.ToBase64String(bytes);

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

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

        string json = await response.Content.ReadAsStringAsync();
        using var doc = JsonDocument.Parse(json);
        return doc.RootElement.GetProperty("ParsedResults")[0].GetProperty("ParsedText").GetString()!;
    }

    public void Dispose() => _client.Dispose();
}

// After:IronOCR service — no HttpClient, no JSON, no API key
public class IronOcrService
{
    private readonly IronTesseract _ocr = new();

    public string ProcessDocument(string path)
    {
        return _ocr.Read(path).Text;
    }
    //NeinDispose — no external connections to close
}
// Before: OCR.space service (simplified — actual implementation is Mehr als 200 lines)
public class OcrSpaceService : IDisposable
{
    private readonly HttpClient _client = new();
    private readonly string _apiKey;

    public OcrSpaceService(string apiKey) => _apiKey = apiKey;

    public async Task<string> ProcessDocument(string path)
    {
        byte[] bytes = File.ReadAllBytes(path);
        string base64 = Convert.ToBase64String(bytes);

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

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

        string json = await response.Content.ReadAsStringAsync();
        using var doc = JsonDocument.Parse(json);
        return doc.RootElement.GetProperty("ParsedResults")[0].GetProperty("ParsedText").GetString()!;
    }

    public void Dispose() => _client.Dispose();
}

// After:IronOCR service — no HttpClient, no JSON, no API key
public class IronOcrService
{
    private readonly IronTesseract _ocr = new();

    public string ProcessDocument(string path)
    {
        return _ocr.Read(path).Text;
    }
    //NeinDispose — no external connections to close
}
Imports System
Imports System.IO
Imports System.Net.Http
Imports System.Threading.Tasks
Imports System.Collections.Generic
Imports System.Text.Json

' Before: OCR.space service (simplified — actual implementation is Mehr als 200 lines)
Public Class OcrSpaceService
    Implements IDisposable

    Private ReadOnly _client As New HttpClient()
    Private ReadOnly _apiKey As String

    Public Sub New(apiKey As String)
        _apiKey = apiKey
    End Sub

    Public Async Function ProcessDocument(path As String) As Task(Of String)
        Dim bytes As Byte() = File.ReadAllBytes(path)
        Dim base64 As String = Convert.ToBase64String(bytes)

        Dim content 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}")
        })

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

        Dim json As String = Await response.Content.ReadAsStringAsync()
        Using doc As JsonDocument = JsonDocument.Parse(json)
            Return doc.RootElement.GetProperty("ParsedResults")(0).GetProperty("ParsedText").GetString()
        End Using
    End Function

    Public Sub Dispose() Implements IDisposable.Dispose
        _client.Dispose()
    End Sub
End Class

' After: IronOCR service — no HttpClient, no JSON, no API key
Public Class IronOcrService
    Private ReadOnly _ocr As New IronTesseract()

    Public Function ProcessDocument(path As String) As String
        Return _ocr.Read(path).Text
    End Function
    'NeinDispose — no external connections to close
End Class
$vbLabelText   $csharpLabel

Die IDisposable-Implementierung verschwindet, weil es keinen HttpClient zu verwalten gibt.

API-Schlüssel entfernen

OCR.space benötigt für jede HTTP-Anfrage einen API-Schlüssel. Dieser Schlüssel muss sicher gespeichert, bei Veröffentlichung regelmäßig geändert und von der Quellcodeverwaltung ausgeschlossen werden.IronOCR verwendet einen Lizenzschlüssel, der einmalig beim Start der Anwendung festgelegt wird, typischerweise aus einer Umgebungsvariablen oder einem Konfigurationssystem:

// At application startup — once, not per request
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
// At application startup — once, not per request
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
' At application startup — once, not per request
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
$vbLabelText   $csharpLabel

Nach der Migration entfallen der API-Schlüsselrotationsprozess, die Konfiguration des Geheimnismanagements und die Logik zur Schlüsseleinfügung pro Anfrage.

Vorverarbeitung nach der Migration

OCR.space wendet serverseitige Verarbeitungsmethoden an, bevor Ergebnisse zurückgegeben werden, jedoch haben Entwickler keine Kontrolle darüber, was diese Verarbeitung beinhaltet oder nicht beinhaltet.IronOCR stellt explizite Vorverarbeitungsmethoden bereit. Wenn die Dokumentqualität ein Problem darstellt – verzerrte Scans, kontrastarme Fotokopien, verrauschte Faxausgabe – besteht die Vorverarbeitungspipeline aus drei bis fünf Methodenaufrufen:

using var input = new OcrInput();
input.LoadImage("low-quality-scan.jpg");
input.Deskew();
input.DeNoise();
input.Contrast();
input.Binarize();
input.EnhanceResolution(300);
var result = new IronTesseract().Read(input);
using var input = new OcrInput();
input.LoadImage("low-quality-scan.jpg");
input.Deskew();
input.DeNoise();
input.Contrast();
input.Binarize();
input.EnhanceResolution(300);
var result = new IronTesseract().Read(input);
Imports IronOcr

Using input As New OcrInput()
    input.LoadImage("low-quality-scan.jpg")
    input.Deskew()
    input.DeNoise()
    input.Contrast()
    input.Binarize()
    input.EnhanceResolution(300)
    Dim result = New IronTesseract().Read(input)
End Using
$vbLabelText   $csharpLabel

Der Leitfaden zur Bildqualitätskorrektur und der Leitfaden zur Bildfarbkorrektur dokumentieren alle verfügbaren Filter anhand von Beispielen, die die Genauigkeitsunterschiede vor und nach der Korrektur aufzeigen.

Asynchron vs. Synchron

OCR.space-Aufrufe sind naturgemäß asynchron, da sie HTTP-Roundtrips beinhalten.IronOCR Aufrufe erfolgen standardmäßig synchron, was den Code in Nicht-Web-Kontexten vereinfacht. Für ASP.NET und Serverszenarien, die asynchrone Ausführung erfordern, unterstützt IronOCR asynchrone Operationen durch Task.Run-Verpackung oder direkte asynchrone API. Der Leitfaden zur asynchronen OCR behandelt die Muster. Das Entfernen von await von einer Servicemethode, die zuvor auf einen Cloud-Anruf gewartet hat, vereinfacht den Aufruf-Stack in Kontexten, in denen Async nur verwendet wurde, weil OCR.space es erforderte.

Zusätzliche Funktionen von IronOCR

Funktionen, die in den obigen Abschnitten nicht behandelt wurden und jeweils eine Funktionalität darstellen, für die es in OCR.space kein Äquivalent gibt:

  • hOCR-Export: result.SaveAsHocrFile("output.hocr") erzeugt standardkonforme hOCR-Ausgabe für nachgelagerte Dokumentenverarbeitungspipelines.
  • Fortschrittsverfolgung : Abonnieren Sie Fortschrittsereignisse für langlaufende, mehrseitige Stapelverarbeitungsvorgänge, um Fortschrittsbalken und ETA-Berechnungen in UI-Anwendungen zu ermöglichen.
  • Tabellenextraktion : Strukturierte Tabellendaten sind über das Ergebnismodell zugänglich und unterstützen Anwendungsfälle wie die Extraktion von Rechnungspositionen, die eine Spaltenausrichtung erfordern.
  • Spezialisierte Dokumentenlesung: Pass- und MRZ-Zonen, Kfz-Kennzeichen, MICR-Scheckzeilen und Handschrifterkennung sind speziell gebaute Funktionen, die über dieselbe IronTesseract-API zugänglich sind.

.NET-Kompatibilität und Zukunftsfähigkeit

IronOCR zielt auf .NET 6, .NET 7, .NET 8 und .NET 9 ab und bietet aktive Unterstützung für jede LTS- und STS-Version. Die Bibliothek läuft unter Windows x64, Windows x86, Linuxx64, macOS, Docker, AWS Lambda und Azure App Service – alles über dasselbe NuGet Paket ohne plattformspezifische Konfiguration. OCR.space erfordert ausgehenden HTTPS-Verkehr aus jeder Bereitstellungsumgebung. Ausgenommen sind abgeschottete Netzwerke, restriktive Unternehmensproxys und Infrastrukturen, bei denen ausgehender Datenverkehr zu APIs von Drittanbietern durch Richtlinien blockiert wird.IronOCR benötigt keine Netzwerkverbindung zur Laufzeit; Es wird vollständig innerhalb desselben Prozesses ausgeführt. Da .NET 10 voraussichtlich Ende 2026 veröffentlicht wird, bietet IronOCR dank seiner Erfolgsbilanz bei der Aufrechterhaltung der Kompatibilität über verschiedene .NET Hauptversionen hinweg Kontinuität ohne Integrationsänderungen.

Abschluss

OCR.space besetzt eine echte und nützliche Nische: Entwickler, die an einem Wochenende einen Prototyp für eine OCR-Funktion erstellen müssen, Studenten, die Konzepte der Textextraktion erforschen, oder persönliche Tools für geringe Volumina von weniger als 25.000 Dokumenten pro Monat ohne Compliance-Anforderungen. Für diese Zielgruppe bietet das kostenlose Angebot einen echten Mehrwert.

Das Problem ist, dass .NET -Entwickler typischerweise keine Prototypen fürs Wochenende entwickeln. Sie entwickeln Rechnungssysteme, Software zur Verarbeitung medizinischer Daten, Dokumentenarchivierungssysteme und Compliance-sensitive Geschäftsanwendungen. Für diese Kontexte ist das Fehlen des NuGet Pakets von OCR.space keine Unannehmlichkeit – es handelt sich um eine strukturelle Inkompatibilität. Jede Stunde, die für den Aufbau des HTTP-Clients, des Ratenbegrenzers, des JSON-Parsers und der Wiederholungsinfrastruktur aufgewendet wird, ist eine Stunde, die nicht für die eigentlichen Anforderungen der Anwendung genutzt werden kann. Diese Kosten fallen zwar zunächst an, die Wartungskosten laufen jedoch über die gesamte Lebensdauer der Integration weiter.

IronOCR adressiert den spezifischen Fehlermodus, der jede OCR.space-Integration kennzeichnet: das Fehlen eines echten SDK. Ein NuGet Paket, ein Methodenaufruf, keine HTTP-Verbindung, keine Ratenbegrenzungen, keine an externe Server übertragenen Dokumente. Der $999 Einsteigerpreis ist ein Einmalbetrag; wiederkehrende OCR.space-Abonnementkosten werden ihn im Laufe der Zeit übersteigen, insbesondere wenn das Volumen wächst. Für Teams mit Compliance-Anforderungen ist die lokale Verarbeitung die einzige Option, unabhängig von den Kosten.

Der Vergleich reduziert sich letztlich auf eine einzige Frage: Benötigt die Anwendung OCR als externe REST-Abhängigkeit oder als Bibliotheksfunktion? Bei .NET Produktionsanwendungen ist die Antwort fast immer letztere.

Hinweis:OCR.space und Tesseract sind eingetragene Marken ihrer jeweiligen Inhaber. Diese Seite ist nicht mit, von oder durch Google oder OCR.space gefördert oder gesponsert. Alle Produktnamen, Logos und Marken sind Eigentum ihrer jeweiligen Eigentümer. Vergleiche dienen nur zu Informationszwecken und spiegeln öffentlich zugängliche Informationen zum Zeitpunkt des Schreibens wider.

Häufig gestellte Fragen

Was ist die OCR.space API?

OCR.space API ist eine OCR-Lösung, die von Entwicklern und Unternehmen verwendet wird, um Text aus Bildern und Dokumenten zu extrahieren. Sie ist eine von mehreren OCR-Optionen, die neben IronOCR for .NET Application Development evaluiert wurden.

Wie ist IronOCR im Vergleich zu OCR.space API for .NET-Entwickler?

IronOCR ist eine NuGet-native OCR-Bibliothek für .NET, die IronTesseract als Kern-Engine verwendet. Im Vergleich zur OCR.space-API bietet sie eine einfachere Bereitstellung (keine SDK-Installationsprogramme), Pauschalpreise und eine saubere C#-API ohne COM-Interop oder Cloud-Abhängigkeiten.

Ist IronOCR einfacher einzurichten als OCR.space API?

IronOCR wird über ein einziges NuGet-Paket installiert. Es gibt keine SDK-Installationsprogramme, keine Lizenzdateien, die kopiert werden müssen, keine COM-Komponenten, die registriert werden müssen, und keine separaten Laufzeit-Binärdateien, die verwaltet werden müssen. Die gesamte OCR-Engine ist in diesem Paket enthalten.

Welche Genauigkeitsunterschiede bestehen zwischen OCR.space API und IronOCR?

IronOCR erreicht eine hohe Erkennungsgenauigkeit für Standardgeschäftsdokumente, Rechnungen, Quittungen und gescannte Formulare. Bei stark degradierten Dokumenten oder ungewöhnlichen Skripten variiert die Genauigkeit je nach Qualität der Quelle. IronOCR enthält Bildvorverarbeitungsfilter zur Verbesserung der Erkennung bei Eingaben von geringer Qualität.

Unterstützt IronOCR die PDF-Textextraktion?

Ja, IronOCR extrahiert Text sowohl aus nativen PDF-Dateien als auch aus gescannten PDF-Bildern in einem einzigen Aufruf. Es unterstützt auch mehrseitige TIFF-Dateien, Bilder und Streams. Bei gescannten PDFs wird die OCR seitenweise mit seitenweisen Ergebnisobjekten angewendet.

Wie sieht die OCR.space API-Lizenzierung im Vergleich zu IronOCR aus?

IronOCR verwendet eine unbefristete Pauschallizenz, bei der keine Gebühren pro Seite oder pro Scan anfallen. Unternehmen, die große Dokumentenmengen verarbeiten, zahlen unabhängig vom Volumen die gleichen Lizenzkosten. Einzelheiten und Volumenpreise finden Sie auf der IronOCR-Lizenzierungsseite.

Welche Sprachen unterstützt IronOCR?

IronOCR unterstützt 127 Sprachen über separate NuGet-Sprachpakete. Das Hinzufügen einer Sprache erfordert einen einzigen Befehl 'dotnet add package IronOcr.Languages.{Language}'. Es ist keine manuelle Dateiablage oder Pfadkonfiguration erforderlich.

Wie installiere ich IronOCR in einem .NET -Projekt?

Installation über NuGet: 'Install-Package IronOcr' in der Paketmanager-Konsole oder 'dotnet add package IronOcr' in der CLI. Zusätzliche Sprachpakete werden auf die gleiche Weise installiert. Es ist kein natives SDK-Installationsprogramm erforderlich.

Ist IronOCR im Gegensatz zu OCR.space für Docker und containerisierte Bereitstellungen geeignet?

Ja, IronOCR funktioniert in Docker-Containern über sein NuGet-Paket. Der Lizenzschlüssel wird über eine Umgebungsvariable festgelegt. Für die OCR-Engine selbst sind keine Lizenzdateien, SDK-Pfade oder Volume-Mounts erforderlich.

Kann ich IronOCR im Vergleich zu OCR.space vor dem Kauf ausprobieren?

Ja. Der IronOCR-Testmodus verarbeitet Dokumente und liefert OCR-Ergebnisse mit einem Wasserzeichen als Overlay auf der Ausgabe. Sie können die Genauigkeit an Ihren eigenen Dokumenten überprüfen, bevor Sie eine Lizenz erwerben.

Unterstützt IronOCR neben der Textextraktion auch das Lesen von Barcodes?

IronOCR konzentriert sich auf die Textextraktion und OCR. Für das Lesen von Barcodes bietet Iron Software IronBarcode als Begleitbibliothek an. Beide sind einzeln oder als Teil des Iron Suite-Pakets erhältlich.

Ist es einfach, von OCR.space API zu IronOCR zu migrieren?

Die Migration von OCR.space API zu IronOCR beinhaltet in der Regel das Ersetzen von Initialisierungssequenzen durch IronTesseract-Instanziierung, das Entfernen des COM-Lifecycle-Managements und das Aktualisieren von API-Aufrufen. Die meisten Migrationen reduzieren die Komplexität des Codes erheblich.

Kannaopat Udonpant
Software Ingenieur
Bevor er Software-Ingenieur wurde, absolvierte Kannapat ein PhD in Umweltressourcen an der Hokkaido University in Japan. Während seines Studiums wurde Kannapat auch Mitglied des Vehicle Robotics Laboratory, das Teil der Fakultät für Bioproduktionstechnik ist. Im Jahr 2022 nutzte er seine C#-Kenntnisse, um dem Engineering-Team von Iron Software ...
Weiterlesen

Iron-Support-Team

Wir sind 24 Stunden am Tag, 5 Tage die Woche online.
Chat
E-Mail
Rufen Sie mich an