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
FormUrlEncodedContentoderMultipartFormDataContentund 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
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 IronOcrist die vollständige Installation. Keine nativen Binärdateien zu verwalten, keine Tessdata-Verzeichnisse, keine Umgebungsvariablen. - Stark typisierte API:
IronTesseract,OcrInput,OcrResultundCropRectanglesind 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
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);
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
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
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
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
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
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
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
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")
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
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.
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.

