Zum Fußzeileninhalt springen
VIDEOS

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
SHELL

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
$vbLabelText   $csharpLabel

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"
$vbLabelText   $csharpLabel

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."))
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

Der betriebliche Unterschied ist klar: Google Cloud Visiongeneriert zur Laufzeit fünf Kategorien von RpcExceptionPermissionDenied, 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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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."))
$vbLabelText   $csharpLabel

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" .
SHELL

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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)
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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" .
SHELL

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_CREDENTIALS konfiguriert ist
  • Jeglicher Code, der GCP-Projekt-IDs oder Bucket-Namen aus der Konfiguration liest, muss nach der Migration entfernt werden.

Code-Migration

  1. Entfernen Sie das Google.Cloud.Vision.V1-NuGet-Paket aus allen Projekten
  2. Entfernen Sie das Google.Cloud.Storage.V1-NuGet-Paket aus allen Projekten
  3. Installieren Sie das IronOcr-NuGet-Paket in allen Projekten, die OCR durchführen
  4. Fügen Sie IronOcr.License.LicenseKey-Initialisierung beim Anwendungsstart hinzu
  5. Ersetzen Sie alle using Google.Cloud.Vision.V1-Imports durch using IronOcr
  6. Ersetzen Sie alle using Google.Cloud.Storage.V1 und using Grpc.Core-Imports
  7. Ersetzen Sie ImageAnnotatorClient.Create() durch new IronTesseract()
  8. Löschen Sie alle GCS-Pipeline-Methoden (StorageClient, UploadObjectAsync, async-Anmerkung, Polling, Download, Löschung)
  9. Ersetzen Sie input.LoadPdf() für alle PDF-Verarbeitungspfade (Entfernen der asynchronen GCS-Orchestrierung)
  10. Ersetzen Sie alle Protobuf-Symbolverkettungsschleifen durch direkten .Text-Eigenschaftszugriff auf OcrResult-Objekte
  11. Ersetzen Sie BoundingPoly.Vertices-Indexberechnungen durch word.X, word.Y, word.Width, word.Height
  12. Entfernen Sie alle RpcException-Fangblocks für ResourceExhausted, PermissionDenied, Unavailable, DeadlineExceeded und Unauthenticated
  13. Ersetzen Sie die pro-Rahmen-TIFF-Schleife durch input.LoadImageFrames()
  14. Konvertieren Sie sequentielle Batch-Schleifen zu Parallel.ForEach mit pro-Thread IronTesseract-Instanzen
  15. Entfernen Sie GOOGLE_APPLICATION_CREDENTIALS aus allen Umgebungskonfigurationen, CI/CD-Pipelines, Docker Compose-Dateien und Kubernetes-Geheimnissen

Nach der Migration

  • Stellen Sie sicher, dass keine RpcException- oder GoogleApiException-Typen im Fehlerbehandlungscode verbleiben
  • Bestätigen Sie, dass GOOGLE_APPLICATION_CREDENTIALS in 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.

Hinweis:Google Cloud Vision, Tesseract und iText sind eingetragene Marken ihrer jeweiligen Besitzer. Diese Seite ist nicht mit Google oder der iText Group verbunden, genehmigt oder gesponsert. Alle Produktnamen, Logos und Marken sind Eigentum ihrer jeweiligen Inhaber. Vergleiche dienen nur zu Informationszwecken und spiegeln öffentlich zugängliche Informationen zum Zeitpunkt des Schreibens wider.

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.

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

Iron-Support-Team

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