Wie installiert man Tesseract OCR unter Windows in C#?
Dieser Leitfaden führt .NET -Entwickler durch die Umstellung von Google Cloud Visionauf IronOCR als sofort einsatzbereite, lokale OCR-Engine. Er behandelt die Entfernung von Anmeldeinformationen, den Ersatz der Protobuf-Annotationsanalyse, die Vereinfachung der Stapelverarbeitung von Annotationen und die Verarbeitung mehrseitiger Dokumente – die vier strukturellen Änderungen, die den Großteil der Migrationsarbeit ausmachen.
Warum von Google Cloud VisionOCRmigrieren?
Die Entscheidung zur Migration beginnt fast immer mit einer der folgenden Erkenntnisse: Entweder blockiert ein Compliance-Audit die Übertragung von Cloud-Dokumenten, oder der operative Aufwand für die Verwaltung von GCP-Zugangsdaten, GCS-Buckets, asynchronem Polling und der Abrechnung pro Bild wird teurer als die OCR selbst.
Lebenszyklus des JSON-Schlüssels für Servicekonten. Jede Bereitstellung Ihrer Anwendung – Entwicklungsrechner, CI/CD-Pipeline, Staging-Server, Produktionsserver, Docker-Container, Kubernetes-Pod – erfordert dieselbe JSON-Schlüsseldatei für das Servicekonto. Diese Datei enthält einen privaten RSA-Schlüssel. Es darf niemals in die Quellcodeverwaltung gelangen, muss nach einem Zeitplan rotiert werden, muss mit Dateisystemberechtigungen geschützt werden und muss bei der Rotation gleichzeitig in allen Umgebungen aktualisiert werden. Ein kompromittierter Schlüssel gewährt API-Zugriff, bis er manuell in der GCP Console widerrufen wird.IronOCR ersetzt diese gesamte Bedienoberfläche durch eine einzige Lizenzschlüsselzeichenfolge, die einmalig beim Start der Anwendung festgelegt wird.
Abrechnung pro Anfrage im großen Stil. Bei 1,50 $ pro 1.000 Bildern und 0,0015 $ pro PDF-Seite sind die Kosten während der Entwicklung nicht sichtbar, fallen aber in der Produktion deutlich ins Gewicht. Eine Dokumentenverarbeitungspipeline, die 200.000 Seiten pro Monat verarbeitet, verursacht allein an API-Gebühren 300 US-Dollar pro Monat, zuzüglich GCS-Speichergebühren und Kosten für den Datenausgang. Diese 300 Dollar wiederholen sich jeden Monat auf unbestimmte Zeit. Mit der unbefristeten Lizenz von IronOCR wird OCR von einer abrechnungsfähigen Betriebsausgabe in einen fixen Kapitalposten umgewandelt, dessen Betrieb im zweiten und dritten Jahr keine Kosten verursacht.
GCS Async Pipeline für PDFs. Google Cloud Visionakzeptiert keine PDFs als direkte API-Eingabe. Die vollständige Pipeline erfordert ein zweites NuGet-Paket (Google.Cloud.Storage.V1), einen bereitgestellten GCS-Bucket, einen asynchronen Upload, einen AsyncBatchAnnotateFilesAsync-Aufruf, eine Polling-Schleife, JSON-Ausgabeparsing aus GCS und einen Bereinigungsschritt. Diese Pipeline erstreckt sich über mehr als 50 Codezeilen, bevor ein Text extrahiert wurde.IronOCR liest eine PDF-Datei in drei Zeilen, synchron und ohne externe Abhängigkeiten.
Protobuf-Symbolverkettung. Die DOCUMENT_TEXT_DETECTION-Antwort speichert Text auf Symbolebene innerhalb einer Protobuf-Hierarchie von Seiten, Blöcken, Absätzen, Wörtern und Symbolen. Das Lesen von Absatztext erfordert das Durchlaufen von fünf geschachtelten Schleifen und den Aufruf von .SelectMany(w => w.Symbols).Select(s => s.Text).IronOCR gibt Absatztext als paragraph.Text zurück — eine typisierte Zeichenfolge-Eigenschaft.
1.800 Anfragen pro Minute Standardkontingent. Batch-Workloads oberhalb des Standardkontingents erhalten StatusCode.ResourceExhausted-Antworten, die die Pipeline für 60 Sekunden pro Überschreitung stoppen. Um das Kontingent zu erhöhen, ist eine Anfrage über die GCP Console und die Genehmigung von Google erforderlich.IronOCR verarbeitet Daten lokal mit der Geschwindigkeit der verfügbaren CPU-Kerne – es gibt kein Kontingent zu verwalten, keine Genehmigung einzuholen und keine Wiederholungslogik zur Ratenbegrenzung.
Keine Offline- oder Air-Gapped-Unterstützung. Google Cloud Visionbenötigt eine Internetverbindung zu den Endpunkten von Google. Air-Gap-Netzwerke, klassifizierte Rechenzentren und industrielle Steuerungssysteme können es auf keiner Ebene architektonischer Komplexität nutzen.IronOCR benötigt nach der ersten Lizenzvalidierung keine ausgehende Netzwerkverbindung.
Das grundsätzliche Problem
Google Cloud Vision benötigt eine JSON-Schlüsseldatei auf der Festplatte, bevor die erste Zeile des OCR-Codes ausgeführt wird:
// Google Cloud Vision: JSON key file deployed to every server before this line works
// GOOGLE_APPLICATION_CREDENTIALS="/etc/secrets/service-account.json" must be set
// Key contains RSA private key — rotate manually, revoke if compromised
_client = ImageAnnotatorClient.Create();
var image = Image.FromFile("document.jpg");
var response = _client.DetectText(image); // document leaves your infrastructure
string text = response[0].Description;
// Google Cloud Vision: JSON key file deployed to every server before this line works
// GOOGLE_APPLICATION_CREDENTIALS="/etc/secrets/service-account.json" must be set
// Key contains RSA private key — rotate manually, revoke if compromised
_client = ImageAnnotatorClient.Create();
var image = Image.FromFile("document.jpg");
var response = _client.DetectText(image); // document leaves your infrastructure
string text = response[0].Description;
' Google Cloud Vision: JSON key file deployed to every server before this line works
' GOOGLE_APPLICATION_CREDENTIALS="/etc/secrets/service-account.json" must be set
' Key contains RSA private key — rotate manually, revoke if compromised
_client = ImageAnnotatorClient.Create()
Dim image = Image.FromFile("document.jpg")
Dim response = _client.DetectText(image) ' document leaves your infrastructure
Dim text As String = response(0).Description
IronOCR beginnt mit einer Zeichenkette und läuft vollständig auf dem lokalen Rechner:
// IronOCR: one string at startup, no key files, no environment variables
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
string text = new IronTesseract().Read("document.jpg").Text; // local, no cloud
// IronOCR: one string at startup, no key files, no environment variables
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
string text = new IronTesseract().Read("document.jpg").Text; // local, no cloud
Imports IronOcr
' IronOCR: one string at startup, no key files, no environment variables
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim text As String = New IronTesseract().Read("document.jpg").Text ' local, no cloud
IronOCR vs. Google Cloud VisionOCR: Funktionsvergleich
Die folgende Tabelle ordnet die Funktionen direkt den Teams zu, die den Business Case für die Migration erstellen.
| Feature | Google Cloud VisionOCR | IronOCR |
|---|---|---|
| Verarbeitungsort | Google Cloud (Remote) | Vor Ort (lokal) |
| Authentifizierung | Dienstkonto-JSON-Schlüssel + Umgebungsvariable | Lizenzschlüsselzeichenfolge |
| PDF-Eingabe | Upload zu GCS + asynchrone API | input.LoadPdf() direkt |
| Passwortgeschütztes PDF | Nicht unterstützt | LoadPdf(path, Password: "...") |
| Mehrseitige TIFF-Eingabe | Beschränkt | input.LoadImageFrames() |
| Durchsuchbare PDF-Ausgabe | Nicht verfügbar | result.SaveAsSearchablePdf() |
| Zugriff auf strukturierte Daten | Protobuf: Seiten > Blöcke > Absätze > Wörter > Symbole | result.Paragraphs, result.Lines, result.Words (typisierte .NET-Objekte) |
| Absatztexteigenschaft | Nein – erfordert Symbolverkettung | paragraph.Text direkte Eigenschaft |
| Konfidenzwerte | Pro Symbol (erfordert Schleife) | result.Confidence, word.Confidence |
| Automatische Bildvorverarbeitung | Keine (ML kümmert sich darum) | Entzerren, Rauschen entfernen, Kontrast erhöhen, Binärisierung, Schärfen |
| Regionsbasierte OCR | Keine einheimische Kulturpflanze | CropRectangle auf OcrInput |
| Barcode-Lesung | Separate API-Funktion | ocr.Configuration.ReadBarCodes = true |
| Ratenbegrenzungen | Standardmäßig 1.800 Anfragen/Minute | Keine (CPU-gebunden) |
| Offline / Air-Gap | Nein | Ja |
| Kosten pro Dokument | 1,50 $ pro 1.000 Bilder; 0,0015 $/PDF-Seite | Keine (Dauerlizenz) |
| Unterstützte Sprachen | ~50 | 125+ |
| FedRAMP-Zulassung | Nicht autorisiert | Nicht zutreffend (vor Ort) |
| HIPAA-Konformitätspfad | Geschäftspartnervereinbarung erforderlich | Keine Datenverarbeitung durch Dritte |
| .NET Framework -Unterstützung | .NET Standard 2.0+ | .NET Framework 4.6.2+ und .NET 5/6/7/8/9 |
| Erforderliche NuGet Pakete | Google.Cloud.Vision.V1 + Google.Cloud.Storage.V1 |
IronOcr nur |
| Preismodell | Abrechnung nach Verbrauch pro Anfrage | Perpetual ($999 Lite / $1,499 Plus / $2,999 Professional / $5,999 Unlimited) |
Schnellstart: Migration von Google Cloud VisionOCRzu IronOCR
Schritt 1: Ersetzen des NuGet-Pakets
Entfernen Sie die Google Cloud-Pakete:
dotnet remove package Google.Cloud.Vision.V1
dotnet remove package Google.Cloud.Storage.V1
dotnet remove package Google.Cloud.Vision.V1
dotnet remove package Google.Cloud.Storage.V1
Installieren Sie IronOCR von der NuGet Paketseite :
dotnet add package IronOcr
Schritt 2: Namespaces aktualisieren
Ersetzen Sie die Google Cloud-Namensräume durch den IronOCR-Namespace:
// Before (Google Cloud Vision)
using Google.Cloud.Vision.V1;
using Google.Cloud.Storage.V1;
using Google.Protobuf;
using Grpc.Core;
// After (IronOCR)
using IronOcr;
// Before (Google Cloud Vision)
using Google.Cloud.Vision.V1;
using Google.Cloud.Storage.V1;
using Google.Protobuf;
using Grpc.Core;
// After (IronOCR)
using IronOcr;
Imports IronOcr
Schritt 3: Lizenz initialisieren
Fügen Sie die Lizenzinitialisierung einmal beim Anwendungsstart hinzu, bevor eine IronTesseract-Instanz erstellt wird:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Im Produktionsbetrieb lesen Sie den Schlüssel aus einer Umgebungsvariablen oder einem Geheimnismanager:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
?? throw new InvalidOperationException("IRONOCR_LICENSE environment variable not set.");
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
?? throw new InvalidOperationException("IRONOCR_LICENSE environment variable not set.");
Imports System
Imports IronOcr
IronOcr.License.LicenseKey = If(Environment.GetEnvironmentVariable("IRONOCR_LICENSE"), Throw New InvalidOperationException("IRONOCR_LICENSE environment variable not set."))
Beispiele für die Code-Migration
Eliminierung der Konfiguration der Dienstkonto-Anmeldeinformationen
Die Initialisierung des Google Cloud Vision-Clients sieht zwar aus wie eine einzelne Zeile, erfordert aber eine umfangreiche Infrastruktur als Voraussetzung. Jeder Entwickler, der dem Projekt beitritt, jede Bereitstellungsumgebung und jede CI/CD-Pipeline benötigt die vollständige Konfiguration der Anmeldeinformationen, bevor der Konstruktor ohne Ausnahme abgeschlossen werden kann.
Google Cloud Vision Ansatz:
using Google.Cloud.Vision.V1;
// Prerequisites before this class can be instantiated:
// 1. GCP project created and Vision API enabled in GCP Console
// 2. Service account created with roles/cloudvision.user IAM role
// 3. JSON key file downloaded to every server that runs this code
// 4. GOOGLE_APPLICATION_CREDENTIALS env var pointing to the JSON file
// 5. JSON file excluded from source control via .gitignore
// 6. Key rotation schedule established (recommended: 90 days)
// 7. Separate credentials per environment (dev/staging/prod)
public class DocumentOcrService
{
private readonly ImageAnnotatorClient _client;
private readonly string _projectId;
public DocumentOcrService(string projectId)
{
_projectId = projectId;
// Throws RpcException(StatusCode.PermissionDenied) if any prerequisite is missing
_client = ImageAnnotatorClient.Create();
}
public string ReadDocument(string imagePath)
{
var image = Image.FromFile(imagePath);
var response = _client.DetectText(image);
return response.Count > 0 ? response[0].Description : string.Empty;
}
}
using Google.Cloud.Vision.V1;
// Prerequisites before this class can be instantiated:
// 1. GCP project created and Vision API enabled in GCP Console
// 2. Service account created with roles/cloudvision.user IAM role
// 3. JSON key file downloaded to every server that runs this code
// 4. GOOGLE_APPLICATION_CREDENTIALS env var pointing to the JSON file
// 5. JSON file excluded from source control via .gitignore
// 6. Key rotation schedule established (recommended: 90 days)
// 7. Separate credentials per environment (dev/staging/prod)
public class DocumentOcrService
{
private readonly ImageAnnotatorClient _client;
private readonly string _projectId;
public DocumentOcrService(string projectId)
{
_projectId = projectId;
// Throws RpcException(StatusCode.PermissionDenied) if any prerequisite is missing
_client = ImageAnnotatorClient.Create();
}
public string ReadDocument(string imagePath)
{
var image = Image.FromFile(imagePath);
var response = _client.DetectText(image);
return response.Count > 0 ? response[0].Description : string.Empty;
}
}
Imports Google.Cloud.Vision.V1
' Prerequisites before this class can be instantiated:
' 1. GCP project created and Vision API enabled in GCP Console
' 2. Service account created with roles/cloudvision.user IAM role
' 3. JSON key file downloaded to every server that runs this code
' 4. GOOGLE_APPLICATION_CREDENTIALS env var pointing to the JSON file
' 5. JSON file excluded from source control via .gitignore
' 6. Key rotation schedule established (recommended: 90 days)
' 7. Separate credentials per environment (dev/staging/prod)
Public Class DocumentOcrService
Private ReadOnly _client As ImageAnnotatorClient
Private ReadOnly _projectId As String
Public Sub New(projectId As String)
_projectId = projectId
' Throws RpcException(StatusCode.PermissionDenied) if any prerequisite is missing
_client = ImageAnnotatorClient.Create()
End Sub
Public Function ReadDocument(imagePath As String) As String
Dim image = Image.FromFile(imagePath)
Dim response = _client.DetectText(image)
Return If(response.Count > 0, response(0).Description, String.Empty)
End Function
End Class
IronOCR Ansatz:
using IronOcr;
// Prerequisites: set the license key once at app startup
//NeinJSON files, no environment variables beyond the key, no GCP Console configuration
//Neinkey rotation, no IAM roles, no per-environment credential sets
public class DocumentOcrService
{
private readonly IronTesseract _ocr;
public DocumentOcrService()
{
// IronTesseract is ready immediately — no external validation required
_ocr = new IronTesseract();
}
public string ReadDocument(string imagePath)
{
return _ocr.Read(imagePath).Text;
}
}
using IronOcr;
// Prerequisites: set the license key once at app startup
//NeinJSON files, no environment variables beyond the key, no GCP Console configuration
//Neinkey rotation, no IAM roles, no per-environment credential sets
public class DocumentOcrService
{
private readonly IronTesseract _ocr;
public DocumentOcrService()
{
// IronTesseract is ready immediately — no external validation required
_ocr = new IronTesseract();
}
public string ReadDocument(string imagePath)
{
return _ocr.Read(imagePath).Text;
}
}
Imports IronOcr
' Prerequisites: set the license key once at app startup
' NeinJSON files, no environment variables beyond the key, no GCP Console configuration
' Neinkey rotation, no IAM roles, no per-environment credential sets
Public Class DocumentOcrService
Private ReadOnly _ocr As IronTesseract
Public Sub New()
' IronTesseract is ready immediately — no external validation required
_ocr = New IronTesseract()
End Sub
Public Function ReadDocument(imagePath As String) As String
Return _ocr.Read(imagePath).Text
End Function
End Class
Der betriebliche Unterschied ist klar: Google Cloud Visiongeneriert zur Laufzeit fünf Kategorien von RpcException — PermissionDenied, ResourceExhausted, Unavailable, DeadlineExceeded und Unauthenticated — jede für einen anderen Infrastruktur-Fehlermodus. Die Fehlermodi von IronOCR sind IOException (Datei nicht gefunden oder gesperrt) und OcrException (Verarbeitungsfehler). Informationen zu den Konfigurationsoptionen finden Sie im IronTesseract-Setup-Leitfaden und Details zur Lizenzierung auf der IronOCR -Produktseite .
Ersetzen von Batch-Anmerkungsanfragen durch mehrere Feature-Typen
Google Cloud Vision unterstützt das Stapeln mehrerer Bilder in einem einzigen BatchAnnotateImagesRequest, wobei jedes Bild mit einer Liste von Feature-Typen konfiguriert ist. Dieses Muster wird verwendet, wenn ein einzelner Aufruf sowohl TEXT_DETECTION- als auch DOCUMENT_TEXT_DETECTION-Ergebnisse sammeln oder viele Bilder einreichen muss, um den Datenverkehr zu minimieren. Die Protobuf-Antwort erfordert das Zurückverfolgen jedes AnnotateImageResponse zu seiner ursprünglichen Anforderung nach Index.
Google Cloud Vision Ansatz:
using Google.Cloud.Vision.V1;
using System.Collections.Generic;
public class BatchAnnotationService
{
private readonly ImageAnnotatorClient _client;
public BatchAnnotationService()
{
_client = ImageAnnotatorClient.Create();
}
public List<string> BatchAnnotateImages(string[] imagePaths)
{
// Build one request per image with TEXT_DETECTION feature
var requests = imagePaths.Select(path => new AnnotateImageRequest
{
Image = Image.FromFile(path),
Features =
{
new Feature { Type = Feature.Types.Type.TextDetection },
new Feature { Type = Feature.Types.Type.DocumentTextDetection }
}
}).ToList();
// Single round-trip for all images in the batch
var batchResponse = _client.BatchAnnotateImages(requests);
// Match responses back to requests by index
var results = new List<string>();
for (int i = 0; i < batchResponse.Responses.Count; i++)
{
var response = batchResponse.Responses[i];
if (response.Error != null)
{
// Per-item error in batch — must handle individually
results.Add($"Error on {imagePaths[i]}: {response.Error.Message}");
continue;
}
// Prefer DOCUMENT_TEXT_DETECTION full text if available
var fullText = response.FullTextAnnotation?.Text
?? response.TextAnnotations.FirstOrDefault()?.Description
?? string.Empty;
results.Add(fullText);
}
return results;
}
}
using Google.Cloud.Vision.V1;
using System.Collections.Generic;
public class BatchAnnotationService
{
private readonly ImageAnnotatorClient _client;
public BatchAnnotationService()
{
_client = ImageAnnotatorClient.Create();
}
public List<string> BatchAnnotateImages(string[] imagePaths)
{
// Build one request per image with TEXT_DETECTION feature
var requests = imagePaths.Select(path => new AnnotateImageRequest
{
Image = Image.FromFile(path),
Features =
{
new Feature { Type = Feature.Types.Type.TextDetection },
new Feature { Type = Feature.Types.Type.DocumentTextDetection }
}
}).ToList();
// Single round-trip for all images in the batch
var batchResponse = _client.BatchAnnotateImages(requests);
// Match responses back to requests by index
var results = new List<string>();
for (int i = 0; i < batchResponse.Responses.Count; i++)
{
var response = batchResponse.Responses[i];
if (response.Error != null)
{
// Per-item error in batch — must handle individually
results.Add($"Error on {imagePaths[i]}: {response.Error.Message}");
continue;
}
// Prefer DOCUMENT_TEXT_DETECTION full text if available
var fullText = response.FullTextAnnotation?.Text
?? response.TextAnnotations.FirstOrDefault()?.Description
?? string.Empty;
results.Add(fullText);
}
return results;
}
}
Imports Google.Cloud.Vision.V1
Imports System.Collections.Generic
Imports System.Linq
Public Class BatchAnnotationService
Private ReadOnly _client As ImageAnnotatorClient
Public Sub New()
_client = ImageAnnotatorClient.Create()
End Sub
Public Function BatchAnnotateImages(imagePaths As String()) As List(Of String)
' Build one request per image with TEXT_DETECTION feature
Dim requests = imagePaths.Select(Function(path) New AnnotateImageRequest With {
.Image = Image.FromFile(path),
.Features = {
New Feature With {.Type = Feature.Types.Type.TextDetection},
New Feature With {.Type = Feature.Types.Type.DocumentTextDetection}
}
}).ToList()
' Single round-trip for all images in the batch
Dim batchResponse = _client.BatchAnnotateImages(requests)
' Match responses back to requests by index
Dim results = New List(Of String)()
For i As Integer = 0 To batchResponse.Responses.Count - 1
Dim response = batchResponse.Responses(i)
If response.Error IsNot Nothing Then
' Per-item error in batch — must handle individually
results.Add($"Error on {imagePaths(i)}: {response.Error.Message}")
Continue For
End If
' Prefer DOCUMENT_TEXT_DETECTION full text if available
Dim fullText = If(response.FullTextAnnotation?.Text, response.TextAnnotations.FirstOrDefault()?.Description, String.Empty)
results.Add(fullText)
Next
Return results
End Function
End Class
IronOCR Ansatz:
using IronOcr;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
public class BatchAnnotationService
{
public List<string> BatchAnnotateImages(string[] imagePaths)
{
var results = new ConcurrentDictionary<int, string>();
// Parallel processing — no batch size limit, no network round-trips
Parallel.For(0, imagePaths.Length, i =>
{
var ocr = new IronTesseract(); // thread-safe: one instance per thread
results[i] = ocr.Read(imagePaths[i]).Text;
});
// Reconstruct in original order
return Enumerable.Range(0, imagePaths.Length)
.Select(i => results[i])
.ToList();
}
public OcrResult BatchAsDocument(string[] imagePaths)
{
// Load all images into a single OcrInput for combined document output
using var input = new OcrInput();
foreach (var path in imagePaths)
input.LoadImage(path);
return new IronTesseract().Read(input);
}
}
using IronOcr;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
public class BatchAnnotationService
{
public List<string> BatchAnnotateImages(string[] imagePaths)
{
var results = new ConcurrentDictionary<int, string>();
// Parallel processing — no batch size limit, no network round-trips
Parallel.For(0, imagePaths.Length, i =>
{
var ocr = new IronTesseract(); // thread-safe: one instance per thread
results[i] = ocr.Read(imagePaths[i]).Text;
});
// Reconstruct in original order
return Enumerable.Range(0, imagePaths.Length)
.Select(i => results[i])
.ToList();
}
public OcrResult BatchAsDocument(string[] imagePaths)
{
// Load all images into a single OcrInput for combined document output
using var input = new OcrInput();
foreach (var path in imagePaths)
input.LoadImage(path);
return new IronTesseract().Read(input);
}
}
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Collections.Generic
Imports System.Threading.Tasks
Public Class BatchAnnotationService
Public Function BatchAnnotateImages(imagePaths As String()) As List(Of String)
Dim results = New ConcurrentDictionary(Of Integer, String)()
' Parallel processing — no batch size limit, no network round-trips
Parallel.For(0, imagePaths.Length, Sub(i)
Dim ocr = New IronTesseract() ' thread-safe: one instance per thread
results(i) = ocr.Read(imagePaths(i)).Text
End Sub)
' Reconstruct in original order
Return Enumerable.Range(0, imagePaths.Length) _
.Select(Function(i) results(i)) _
.ToList()
End Function
Public Function BatchAsDocument(imagePaths As String()) As OcrResult
' Load all images into a single OcrInput for combined document output
Using input As New OcrInput()
For Each path In imagePaths
input.LoadImage(path)
Next
Return New IronTesseract().Read(input)
End Using
End Function
End Class
IronOCR verarbeitet die Batches parallel auf mehreren CPU-Kernen ohne Netzwerk-Overhead. Es gibt keine BatchSize-Grenze, keine Antwort-Index-Abgleichung und keine per-Item-Fehlerbehandlung für Netzwerk- oder Anmeldeinformationen-Ausfälle. Für Workloads, die mehrere Bilder in ein einziges logisches Dokument kombinieren — beispielsweise gescannte mehrseitige Formulare, die als einzelne JPEGs eingereicht werden — lädt BatchAsDocument alle Bilder in ein OcrInput und gibt ein einheitliches OcrResult zurück, mit result.Pages-Indexierung für jedes Eingabebild. Das Beispiel zur Multithreading-Architektur zeigt Leistungsbenchmarks für die Parallelverarbeitung.
Migration der Protobuf-Wortebenen-Annotationsextraktion
Um die wortbasierten Begrenzungsrahmen und Konfidenzdaten von Google Cloud Visionzu erhalten, muss die gesamte Protobuf-Hierarchie durchlaufen werden: Seiten, dann Blöcke, dann Absätze, dann Wörter, dann Symbole. Das Extrahieren von Worttext erfordert das Verbinden von Symbolen aus jedem Wort — das Word-Objekt verfügt über keine direkte .Text-Eigenschaft. Die Begrenzungsrahmenkoordinaten werden als BoundingPoly mit einer Liste von Vertices gespeichert, statt als diskrete X-, Y-, Breite- und Höhe-Felder.
Google Cloud Vision Ansatz:
using Google.Cloud.Vision.V1;
using System.Collections.Generic;
public record WordAnnotation(string Text, int X, int Y, int Width, int Height, float Confidence);
public class WordLevelExtractor
{
private readonly ImageAnnotatorClient _client;
public WordLevelExtractor()
{
_client = ImageAnnotatorClient.Create();
}
public List<WordAnnotation> ExtractWordAnnotations(string imagePath)
{
var image = Image.FromFile(imagePath);
var annotation = _client.DetectDocumentText(image);
var words = new List<WordAnnotation>();
// Navigate: Pages -> Blocks -> Paragraphs -> Words
foreach (var page in annotation.Pages)
{
foreach (var block in page.Blocks)
{
foreach (var paragraph in block.Paragraphs)
{
foreach (var word in paragraph.Words)
{
// Word.Text does not exist — must concatenate Symbols
var text = string.Concat(word.Symbols.Select(s => s.Text));
// BoundingPoly has Vertices, not X/Y/Width/Height
var vertices = word.BoundingBox.Vertices;
int x = vertices[0].X;
int y = vertices[0].Y;
int width = vertices.Count > 1 ? vertices[1].X - vertices[0].X : 0;
int height = vertices.Count > 2 ? vertices[2].Y - vertices[0].Y : 0;
words.Add(new WordAnnotation(text, x, y, width, height, word.Confidence));
}
}
}
}
return words;
}
}
using Google.Cloud.Vision.V1;
using System.Collections.Generic;
public record WordAnnotation(string Text, int X, int Y, int Width, int Height, float Confidence);
public class WordLevelExtractor
{
private readonly ImageAnnotatorClient _client;
public WordLevelExtractor()
{
_client = ImageAnnotatorClient.Create();
}
public List<WordAnnotation> ExtractWordAnnotations(string imagePath)
{
var image = Image.FromFile(imagePath);
var annotation = _client.DetectDocumentText(image);
var words = new List<WordAnnotation>();
// Navigate: Pages -> Blocks -> Paragraphs -> Words
foreach (var page in annotation.Pages)
{
foreach (var block in page.Blocks)
{
foreach (var paragraph in block.Paragraphs)
{
foreach (var word in paragraph.Words)
{
// Word.Text does not exist — must concatenate Symbols
var text = string.Concat(word.Symbols.Select(s => s.Text));
// BoundingPoly has Vertices, not X/Y/Width/Height
var vertices = word.BoundingBox.Vertices;
int x = vertices[0].X;
int y = vertices[0].Y;
int width = vertices.Count > 1 ? vertices[1].X - vertices[0].X : 0;
int height = vertices.Count > 2 ? vertices[2].Y - vertices[0].Y : 0;
words.Add(new WordAnnotation(text, x, y, width, height, word.Confidence));
}
}
}
}
return words;
}
}
Imports Google.Cloud.Vision.V1
Imports System.Collections.Generic
Imports System.Linq
Public Class WordAnnotation
Public Property Text As String
Public Property X As Integer
Public Property Y As Integer
Public Property Width As Integer
Public Property Height As Integer
Public Property Confidence As Single
Public Sub New(text As String, x As Integer, y As Integer, width As Integer, height As Integer, confidence As Single)
Me.Text = text
Me.X = x
Me.Y = y
Me.Width = width
Me.Height = height
Me.Confidence = confidence
End Sub
End Class
Public Class WordLevelExtractor
Private ReadOnly _client As ImageAnnotatorClient
Public Sub New()
_client = ImageAnnotatorClient.Create()
End Sub
Public Function ExtractWordAnnotations(imagePath As String) As List(Of WordAnnotation)
Dim image = Image.FromFile(imagePath)
Dim annotation = _client.DetectDocumentText(image)
Dim words As New List(Of WordAnnotation)()
' Navigate: Pages -> Blocks -> Paragraphs -> Words
For Each page In annotation.Pages
For Each block In page.Blocks
For Each paragraph In block.Paragraphs
For Each word In paragraph.Words
' Word.Text does not exist — must concatenate Symbols
Dim text = String.Concat(word.Symbols.Select(Function(s) s.Text))
' BoundingPoly has Vertices, not X/Y/Width/Height
Dim vertices = word.BoundingBox.Vertices
Dim x As Integer = vertices(0).X
Dim y As Integer = vertices(0).Y
Dim width As Integer = If(vertices.Count > 1, vertices(1).X - vertices(0).X, 0)
Dim height As Integer = If(vertices.Count > 2, vertices(2).Y - vertices(0).Y, 0)
words.Add(New WordAnnotation(text, x, y, width, height, word.Confidence))
Next
Next
Next
Next
Return words
End Function
End Class
IronOCR Ansatz:
using IronOcr;
using System.Collections.Generic;
public record WordAnnotation(string Text, int X, int Y, int Width, int Height, double Confidence);
public class WordLevelExtractor
{
private readonly IronTesseract _ocr;
public WordLevelExtractor()
{
_ocr = new IronTesseract();
}
public List<WordAnnotation> ExtractWordAnnotations(string imagePath)
{
var result = _ocr.Read(imagePath);
// Words are a flat collection — no hierarchy traversal, no symbol concatenation
return result.Words.Select(w => new WordAnnotation(
Text: w.Text, // direct string property
X: w.X,
Y: w.Y,
Width: w.Width,
Height: w.Height,
Confidence: w.Confidence // double, no conversion needed
)).ToList();
}
}
using IronOcr;
using System.Collections.Generic;
public record WordAnnotation(string Text, int X, int Y, int Width, int Height, double Confidence);
public class WordLevelExtractor
{
private readonly IronTesseract _ocr;
public WordLevelExtractor()
{
_ocr = new IronTesseract();
}
public List<WordAnnotation> ExtractWordAnnotations(string imagePath)
{
var result = _ocr.Read(imagePath);
// Words are a flat collection — no hierarchy traversal, no symbol concatenation
return result.Words.Select(w => new WordAnnotation(
Text: w.Text, // direct string property
X: w.X,
Y: w.Y,
Width: w.Width,
Height: w.Height,
Confidence: w.Confidence // double, no conversion needed
)).ToList();
}
}
Imports IronOcr
Imports System.Collections.Generic
Public Class WordAnnotation
Public Property Text As String
Public Property X As Integer
Public Property Y As Integer
Public Property Width As Integer
Public Property Height As Integer
Public Property Confidence As Double
Public Sub New(text As String, x As Integer, y As Integer, width As Integer, height As Integer, confidence As Double)
Me.Text = text
Me.X = x
Me.Y = y
Me.Width = width
Me.Height = height
Me.Confidence = confidence
End Sub
End Class
Public Class WordLevelExtractor
Private ReadOnly _ocr As IronTesseract
Public Sub New()
_ocr = New IronTesseract()
End Sub
Public Function ExtractWordAnnotations(imagePath As String) As List(Of WordAnnotation)
Dim result = _ocr.Read(imagePath)
' Words are a flat collection — no hierarchy traversal, no symbol concatenation
Return result.Words.Select(Function(w) New WordAnnotation(
Text:=w.Text, ' direct string property
X:=w.X,
Y:=w.Y,
Width:=w.Width,
Height:=w.Height,
Confidence:=w.Confidence ' double, no conversion needed
)).ToList()
End Function
End Class
Die Symbolverkettungsschleife in der Google Cloud Vision-Version ist keine Designentscheidung – sie ist durch das Protobuf-Schema erforderlich. Word.Text ist keine Eigenschaft im Antwortobjekt. Jedes Team, das die API auf Wortebene verwendet, schreibt eine äquivalente Schleife. Die OcrResult.Words-Sammlung von IronOCR macht Text, X, Y, Width, Height und Confidence als erstklassige Eigenschaften zugänglich. Der Leitfaden zu den Leseergebnissen dokumentiert den vollständigen Satz an Eigenschaften, die auf jeder Granularitätsebene verfügbar sind.
Verarbeitung mehrseitiger TIFF-Dateien zu durchsuchbarer PDF-Ausgabe
Google Cloud Vision behandelt mehrseitige TIFF-Dateien als eine sequentielle Reihe von Bildern, für die jeweils ein separater API-Aufruf erforderlich ist. Es gibt keinen einzelnen API-Aufruf, der ein TIFF-Format akzeptiert und eine strukturierte Ausgabe für alle Frames zurückgibt. Um aus den Ergebnissen von Google Cloud Visionein durchsuchbares PDF zu erstellen, ist eine separate PDF-Generierungsbibliothek erforderlich – die API gibt nur Text zurück.
Google Cloud Vision Ansatz:
using Google.Cloud.Vision.V1;
using System.Collections.Generic;
using System.Drawing; // for TIFF frame extraction
using System.Drawing.Imaging;
public class TiffProcessingService
{
private readonly ImageAnnotatorClient _client;
public TiffProcessingService()
{
_client = ImageAnnotatorClient.Create();
}
public List<string> ProcessMultiPageTiff(string tiffPath)
{
var pageTexts = new List<string>();
// Load TIFF and extract frames manually using System.Drawing
using var tiff = System.Drawing.Image.FromFile(tiffPath);
var frameDimension = new FrameDimension(tiff.FrameDimensionsList[0]);
int frameCount = tiff.GetFrameCount(frameDimension);
for (int i = 0; i < frameCount; i++)
{
tiff.SelectActiveFrame(frameDimension, i);
// Save each frame to a temp file — Vision API does not accept TIFF frames directly
var tempPath = Path.Combine(Path.GetTempPath(), $"tiff-frame-{i}.jpg");
tiff.Save(tempPath, ImageFormat.Jpeg);
// One API call per frame — each call = one unit of quota
var visionImage = Google.Cloud.Vision.V1.Image.FromFile(tempPath);
var response = _client.DetectText(visionImage);
pageTexts.Add(response.FirstOrDefault()?.Description ?? string.Empty);
File.Delete(tempPath);
}
// Producing a searchable PDF requires a separate library (e.g., iTextSharp)
// Google Cloud Visionhas no PDF output capability
return pageTexts;
}
}
using Google.Cloud.Vision.V1;
using System.Collections.Generic;
using System.Drawing; // for TIFF frame extraction
using System.Drawing.Imaging;
public class TiffProcessingService
{
private readonly ImageAnnotatorClient _client;
public TiffProcessingService()
{
_client = ImageAnnotatorClient.Create();
}
public List<string> ProcessMultiPageTiff(string tiffPath)
{
var pageTexts = new List<string>();
// Load TIFF and extract frames manually using System.Drawing
using var tiff = System.Drawing.Image.FromFile(tiffPath);
var frameDimension = new FrameDimension(tiff.FrameDimensionsList[0]);
int frameCount = tiff.GetFrameCount(frameDimension);
for (int i = 0; i < frameCount; i++)
{
tiff.SelectActiveFrame(frameDimension, i);
// Save each frame to a temp file — Vision API does not accept TIFF frames directly
var tempPath = Path.Combine(Path.GetTempPath(), $"tiff-frame-{i}.jpg");
tiff.Save(tempPath, ImageFormat.Jpeg);
// One API call per frame — each call = one unit of quota
var visionImage = Google.Cloud.Vision.V1.Image.FromFile(tempPath);
var response = _client.DetectText(visionImage);
pageTexts.Add(response.FirstOrDefault()?.Description ?? string.Empty);
File.Delete(tempPath);
}
// Producing a searchable PDF requires a separate library (e.g., iTextSharp)
// Google Cloud Visionhas no PDF output capability
return pageTexts;
}
}
Imports Google.Cloud.Vision.V1
Imports System.Collections.Generic
Imports System.Drawing ' for TIFF frame extraction
Imports System.Drawing.Imaging
Imports System.IO
Public Class TiffProcessingService
Private ReadOnly _client As ImageAnnotatorClient
Public Sub New()
_client = ImageAnnotatorClient.Create()
End Sub
Public Function ProcessMultiPageTiff(tiffPath As String) As List(Of String)
Dim pageTexts As New List(Of String)()
' Load TIFF and extract frames manually using System.Drawing
Using tiff As System.Drawing.Image = System.Drawing.Image.FromFile(tiffPath)
Dim frameDimension As New FrameDimension(tiff.FrameDimensionsList(0))
Dim frameCount As Integer = tiff.GetFrameCount(frameDimension)
For i As Integer = 0 To frameCount - 1
tiff.SelectActiveFrame(frameDimension, i)
' Save each frame to a temp file — Vision API does not accept TIFF frames directly
Dim tempPath As String = Path.Combine(Path.GetTempPath(), $"tiff-frame-{i}.jpg")
tiff.Save(tempPath, ImageFormat.Jpeg)
' One API call per frame — each call = one unit of quota
Dim visionImage As Google.Cloud.Vision.V1.Image = Google.Cloud.Vision.V1.Image.FromFile(tempPath)
Dim response = _client.DetectText(visionImage)
pageTexts.Add(If(response.FirstOrDefault()?.Description, String.Empty))
File.Delete(tempPath)
Next
End Using
' Producing a searchable PDF requires a separate library (e.g., iTextSharp)
' Google Cloud Vision has no PDF output capability
Return pageTexts
End Function
End Class
IronOCR Ansatz:
using IronOcr;
public class TiffProcessingService
{
private readonly IronTesseract _ocr;
public TiffProcessingService()
{
_ocr = new IronTesseract();
}
public string ProcessMultiPageTiff(string tiffPath)
{
using var input = new OcrInput();
// LoadImageFrames handles all TIFF frames in one call — no temp files, no frame loop
input.LoadImageFrames(tiffPath);
var result = _ocr.Read(input);
return result.Text;
}
public void ProcessMultiPageTiffToSearchablePdf(string tiffPath, string outputPdfPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath);
var result = _ocr.Read(input);
// Google Cloud Visionhas no equivalent — this single call produces a searchable PDF
result.SaveAsSearchablePdf(outputPdfPath);
}
public void ProcessLowQualityTiff(string tiffPath, string outputPdfPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath);
// Preprocessing before OCR improves accuracy on degraded scans
input.Deskew();
input.DeNoise();
input.Contrast();
var result = _ocr.Read(input);
result.SaveAsSearchablePdf(outputPdfPath);
}
}
using IronOcr;
public class TiffProcessingService
{
private readonly IronTesseract _ocr;
public TiffProcessingService()
{
_ocr = new IronTesseract();
}
public string ProcessMultiPageTiff(string tiffPath)
{
using var input = new OcrInput();
// LoadImageFrames handles all TIFF frames in one call — no temp files, no frame loop
input.LoadImageFrames(tiffPath);
var result = _ocr.Read(input);
return result.Text;
}
public void ProcessMultiPageTiffToSearchablePdf(string tiffPath, string outputPdfPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath);
var result = _ocr.Read(input);
// Google Cloud Visionhas no equivalent — this single call produces a searchable PDF
result.SaveAsSearchablePdf(outputPdfPath);
}
public void ProcessLowQualityTiff(string tiffPath, string outputPdfPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath);
// Preprocessing before OCR improves accuracy on degraded scans
input.Deskew();
input.DeNoise();
input.Contrast();
var result = _ocr.Read(input);
result.SaveAsSearchablePdf(outputPdfPath);
}
}
Imports IronOcr
Public Class TiffProcessingService
Private ReadOnly _ocr As IronTesseract
Public Sub New()
_ocr = New IronTesseract()
End Sub
Public Function ProcessMultiPageTiff(tiffPath As String) As String
Using input As New OcrInput()
' LoadImageFrames handles all TIFF frames in one call — no temp files, no frame loop
input.LoadImageFrames(tiffPath)
Dim result = _ocr.Read(input)
Return result.Text
End Using
End Function
Public Sub ProcessMultiPageTiffToSearchablePdf(tiffPath As String, outputPdfPath As String)
Using input As New OcrInput()
input.LoadImageFrames(tiffPath)
Dim result = _ocr.Read(input)
' Google Cloud Visionhas no equivalent — this single call produces a searchable PDF
result.SaveAsSearchablePdf(outputPdfPath)
End Using
End Sub
Public Sub ProcessLowQualityTiff(tiffPath As String, outputPdfPath As String)
Using input As New OcrInput()
input.LoadImageFrames(tiffPath)
' Preprocessing before OCR improves accuracy on degraded scans
input.Deskew()
input.DeNoise()
input.Contrast()
Dim result = _ocr.Read(input)
result.SaveAsSearchablePdf(outputPdfPath)
End Using
End Sub
End Class
Die Google Cloud Vision-Version benötigt System.Drawing für die Frame-Extraktion, temporäre JPEG-Dateien, die auf die Festplatte geschrieben werden, einen API-Aufruf pro Frame (wodurch das Kontingent proportional zur Frame-Anzahl verbraucht wird) und eine separate PDF-Bibliothek, um Ausgaben zu erzeugen, die über reinen Text hinausgehen.IronOCR verarbeitet mehrseitige TIFFs nativ über LoadImageFrames, bearbeitet alle Rahmen in einem einzigen Read-Aufruf und erzeugt durch SaveAsSearchablePdf ausgebbare PDF-Ausgaben. Für gescannte archivierte TIFF-Dokumente behebt die Vorverarbeitungspipeline in ProcessLowQualityTiff die häufigsten Qualitätsprobleme in einem einzigen Durchgang. Die vollständige API finden Sie unter TIFF- und GIF-Eingabe sowie durchsuchbarer PDF-Ausgabe .
Ratenbegrenzte Batch-Migration mit Fortschrittsverfolgung
Bei Produktionsumgebungen erfordert das Standardkontingent von 1.800 Anfragen pro Minute von Google Cloud Visionentweder eine Drosselung oder eine Wiederholungslogik mit exponentiellem Backoff. Die Bearbeitung von 5.000 Dokumenten in einem einzigen nächtlichen Auftrag wird das Kontingent um ein Vielfaches überschreiten. Bei jeder Überschreitung des Kontingents wird die Pipeline für eine obligatorische Wartezeit von 60 Sekunden blockiert.IronOCR hat keine Ratenbegrenzung – die Pipeline ist nur durch die CPU-Kerne und die verfügbaren Threads begrenzt.
Google Cloud Vision Ansatz:
using Google.Cloud.Vision.V1;
using Grpc.Core;
using System.Collections.Generic;
public class ThrottledBatchProcessor
{
private readonly ImageAnnotatorClient _client;
private const int MaxRequestsPerMinute = 1800;
private const int RetryDelayMs = 60_000;
public ThrottledBatchProcessor()
{
_client = ImageAnnotatorClient.Create();
}
public async Task<Dictionary<string, string>> ProcessWithThrottlingAsync(
string[] imagePaths,
IProgress<(int completed, int total)> progress)
{
var results = new Dictionary<string, string>();
int completed = 0;
foreach (var path in imagePaths)
{
bool succeeded = false;
while (!succeeded)
{
try
{
var image = Google.Cloud.Vision.V1.Image.FromFile(path);
var response = _client.DetectText(image);
results[path] = response.FirstOrDefault()?.Description ?? string.Empty;
succeeded = true;
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.ResourceExhausted)
{
// Rate limit exceeded — wait and retry
await Task.Delay(RetryDelayMs);
}
}
progress.Report((++completed, imagePaths.Length));
}
return results;
}
}
using Google.Cloud.Vision.V1;
using Grpc.Core;
using System.Collections.Generic;
public class ThrottledBatchProcessor
{
private readonly ImageAnnotatorClient _client;
private const int MaxRequestsPerMinute = 1800;
private const int RetryDelayMs = 60_000;
public ThrottledBatchProcessor()
{
_client = ImageAnnotatorClient.Create();
}
public async Task<Dictionary<string, string>> ProcessWithThrottlingAsync(
string[] imagePaths,
IProgress<(int completed, int total)> progress)
{
var results = new Dictionary<string, string>();
int completed = 0;
foreach (var path in imagePaths)
{
bool succeeded = false;
while (!succeeded)
{
try
{
var image = Google.Cloud.Vision.V1.Image.FromFile(path);
var response = _client.DetectText(image);
results[path] = response.FirstOrDefault()?.Description ?? string.Empty;
succeeded = true;
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.ResourceExhausted)
{
// Rate limit exceeded — wait and retry
await Task.Delay(RetryDelayMs);
}
}
progress.Report((++completed, imagePaths.Length));
}
return results;
}
}
Imports Google.Cloud.Vision.V1
Imports Grpc.Core
Imports System.Collections.Generic
Imports System.Threading.Tasks
Public Class ThrottledBatchProcessor
Private ReadOnly _client As ImageAnnotatorClient
Private Const MaxRequestsPerMinute As Integer = 1800
Private Const RetryDelayMs As Integer = 60000
Public Sub New()
_client = ImageAnnotatorClient.Create()
End Sub
Public Async Function ProcessWithThrottlingAsync(
imagePaths As String(),
progress As IProgress(Of (completed As Integer, total As Integer))) As Task(Of Dictionary(Of String, String))
Dim results As New Dictionary(Of String, String)()
Dim completed As Integer = 0
For Each path In imagePaths
Dim succeeded As Boolean = False
While Not succeeded
Try
Dim image = Google.Cloud.Vision.V1.Image.FromFile(path)
Dim response = _client.DetectText(image)
results(path) = If(response.FirstOrDefault()?.Description, String.Empty)
succeeded = True
Catch ex As RpcException When ex.StatusCode = StatusCode.ResourceExhausted
' Rate limit exceeded — wait and retry
Await Task.Delay(RetryDelayMs)
End Try
End While
progress.Report((Threading.Interlocked.Increment(completed), imagePaths.Length))
Next
Return results
End Function
End Class
IronOCR Ansatz:
using IronOcr;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public class BatchProcessor
{
public Dictionary<string, string> ProcessBatch(
string[] imagePaths,
IProgress<(int completed, int total)> progress)
{
var results = new ConcurrentDictionary<string, string>();
int completed = 0;
// Keine Ratenbegrenzungen — full parallelism, no retry logic needed
Parallel.ForEach(imagePaths, imagePath =>
{
var ocr = new IronTesseract();
results[imagePath] = ocr.Read(imagePath).Text;
progress.Report((Interlocked.Increment(ref completed), imagePaths.Length));
});
return new Dictionary<string, string>(results);
}
public void ProcessBatchToSearchablePdfs(
string[] imagePaths,
string outputDirectory)
{
Parallel.ForEach(imagePaths, imagePath =>
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
var outputPath = Path.Combine(
outputDirectory,
Path.GetFileNameWithoutExtension(imagePath) + "-searchable.pdf");
result.SaveAsSearchablePdf(outputPath);
});
}
}
using IronOcr;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public class BatchProcessor
{
public Dictionary<string, string> ProcessBatch(
string[] imagePaths,
IProgress<(int completed, int total)> progress)
{
var results = new ConcurrentDictionary<string, string>();
int completed = 0;
// Keine Ratenbegrenzungen — full parallelism, no retry logic needed
Parallel.ForEach(imagePaths, imagePath =>
{
var ocr = new IronTesseract();
results[imagePath] = ocr.Read(imagePath).Text;
progress.Report((Interlocked.Increment(ref completed), imagePaths.Length));
});
return new Dictionary<string, string>(results);
}
public void ProcessBatchToSearchablePdfs(
string[] imagePaths,
string outputDirectory)
{
Parallel.ForEach(imagePaths, imagePath =>
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
var outputPath = Path.Combine(
outputDirectory,
Path.GetFileNameWithoutExtension(imagePath) + "-searchable.pdf");
result.SaveAsSearchablePdf(outputPath);
});
}
}
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks
Imports System.IO
Public Class BatchProcessor
Public Function ProcessBatch(imagePaths As String(), progress As IProgress(Of (completed As Integer, total As Integer))) As Dictionary(Of String, String)
Dim results = New ConcurrentDictionary(Of String, String)()
Dim completed As Integer = 0
' Keine Ratenbegrenzungen — full parallelism, no retry logic needed
Parallel.ForEach(imagePaths, Sub(imagePath)
Dim ocr = New IronTesseract()
results(imagePath) = ocr.Read(imagePath).Text
progress.Report((Interlocked.Increment(completed), imagePaths.Length))
End Sub)
Return New Dictionary(Of String, String)(results)
End Function
Public Sub ProcessBatchToSearchablePdfs(imagePaths As String(), outputDirectory As String)
Parallel.ForEach(imagePaths, Sub(imagePath)
Dim ocr = New IronTesseract()
Dim result = ocr.Read(imagePath)
Dim outputPath = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(imagePath) & "-searchable.pdf")
result.SaveAsSearchablePdf(outputPath)
End Sub)
End Sub
End Class
Die Google Cloud Vision-Version ist sequenziell, da parallele Anfragen die Belastung durch das Ratenlimit vervielfachen würden. Jede ResourceExhausted-Ausnahme verursacht eine vollständige 60-Sekunden-Verzögerung. Ein Stapel von 5.000 Dokumenten, der das Kontingent 10 Mal überschreitet, führt zu 10 Minuten Leerlaufzeit. Die Version von IronOCR parallelisiert auf allen verfügbaren Kernen ohne Wartezeiten. Für lang laufende Batches bietet die Fortschritts-Tracking-API eingebaute Fortschrittsrückrufe, ohne dass eine manuelle Interlocked.Increment-Verkabelung erforderlich ist. Für Probleme mit der Bildqualität bei Batch-Scans behandelt die Bildqualitätskorrekturanleitung die Vorverarbeitungspipeline, die vor jedem Read-Aufruf hinzugefügt werden kann.
Google Cloud VisionOCRAPI zu IronOCR-Zuordnungsreferenz
| Google Cloud Vision | IronOCR | Notizen |
|---|---|---|
ImageAnnotatorClient.Create() |
new IronTesseract() |
Clientinitialisierung; keine Anmeldedatei erforderlich |
Image.FromFile(path) |
_ocr.Read(path) oder input.LoadImage(path) |
Direktes Pfadlesen verfügbar auf IronTesseract |
_client.DetectText(image) |
_ocr.Read(path).Text |
TEXT_DETECTION-Äquivalent |
_client.DetectDocumentText(image) |
_ocr.Read(path) |
DOCUMENT_TEXT_DETECTION-Äquivalent; modus ist automatisch |
response[0].Description |
result.Text |
Vollständiger Dokumenttext |
TextAnnotation |
OcrResult |
Ergebniscontainer der obersten Ebene |
annotation.Text |
result.Text |
Vollständiger Text |
annotation.Pages[i] |
result.Pages[i] |
Seitenweiser Zugriff |
page.Blocks[i].Paragraphs[j] |
result.Paragraphs[i] |
IronOCR stellt Absätze als eine flache Sammlung dar. |
paragraph.Words.SelectMany(w => w.Symbols).Select(s => s.Text) |
paragraph.Text |
Direkte Zeichenketteneigenschaft; keine Symbol-Iteration |
word.BoundingBox.Vertices |
word.X, word.Y, word.Width, word.Height |
Diskrete int-Eigenschaften anstelle einer Knotenliste |
word.Confidence |
word.Confidence |
Konfidenzwert pro Wort |
page.Confidence |
result.Confidence |
Gesamtvertrauen in das Ergebnis |
Feature.Types.Type.DocumentTextDetection |
Automatisch | IronOCR wählt automatisch den Verarbeitungsmodus aus |
BatchAnnotateImagesRequest |
Parallel.ForEach + new IronTesseract() pro Thread |
Parallele lokale Verarbeitung; keine Begrenzung der Losgröße |
_client.BatchAnnotateImages(requests) |
new IronTesseract().Read(input) mit Multi-Image OcrInput |
Ein einziger Aufruf für die Eingabe mehrerer Bilder |
AsyncBatchAnnotateFilesAsync() |
input.LoadPdf(); _ocr.Read(input) |
Die PDF-Verarbeitung erfolgt synchron; Keine GCS erforderlich |
StorageClient.Create() |
Nicht erforderlich | Keine GCS-Abhängigkeit |
storageClient.UploadObjectAsync() |
Nicht erforderlich | PDFs werden direkt vom lokalen Pfad oder Stream geladen. |
operation.PollUntilCompletedAsync() |
Nicht erforderlich | Die Verarbeitung erfolgt synchron. |
RpcException (StatusCode.ResourceExhausted) |
Nicht zutreffend | Keine Ratenbegrenzungen |
RpcException (StatusCode.PermissionDenied) |
Nicht zutreffend | Keine Laufzeitauthentifizierung |
GOOGLE_APPLICATION_CREDENTIALS Umgebungsvariable |
IronOcr.License.LicenseKey |
Zeichenkettenzuweisung, nicht Dateipfad |
Gängige Migrationsprobleme und Lösungen
Problem 1: Konstruktor löst eine Ausnahme aus, wenn GOOGLE_APPLICATION_CREDENTIALS nicht vorhanden sind
Google Cloud Vision: ImageAnnotatorClient.Create() wirft RpcException mit StatusCode.Unauthenticated oder StatusCode.PermissionDenied, wenn die Umgebungsvariable nicht gesetzt ist oder auf eine ungültige Datei zeigt. Dieser Fehler tritt beim Start auf und nicht beim ersten API-Aufruf. Das bedeutet, dass die gesamte Anwendung nicht initialisiert wird, wenn die Anmeldeinformationen in einer Umgebung fehlen.
Lösung: Entfernen Sie nach dem Entfernen der Google Cloud Vision-Pakete alle Verweise auf GOOGLE_APPLICATION_CREDENTIALS aus Ihren Umgebungskonfigurationen, CI/CD-Pipeline-Geheimnissen, Kubernetes-Geheimnissen und Docker Compose-Dateien. Ersetzen Sie durch eine einzelne IRONOCR_LICENSE-Umgebungsvariable:
// Remove this from every deployment environment:
// GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
// Add this once at application startup:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
?? throw new InvalidOperationException("IRONOCR_LICENSE environment variable is required.");
// Remove this from every deployment environment:
// GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
// Add this once at application startup:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
?? throw new InvalidOperationException("IRONOCR_LICENSE environment variable is required.");
' Remove this from every deployment environment:
' GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
' Add this once at application startup:
IronOcr.License.LicenseKey = If(Environment.GetEnvironmentVariable("IRONOCR_LICENSE"), Throw New InvalidOperationException("IRONOCR_LICENSE environment variable is required."))
Problem 2: Protobuf-Symbolverkettungscode funktioniert nach Entfernung des Namespace nicht mehr.
Google Cloud Vision: Jede Stelle in Ihrem Code, an der Absatz- oder Worttexte mit .SelectMany(w => w.Symbols).Select(s => s.Text) extrahiert wurden, führt nach dem Entfernen des Google.Cloud.Vision.V1-Namensraums zu Kompilierungsfehlern. Diese Aufrufe werden auf die jeweiligen Hilfs- oder Serviceklassen verteilt, die die API-Antwort verarbeiten.
Lösung: Suchen Sie nach allen SelectMany und w.Symbols-Mustern in Ihrem Code und ersetzen Sie sie durch direkten Eigenschaftszugriff auf IronOCR-Ergebnisobjekte. Der Lesen von Ergebnissen How-to-Leitfaden behandelt alle verfügbaren Eigenschaften auf OcrResult, OcrResult.Page, OcrResult.Paragraph, OcrResult.Line und OcrResult.Word:
# Find all Protobuf symbol concatenation patterns
grep -rn "\.Symbols\." --include="*.cs" .
grep -rn "SelectMany.*Symbols" --include="*.cs" .
grep -rn "w\.Symbols\.Select" --include="*.cs" .
# Find all Protobuf symbol concatenation patterns
grep -rn "\.Symbols\." --include="*.cs" .
grep -rn "SelectMany.*Symbols" --include="*.cs" .
grep -rn "w\.Symbols\.Select" --include="*.cs" .
Ersetzen Sie jedes Vorkommen:
// Before: symbol concatenation required by Protobuf schema
var text = string.Join("", paragraph.Words.SelectMany(w => w.Symbols).Select(s => s.Text));
// After: direct property on OcrResult.Paragraph
var text = paragraph.Text;
// Before: symbol concatenation required by Protobuf schema
var text = string.Join("", paragraph.Words.SelectMany(w => w.Symbols).Select(s => s.Text));
// After: direct property on OcrResult.Paragraph
var text = paragraph.Text;
' Before: symbol concatenation required by Protobuf schema
Dim text = String.Join("", paragraph.Words.SelectMany(Function(w) w.Symbols).Select(Function(s) s.Text))
' After: direct property on OcrResult.Paragraph
Dim text = paragraph.Text
Problem 3: PDF-Verarbeitungscode lässt sich nach Entfernung von Storage.V1 nicht kompilieren.
Google Cloud Vision: Nach dem Entfernen von Google.Cloud.Storage.V1 werden alle Codes, die StorageClient, UploadObjectAsync, DeleteObjectAsync, AsyncAnnotateFileRequest, GcsSource, GcsDestination und PollUntilCompletedAsync referenzieren, nicht mehr kompiliert. Dieser Code kann sich über mehrere Dienstklassen erstrecken und stellt typischerweise den größten einzelnen Block an Änderungen dar.
Lösung: Löschen Sie die gesamte GCS-Pipeline. Ersetzen Sie die über 50 Zeilen lange asynchrone Methode durch das dreizeilige Äquivalent von IronOCR. Für Code, der ein asynchrones Signatur aufrechterhielt, um die Kompatibilität mit dem Aufrufer zu gewährleisten, umwickeln Sie mit Task.Run:
// Delete: StorageClient, GCS upload, AsyncBatchAnnotateFilesAsync,
// PollUntilCompletedAsync, output download, DeleteObjectAsync
// Replace with:
public async Task<string> ProcessPdfAsync(string pdfPath)
{
return await Task.Run(() =>
{
using var input = new OcrInput();
input.LoadPdf(pdfPath);
return new IronTesseract().Read(input).Text;
});
}
// Delete: StorageClient, GCS upload, AsyncBatchAnnotateFilesAsync,
// PollUntilCompletedAsync, output download, DeleteObjectAsync
// Replace with:
public async Task<string> ProcessPdfAsync(string pdfPath)
{
return await Task.Run(() =>
{
using var input = new OcrInput();
input.LoadPdf(pdfPath);
return new IronTesseract().Read(input).Text;
});
}
Imports System.Threading.Tasks
Public Async Function ProcessPdfAsync(pdfPath As String) As Task(Of String)
Return Await Task.Run(Function()
Using input As New OcrInput()
input.LoadPdf(pdfPath)
Return New IronTesseract().Read(input).Text
End Using
End Function)
End Function
Für neue Code verwenden Sie stattdessen die native asynchrone OCR-Unterstützung anstelle des Task.Run-Wrappers. Die PDF-Eingabeanleitung behandelt die Auswahl von Seitenbereichen und das passwortgeschützte Laden von PDFs.
Problem 4: Die Logik für Wiederholungsversuche mit Ratenbegrenzung ist nicht mehr erforderlich.
Google Cloud Vision: Jeder Code, der RpcException mit StatusCode.ResourceExhausted abfängt und ein Warten-und-Wiederholen-Muster implementiert, wurde zur Behandlung des 1.800-Anfragen-pro-Minute-Kontingents geschrieben. Diese Wiederholungslogik kann in Middleware, Pipeline-Schritten oder Batchverarbeitungsschleifen eingebettet sein.
Lösung: Entfernen Sie die gesamte Wiederholungslogik im Zusammenhang mit Quotenfehlern.IronOCR verarbeitet die Prozesse lokal ohne externes Kontingent. Der Fehlerbehandlungsvertrag ändert sich von fünf RpcException-Fällen auf zwei:
// Remove: all RpcException handlers for ResourceExhausted, PermissionDenied,
// Unavailable, DeadlineExceeded, Unauthenticated
//IronOCR error surface:
try
{
var result = new IronTesseract().Read(imagePath);
if (result.Confidence < 50)
input.DeNoise(); // add preprocessing for low-confidence results
return result.Text;
}
catch (IOException ex)
{
// File not found or locked
throw new InvalidOperationException($"Cannot read: {imagePath}", ex);
}
catch (IronOcr.Exceptions.OcrException ex)
{
// Processing failure — not a transient network error
throw new InvalidOperationException($"OCR failed: {ex.Message}", ex);
}
// Remove: all RpcException handlers for ResourceExhausted, PermissionDenied,
// Unavailable, DeadlineExceeded, Unauthenticated
//IronOCR error surface:
try
{
var result = new IronTesseract().Read(imagePath);
if (result.Confidence < 50)
input.DeNoise(); // add preprocessing for low-confidence results
return result.Text;
}
catch (IOException ex)
{
// File not found or locked
throw new InvalidOperationException($"Cannot read: {imagePath}", ex);
}
catch (IronOcr.Exceptions.OcrException ex)
{
// Processing failure — not a transient network error
throw new InvalidOperationException($"OCR failed: {ex.Message}", ex);
}
Imports IronOcr
Imports System.IO
' Remove: all RpcException handlers for ResourceExhausted, PermissionDenied,
' Unavailable, DeadlineExceeded, Unauthenticated
'IronOCR error surface:
Try
Dim result = New IronTesseract().Read(imagePath)
If result.Confidence < 50 Then
input.DeNoise() ' add preprocessing for low-confidence results
End If
Return result.Text
Catch ex As IOException
' File not found or locked
Throw New InvalidOperationException($"Cannot read: {imagePath}", ex)
Catch ex As IronOcr.Exceptions.OcrException
' Processing failure — not a transient network error
Throw New InvalidOperationException($"OCR failed: {ex.Message}", ex)
End Try
Problem 5: Mehrseitiges TIFF erfordert Frame-Extraktionsschleife
Google Cloud Vision: Der vorhandene TIFF-Verarbeitungscode extrahiert wahrscheinlich Rahmen mit System.Drawing.Image, speichert jeden Rahmen als JPEG in einem temporären Verzeichnis, reicht jedes JPEG als separaten API-Aufruf ein und löscht anschließend die temporären Dateien. Dieses Muster verbraucht eine Kontingenteinheit pro Frame und kann beim Absturz verwaiste temporäre Dateien hinterlassen.
Lösung: Ersetzen Sie die Rahmenentnahme-Schleife und die temporäre Dateiverwaltung mit input.LoadImageFrames(). Die gesamte System.Drawing-Frame-Schleife wurde gelöscht:
// Remove: System.Drawing frame extraction, temp file writes, per-frame API calls
// Replace with:
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // all frames, no temp files
var result = new IronTesseract().Read(input);
// Remove: System.Drawing frame extraction, temp file writes, per-frame API calls
// Replace with:
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // all frames, no temp files
var result = new IronTesseract().Read(input);
Imports IronOcr
Dim input As New OcrInput()
input.LoadImageFrames(tiffPath) ' all frames, no temp files
Dim result = (New IronTesseract()).Read(input)
Informationen zu den Mehrbildverarbeitungsoptionen, einschließlich der Auswahl des Bildbereichs, finden Sie im Leitfaden zur TIFF- und GIF-Eingabe .
Problem 6: Fehler bei der Berechnung von Eckpunkten in BoundingPoly
Google Cloud Vision: Code, der Begrenzungsrahmenkoordinaten aus annotation.BoundingPoly.Vertices las, berechnete X, Y, Breite und Höhe aus Vertex-Indexarithmetik: vertices[0].X für X, vertices[1].X - vertices[0].X für Breite, vertices[2].Y - vertices[0].Y für Höhe. Nach der Migration haben diese Ausdrücke kein Äquivalent in IronOCR, da Vertices nicht existiert.
Lösung: Ersetzen Sie die Vertexarithmetik durch direkte int-Eigenschaften. Es ist keine Berechnung erforderlich:
// Before: vertex index arithmetic
int x = word.BoundingBox.Vertices[0].X;
int y = word.BoundingBox.Vertices[0].Y;
int width = word.BoundingBox.Vertices[1].X - word.BoundingBox.Vertices[0].X;
int height = word.BoundingBox.Vertices[2].Y - word.BoundingBox.Vertices[0].Y;
// After: direct properties
int x = word.X;
int y = word.Y;
int width = word.Width;
int height = word.Height;
// Before: vertex index arithmetic
int x = word.BoundingBox.Vertices[0].X;
int y = word.BoundingBox.Vertices[0].Y;
int width = word.BoundingBox.Vertices[1].X - word.BoundingBox.Vertices[0].X;
int height = word.BoundingBox.Vertices[2].Y - word.BoundingBox.Vertices[0].Y;
// After: direct properties
int x = word.X;
int y = word.Y;
int width = word.Width;
int height = word.Height;
' Before: vertex index arithmetic
Dim x As Integer = word.BoundingBox.Vertices(0).X
Dim y As Integer = word.BoundingBox.Vertices(0).Y
Dim width As Integer = word.BoundingBox.Vertices(1).X - word.BoundingBox.Vertices(0).X
Dim height As Integer = word.BoundingBox.Vertices(2).Y - word.BoundingBox.Vertices(0).Y
' After: direct properties
Dim x As Integer = word.X
Dim y As Integer = word.Y
Dim width As Integer = word.Width
Dim height As Integer = word.Height
Google Cloud VisionOCR-Migrations-Checkliste
Vor der Migration
Prüfen Sie den Quellcode, um alle Abhängigkeiten von Google Cloud Visionzu identifizieren, bevor Sie Änderungen vornehmen:
# Find all Google Cloud Visionnamespace imports
grep -rn "using Google.Cloud.Vision" --include="*.cs" .
grep -rn "using Google.Cloud.Storage" --include="*.cs" .
grep -rn "using Grpc.Core" --include="*.cs" .
# Find ImageAnnotatorClient usage
grep -rn "ImageAnnotatorClient" --include="*.cs" .
# Find GCS pipeline code
grep -rn "StorageClient\|UploadObjectAsync\|DeleteObjectAsync" --include="*.cs" .
grep -rn "AsyncBatchAnnotateFilesAsync\|PollUntilCompleted" --include="*.cs" .
# Find Protobuf symbol concatenation
grep -rn "\.Symbols\." --include="*.cs" .
grep -rn "SelectMany.*Symbols" --include="*.cs" .
# Find BoundingPoly vertex calculations
grep -rn "BoundingPoly\|BoundingBox\.Vertices" --include="*.cs" .
# Find rate limit retry handlers
grep -rn "ResourceExhausted\|StatusCode\." --include="*.cs" .
# Find environment variable references
grep -rn "GOOGLE_APPLICATION_CREDENTIALS" .
# Find all Google Cloud Visionnamespace imports
grep -rn "using Google.Cloud.Vision" --include="*.cs" .
grep -rn "using Google.Cloud.Storage" --include="*.cs" .
grep -rn "using Grpc.Core" --include="*.cs" .
# Find ImageAnnotatorClient usage
grep -rn "ImageAnnotatorClient" --include="*.cs" .
# Find GCS pipeline code
grep -rn "StorageClient\|UploadObjectAsync\|DeleteObjectAsync" --include="*.cs" .
grep -rn "AsyncBatchAnnotateFilesAsync\|PollUntilCompleted" --include="*.cs" .
# Find Protobuf symbol concatenation
grep -rn "\.Symbols\." --include="*.cs" .
grep -rn "SelectMany.*Symbols" --include="*.cs" .
# Find BoundingPoly vertex calculations
grep -rn "BoundingPoly\|BoundingBox\.Vertices" --include="*.cs" .
# Find rate limit retry handlers
grep -rn "ResourceExhausted\|StatusCode\." --include="*.cs" .
# Find environment variable references
grep -rn "GOOGLE_APPLICATION_CREDENTIALS" .
Vor Beginn der Arbeiten auszufüllende Inventarlisten:
- Alle für OCR-Ein-/Ausgabe erstellten GCS-Buckets auflisten – Bereinigung nach der Migration planen
- Dokumentieren Sie die E-Mail-Adresse des Dienstkontos, damit dieses nach der Migration in der GCP Console deaktiviert werden kann.
- Identifizieren Sie alle Umgebungen, in denen
GOOGLE_APPLICATION_CREDENTIALSkonfiguriert ist - Jeglicher Code, der GCP-Projekt-IDs oder Bucket-Namen aus der Konfiguration liest, muss nach der Migration entfernt werden.
Code-Migration
- Entfernen Sie das
Google.Cloud.Vision.V1-NuGet-Paket aus allen Projekten - Entfernen Sie das
Google.Cloud.Storage.V1-NuGet-Paket aus allen Projekten - Installieren Sie das
IronOcr-NuGet-Paket in allen Projekten, die OCR durchführen - Fügen Sie
IronOcr.License.LicenseKey-Initialisierung beim Anwendungsstart hinzu - Ersetzen Sie alle
using Google.Cloud.Vision.V1-Imports durchusing IronOcr - Ersetzen Sie alle
using Google.Cloud.Storage.V1undusing Grpc.Core-Imports - Ersetzen Sie
ImageAnnotatorClient.Create()durchnew IronTesseract() - Löschen Sie alle GCS-Pipeline-Methoden (
StorageClient,UploadObjectAsync, async-Anmerkung, Polling, Download, Löschung) - Ersetzen Sie
input.LoadPdf()für alle PDF-Verarbeitungspfade (Entfernen der asynchronen GCS-Orchestrierung) - Ersetzen Sie alle Protobuf-Symbolverkettungsschleifen durch direkten
.Text-Eigenschaftszugriff aufOcrResult-Objekte - Ersetzen Sie
BoundingPoly.Vertices-Indexberechnungen durchword.X,word.Y,word.Width,word.Height - Entfernen Sie alle
RpcException-Fangblocks fürResourceExhausted,PermissionDenied,Unavailable,DeadlineExceededundUnauthenticated - Ersetzen Sie die pro-Rahmen-TIFF-Schleife durch
input.LoadImageFrames() - Konvertieren Sie sequentielle Batch-Schleifen zu
Parallel.ForEachmit pro-ThreadIronTesseract-Instanzen - Entfernen Sie
GOOGLE_APPLICATION_CREDENTIALSaus allen Umgebungskonfigurationen, CI/CD-Pipelines, Docker Compose-Dateien und Kubernetes-Geheimnissen
Nach der Migration
- Stellen Sie sicher, dass keine
RpcException- oderGoogleApiException-Typen im Fehlerbehandlungscode verbleiben - Bestätigen Sie, dass
GOOGLE_APPLICATION_CREDENTIALSin allen Bereitstellungsumgebungskonfigurationen fehlt - Führen Sie die OCR-Pipeline auf demselben Satz von Beispieldokumenten aus, der auch in der Produktion verwendet wird, und vergleichen Sie die Qualität der Textausgabe.
- Testen Sie die PDF-Verarbeitung an Dokumenten, die zuvor von der asynchronen GCS-Pipeline verarbeitet wurden, und bestätigen Sie die identische Textausgabe.
- Testen Sie passwortgeschützte PDFs mit
input.LoadPdf(path, Password: "...")— vorher nicht unterstützt - Testen Sie die Verarbeitung von mehrseitigen TIFFs mithilfe von
input.LoadImageFrames()und verifizieren Sie, dass alle Rahmen verarbeitet werden - Führen Sie den Batch-Prozessor an einer repräsentativen Stichprobe aus und überprüfen Sie, ob die Ausgabequalität mit den vorherigen Ergebnissen übereinstimmt.
- Bestätigen Sie, dass
result.Confidence-Werte innerhalb des akzeptablen Bereichs für Ihr Dokumenten-Corpus liegen - Überprüfen Sie ausgebbare PDF-Ausgaben mit
result.SaveAsSearchablePdf()für Dokumente, die zuvor eine separate PDF-Bibliothek erforderten - Führen Sie die Anwendung in einer Umgebung ohne ausgehende Internetverbindung aus und überprüfen Sie, ob die OCR-Funktion korrekt funktioniert.
Wichtigste Vorteile der Migration zu IronOCR
Anmeldeschlüssel-Oberfläche auf Null Dateien reduziert. Nach der Migration gibt es keine JSON-Schlüsseldateien, keine GCS-Bucket-Konfigurationen, keine IAM-Rollen, keine Dienstkonten und keine GOOGLE_APPLICATION_CREDENTIALS-Umgebungsvariablen in Ihrer Infrastruktur. Die gesamte Anmeldeinformationsoberfläche besteht aus einer einzigen Umgebungsvariablen, die eine Lizenzschlüsselzeichenfolge enthält. Die Schlüsselrotation, die bei Google Cloud Visionein obligatorischer periodischer Vorgang war, ist kein Konzept mehr, das Anwendung findet. Für Teams, die in mehreren Regionen oder Cloud-Anbietern tätig sind, ist die Reduzierung der Komplexität der Bereitstellungskonfiguration unmittelbar spürbar.
PDF- und TIFF-Verarbeitung ohne externe Abhängigkeiten. Die asynchrone GCS-Pipeline und die System.Drawing-TIFF-Frame-Schleife werden vollständig entfernt. input.LoadPdf() und input.LoadImageFrames() sind die Ersatzvarianten — beide synchron, beide lokal, beide in drei Zeilen von Aufruf zu Ergebnis. Passwortgeschützte PDFs, die mit Google Cloud Visionnicht möglich waren, funktionieren mit einem einzigen zusätzlichen Parameter. Der PDF-OCR-Leitfaden und der TIFF-Eingabeleitfaden decken die vollständige Eingabe-API ab.
Stapelverarbeitung in CPU-Geschwindigkeit. Durch den Wegfall des Limits von 1.800 Anfragen pro Minute und der obligatorischen Wartezeit von 60 Sekunden für Wiederholungsversuche können Stapelverarbeitungsaufträge, die zuvor durch eine Geschwindigkeitsbegrenzung eingeschränkt waren, nun mit der Geschwindigkeit der verfügbaren Prozessorkerne ausgeführt werden. Eine Maschine mit 16 Kernen verarbeitet 16 Dokumente gleichzeitig ohne jegliche externe Genehmigung. Das Parallel.ForEach-Muster mit pro-Thread IronTesseract-Instanzen ist der direkte Ersatz für die gedrosselte sequentielle Schleife. Der Schnelligkeitsoptimierungs-Leitfaden behandelt Motor-Einstellungsoptionen, die den Durchsatz für spezifische Dokumenttypen tunen.
Strukturierte Daten ohne Protobuf. Jeder OcrResult macht Text, Confidence, Pages, Paragraphs, Lines, Words und Characters als typisierte .NET-Eigenschaften zugänglich, ohne Protobuf-Namensraumabhängigkeit, ohne Symbolverkettung und ohne Vertex-Arithmetik für Begrenzungsrahmen. Code, der zuvor 20-zeilige geschachtelte Schleifen erforderte, um Absatztexte zu extrahieren, reduziert sich auf result.Paragraphs.Select(p => p.Text). Für Anwendungsfälle, die eine Wortpositionierung für die Dokumentlayoutanalyse benötigen, sind word.X, word.Y, word.Width und word.Height direkt verfügbar. Die OCR-Ergebnisseite dokumentiert jede Eigenschaft im Ergebnismodell.
Integrierte Suchfunktion für PDF-Ausgabe. Google Cloud Visionliefert nur Text – für die Erstellung eines durchsuchbaren PDFs war eine separate PDF-Generierungsbibliothek erforderlich, was eine weitere NuGet Abhängigkeit, eine weitere zu erlernende API und zusätzliche Lizenzierungsanforderungen mit sich brachte. IronOCR's result.SaveAsSearchablePdf(outputPath) erzeugt ein vollständig durchsuchbares PDF aus jedem OCR-Ergebnis in einer Zeile. Für Dokumentarchivierungs-Workflows und rechtliche Entdeckungs-Pipelines entfällt damit eine ganze Abhängigkeit. Das durchsuchbare PDF-Beispiel veranschaulicht das Muster von Anfang bis Ende.
Datensouveränität für regulierte Branchen. Von IronOCR verarbeitete Dokumente verlassen niemals den Server. Bei HIPAA-geschützten Gesundheitsdatensätzen, ITAR-kontrollierten technischen Daten, CMMC-geschützten Materialien von Verteidigungsauftragnehmern, Anwaltsgeheimnis-geschützten Rechtsdokumenten und PCI-DSS-geschützten Finanzdatensätzen entfällt durch die On-Premise-Architektur die Kategorie der Drittanbieter-Datenverarbeiter vollständig aus dem Geltungsbereich der Compliance. Es gibt keine Vereinbarung mit Geschäftspartnern auszuhandeln, keine Datenschutzvereinbarung abzuschließen und keine Datenaufbewahrungsrichtlinie von Google zu überprüfen. Das IronOCR Dokumentationsportal umfasst Bereitstellungskonfigurationen für Docker-, Linux-, Azure- und AWS-Umgebungen, in denen Anforderungen an den Datenstandort gelten.
Häufig gestellte Fragen
Warum sollte ich von Google Cloud Vision API zu IronOCR migrieren?
Zu den häufigsten Gründen gehören die Beseitigung der Komplexität der COM-Interoperabilität, die Ablösung der dateibasierten Lizenzverwaltung, die Vermeidung der seitenweisen Abrechnung, die Ermöglichung der Docker-/Container-Bereitstellung und die Einführung eines NuGet-nativen Workflows, der sich in die Standard-.NET-Werkzeuge integriert.
Was sind die wichtigsten Code-Änderungen bei der Migration von Google Cloud Vision API zu IronOCR?
Ersetzen Sie die Initialisierungssequenzen von Google Cloud Vision durch die Instanziierung von IronTesseract, entfernen Sie das COM-Lebenszyklusmanagement (explizite Create/Load/Close-Muster) und aktualisieren Sie die Namen der Ergebniseigenschaften. Das Ergebnis sind deutlich weniger Boilerplate-Zeilen.
Wie installiere ich IronOCR, um die Migration zu beginnen?
Führen Sie 'Install-Package IronOcr' in der Paketmanager-Konsole oder 'dotnet add package IronOcr' in der CLI aus. Sprachpakete sind separate Pakete: 'dotnet add package IronOcr.Languages.French' für Französisch, zum Beispiel.
Kann IronOCR die OCR-Genauigkeit von Google Cloud Vision API für Standardgeschäftsdokumente erreichen?
IronOCR erzielt eine hohe Genauigkeit bei Standardgeschäftsinhalten wie Rechnungen, Verträgen, Quittungen und getippten Formularen. Bildvorverarbeitungsfilter (Schräglagenkorrektur, Rauschunterdrückung, Kontrastverbesserung) verbessern die Erkennungsgenauigkeit bei verschlechterten Eingaben weiter.
Wie geht IronOCR mit den Sprachdaten um, die die Google Cloud Vision API separat installiert?
Die Sprachdaten in IronOCR werden als NuGet-Pakete verteilt. mit 'dotnet add package IronOcr.Languages.German' wird die deutsche Unterstützung installiert. Es sind keine manuellen Dateiplatzierungen oder Verzeichnispfade erforderlich.
Erfordert die Migration von Google Cloud Vision API zu IronOCR Änderungen an der Bereitstellungsinfrastruktur?
IronOCR erfordert weniger Änderungen an der Infrastruktur als Google Cloud Vision API. Es gibt keine SDK-Binärpfade, Lizenzdateiplätze oder Lizenzserverkonfigurationen. Das NuGet-Paket enthält die komplette OCR-Engine, und der Lizenzschlüssel ist eine im Anwendungscode festgelegte Zeichenfolge.
Wie konfiguriere ich die IronOCR-Lizenzierung nach der Migration?
Weisen Sie IronOcr.License.LicenseKey = "YOUR-KEY" im Startup-Code der Anwendung zu. In Docker oder Kubernetes speichern Sie den Schlüssel als Umgebungsvariable und lesen ihn beim Starten aus. Verwenden Sie License.IsValidLicense zur Validierung, bevor Sie den Datenverkehr akzeptieren.
Kann IronOCR PDFs auf die gleiche Weise verarbeiten wie Google Cloud Vision?
Ja, IronOCR liest sowohl native als auch gescannte PDFs. Instanziieren Sie IronTesseract, rufen Sie ocr.Read(input) auf, wobei input ein PDF-Pfad oder OcrPdfInput ist, und iterieren Sie die OcrResult-Seiten. Es ist keine separate PDF-Rendering-Pipeline erforderlich.
Wie handhabt IronOCR das Threading bei der Verarbeitung großer Datenmengen?
IronTesseract kann sicher pro Thread instanziiert werden. Sie können eine Instanz pro Thread in einem Parallel.ForEach- oder Task-Pool aufsetzen, OCR gleichzeitig ausführen und jede Instanz nach Abschluss entsorgen. Es ist kein globaler Zustand oder Sperren erforderlich.
Welche Ausgabeformate unterstützt IronOCR nach der Textextraktion?
IronOCR liefert strukturierte Ergebnisse mit Text, Wortkoordinaten, Konfidenzwerten und Seitenstruktur. Zu den Exportoptionen gehören reiner Text, durchsuchbare PDF-Dateien und strukturierte Ergebnisobjekte für die nachgeschaltete Verarbeitung.
Ist die Preisgestaltung von IronOCR vorhersehbarer als die von Google Cloud Vision API für die Skalierung von Workloads?
IronOCR verwendet eine pauschale, unbefristete Lizenzierung ohne Seiten- oder Volumengebühren. Egal, ob Sie 10.000 oder 10 Millionen Seiten verarbeiten, die Lizenzkosten bleiben konstant. Optionen für Volumen- und Teamlizenzen finden Sie auf der IronOCR-Preisseite.
Was passiert mit meinen bestehenden Tests nach der Migration von Google Cloud Vision API zu IronOCR?
Tests, die extrahierte Textinhalte überprüfen, sollten auch nach der Migration bestehen. Tests, die API-Aufrufmuster oder den Lebenszyklus von COM-Objekten validieren, müssen aktualisiert werden, um das einfachere Initialisierungs- und Ergebnismodell von IronOCR widerzuspiegeln.

