Passer au contenu du pied de page
VIDéOS

Comment installer Tesseract OCR sur Windows en C#

Ce guide accompagne les développeurs .NET dans le remplacement de Google Cloud Visionpar IronOCR , un moteur OCR local prêt à l'emploi. Il aborde la suppression des identifiants, le remplacement de l'analyse des annotations Protobuf, la simplification du traitement par lots des annotations et le traitement des documents multipages — les quatre changements structurels qui représentent la majeure partie du travail de migration.

Pourquoi migrer depuis Google Cloud VisionOCR

La décision de migrer commence presque toujours par l'un de ces deux constats : un audit de conformité bloque la transmission de documents dans le cloud, ou la surface opérationnelle de gestion des identifiants GCP, des compartiments GCS, de l'interrogation asynchrone et de la facturation par image devient plus coûteuse que l'OCR elle-même.

Cycle de vie de la clé JSON du compte de service. Chaque déploiement de votre application (machine de développement, pipeline CI/CD, serveur de préproduction, serveur de production, conteneur Docker, pod Kubernetes) requiert le même fichier de clé JSON pour le compte de service. Ce fichier contient une clé privée RSA. Il ne doit jamais être intégré au système de contrôle de version, doit être renouvelé régulièrement, doit être protégé par des permissions de système de fichiers et doit être mis à jour simultanément dans tous les environnements lors de chaque renouvellement. Une clé compromise donne accès à l'API jusqu'à sa révocation manuelle dans la console GCP. IronOCR remplace toute cette interface opérationnelle par une seule chaîne de clés de licence définie une seule fois au démarrage de l'application.

Facturation à la demande à grande échelle. À 1,50 $ pour 1 000 images et 0,0015 $ par page PDF, les coûts sont invisibles pendant le développement et problématiques en production. Un pipeline de traitement de documents gérant 200 000 pages par mois coûte 300 $ par mois rien qu'en frais d'API, avant les frais de stockage GCS et les coûts de sortie. Ces 300 dollars se répètent chaque mois indéfiniment. La licence perpétuelle d'IronOCR transforme l'OCR, initialement considérée comme une dépense d'exploitation mesurée, en un poste de dépense fixe dont l'exécution ne coûte rien la deuxième ou la troisième année.

Pipeline asynchrone GCS pour les PDF. Google Cloud Visionn'accepte pas les PDF comme entrée directe de l'API. Le pipeline complet nécessite un deuxième package NuGet (Google.Cloud.Storage.V1), un bucket GCS provisionné, un téléchargement asynchrone, un appel AsyncBatchAnnotateFilesAsync, une boucle de sondage, une analyse de la sortie JSON depuis GCS, et une étape de nettoyage. Ce pipeline s'étend sur plus de 50 lignes de code avant que tout texte ne soit extrait. IronOCR lit un PDF en trois lignes, de manière synchrone, sans dépendances externes.

Concaténation de symboles Protobuf. La réponse DOCUMENT_TEXT_DETECTION stocke le texte au niveau du symbole dans une hiérarchie Protobuf de Pages, Blocs, Paragraphes, Mots et Symboles. La lecture du texte des paragraphes nécessite de parcourir cinq boucles imbriquées et d'appeler .SelectMany(w => w.Symbols).Select(s => s.Text). IronOCR renvoie le texte des paragraphes comme paragraph.Text — une propriété chaîne typée.

1 800 requêtes par minute de quota par défaut. Les charges de travail par lots au-dessus du quota par défaut reçoivent des réponses StatusCode.ResourceExhausted qui bloquent le pipeline pendant 60 secondes par dépassement. L'augmentation du quota nécessite une demande via la console GCP et l'approbation de Google. IronOCR traite les données localement à la vitesse des cœurs de processeur disponibles — il n'y a pas de quota à gérer, pas d'approbation à demander et aucune logique de nouvelle tentative requise pour la limitation du débit.

Aucune prise en charge hors ligne ou en mode isolé. Google Cloud Visionnécessite une connexion Internet aux points de terminaison de Google. Les réseaux isolés, les centres de données classifiés et les systèmes de contrôle industriels ne peuvent pas l'utiliser, quel que soit leur niveau de complexité architecturale. IronOCR fonctionne sans aucune connectivité réseau sortante après la validation initiale de la licence.

Le problème fondamental

Google Cloud Vision nécessite un fichier de clé JSON sur le disque avant l'exécution de la première ligne de code OCR :

// 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 démarre avec une seule chaîne de caractères et s'exécute entièrement sur la machine locale :

// 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

Comparaison des fonctionnalités IronOCR et de Google Cloud VisionOCR

Le tableau ci-dessous répertorie les fonctionnalités directement destinées aux équipes qui élaborent l'analyse de rentabilité de la migration.

Fonction Google Cloud VisionOCR IronOCR
Lieu de traitement Google Cloud (à distance) Sur place (local)
Authentification Clé JSON du compte de service + variable d'environnement chaîne de clé de licence
Entrée PDF Téléversement vers GCS + API asynchrone input.LoadPdf() direct
PDF protégé par mot de passe Non pris en charge LoadPdf(path, Password: "...")
Entrée TIFF multipage Limité input.LoadImageFrames()
Sortie PDF consultable Non disponible result.SaveAsSearchablePdf()
Accès aux données structurées Protobuf : Pages > Blocs > Paragraphes > Mots > Symboles result.Paragraphs, result.Lines, result.Words (objets .NET typés)
Propriété du texte du paragraphe Non — nécessite la concaténation de symboles paragraph.Text propriété directe
scores de confiance Par symbole (nécessite une boucle) result.Confidence, word.Confidence
prétraitement automatique des images Aucun (ML s'en charge) Redresser, réduire le bruit, contraster, binariser, accentuer
OCR basé sur la région Aucune culture indigène CropRectangle sur OcrInput
Lecture de codes-barres Fonctionnalité API distincte ocr.Configuration.ReadBarCodes = true
Limites de débit 1 800 requêtes/minute par défaut Aucun (lié au processeur)
Hors ligne / isolé dans l'air Non Oui
Coût par document 1,50 $ pour 1 000 images ; 0,0015 $/page PDF Aucun (licence perpétuelle)
Langues prises en charge ~50 125+
Autorisation FedRAMP Non autorisé Non applicable (sur place)
Parcours de conformité HIPAA Accord de partenariat commercial requis Aucun traitement de données par des tiers
Prise en charge du .NET Framework .NET Standard 2.0+ .NET Framework 4.6.2+ et .NET 5/6/7/8/9
Packages NuGet requis Google.Cloud.Vision.V1 + Google.Cloud.Storage.V1 IronOcr seulement
Modèle de tarification Facturation à la demande Perpetual ($999 Lite / $1,499 Plus / $2,999 Professional / $5,999 Unlimited)

Démarrage rapide : Migration de Google Cloud VisionOCRvers IronOCR

Étape 1 : Remplacer le package NuGet

Supprimez les packages Google Cloud :

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

Installez IronOCR depuis la page du package NuGet :

dotnet add package IronOcr

Étape 2 : Mise à jour des espaces de noms

Remplacez les espaces de noms Google Cloud par l'espace de noms 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;
// 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

Étape 3 : initialisation de la licence

Ajoutez l'initialisation de la licence une fois au démarrage de l'application, avant que toute instance de IronTesseract ne soit créée.

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

En production, lisez la clé à partir d'une variable d'environnement ou d'un gestionnaire de secrets :

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

Exemples de migration de code

Suppression de la configuration des informations d'identification du compte de service

L'initialisation du client Google Cloud Visionressemble à une simple ligne de code, mais nécessite une infrastructure préalable importante. Chaque développeur qui rejoint le projet, chaque environnement de déploiement et chaque pipeline CI/CD doivent disposer d'une configuration d'identification complète avant que le constructeur ne se termine sans générer d'erreur.

Approche de Google Cloud Vision :

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

Approche IronOCR :

using IronOcr;

// Prerequisites: set the license key once at app startup
// Non JSON files, no environment variables beyond the key, no GCP Console configuration
// Non key 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
// Non JSON files, no environment variables beyond the key, no GCP Console configuration
// Non key 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
' Non JSON files, no environment variables beyond the key, no GCP Console configuration
' Non key 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

La différence opérationnelle est concrète : Google Cloud Visiongénère cinq catégories de RpcException à l'exécution — PermissionDenied, ResourceExhausted, Unavailable, DeadlineExceeded, et Unauthenticated — chacune représentant un mode de panne d'infrastructure différent. Les modes de panne d'IronOCR sont IOException (fichier non trouvé ou verrouillé) et OcrException (échec du traitement). Consultez le guide d'installation d'IronTesseract pour les options de configuration et la page produit IronOCR pour les détails de licence.

Remplacement des requêtes d'annotation par lots par plusieurs types de fonctionnalités

Google Cloud Vision prend en charge le regroupement de plusieurs images en un seul BatchAnnotateImagesRequest, chaque image étant configurée avec une liste de types de Feature. Ce modèle est utilisé lorsqu'un appel unique doit collecter à la fois les résultats de TEXT_DETECTION et de DOCUMENT_TEXT_DETECTION, ou lors de la soumission de nombreuses images pour minimiser les surcoûts de round-trip. La réponse Protobuf nécessite de faire correspondre chaque AnnotateImageResponse à sa demande d'origine par index.

Approche de Google Cloud Vision :

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 Fonction { Type = Feature.Types.Type.TextDetection },
                new Fonction { 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 Fonction { Type = Feature.Types.Type.TextDetection },
                new Fonction { 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

Approche IronOCR :

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 exécute le traitement par lots en parallèle sur les cœurs du processeur sans aucune surcharge réseau. Il n'y a pas de plafonnement BatchSize, pas de correspondance d'index de réponse, et pas de gestion des erreurs par élément pour les échecs de réseau ou de credentials. Pour les charges de travail qui combinent plusieurs images en un seul document logique — des formulaires multi-pages scannés soumis sous forme de JPEG individuels, par exemple — BatchAsDocument charge toutes les images dans un seul OcrInput et renvoie un OcrResult unifié avec result.Pages indexé sur chaque image d'entrée. L' exemple de multithreading illustre les performances de référence pour le traitement parallèle.

Migration de l'extraction d'annotations au niveau des mots Protobuf

Les données de délimitation et de confiance au niveau des mots de Google Cloud Visionnécessitent de parcourir toute la hiérarchie Protobuf : Pages, puis Blocs, puis Paragraphes, puis Mots, puis Symboles. L'extraction de texte de mots nécessite de concaténer des Symboles de chaque Mot — l'objet Word n'a pas de propriété directe .Text. Les coordonnées de la boîte englobante sont stockées sous forme de BoundingPoly avec une liste de Vertices plutôt que sous forme de champs discrets X, Y, Largeur, Hauteur.

Approche de Google Cloud Vision :

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

Approche IronOCR :

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

La boucle de concaténation de symboles dans la version Google Cloud Visionn'est pas un choix de conception ; elle est requise par le schéma Protobuf. Word.Text n'est pas une propriété dans l'objet de réponse. Chaque équipe qui utilise l'API au niveau des mots écrit une boucle équivalente. La collection OcrResult.Words d'IronOCR expose Text, X, Y, Width, Height et Confidence en tant que propriétés de premier ordre. Le guide des résultats de lecture documente l'ensemble complet des propriétés disponibles à chaque niveau de granularité.

Traitement TIFF multipage vers un PDF interrogeable

Google Cloud Vision traite les fichiers TIFF multipages comme une série séquentielle d'images, chacune nécessitant un appel API distinct. Il n'existe pas d'appel API unique qui accepte un fichier TIFF et renvoie une sortie structurée pour toutes les images. La génération d'un PDF consultable à partir des résultats de Google Cloud Visionnécessite une bibliothèque de génération de PDF distincte — l'API ne renvoie que du texte.

Approche de Google Cloud Vision :

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

Approche IronOCR :

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

La version Google Cloud Visionnécessite System.Drawing pour l'extraction des images, des fichiers JPEG temporaires écrits sur le disque, un appel API par image (consommant un quota proportionnellement au nombre d'images) et une bibliothèque PDF distincte pour produire toute sortie autre que du texte brut. IronOCR gère les TIFF multi-cadres nativement via LoadImageFrames, traite tous les cadres en un seul appel Read, et produit une sortie PDF consultable via SaveAsSearchablePdf. Pour les documents TIFF archivés numérisés, le pipeline de prétraitement dans ProcessLowQualityTiff traite les problèmes de qualité les plus courants en une seule passe. Consultez la section relative aux entrées TIFF et GIF et à la sortie PDF consultable pour obtenir l'API complète.

Migration par lots à débit limité avec suivi de la progression

En production, le quota par défaut de 1 800 requêtes par minute de Google Cloud Visionnécessite soit une limitation du débit, soit une logique de nouvelle tentative avec un délai exponentiel. Le traitement de 5 000 documents en une seule tâche de nuit dépassera largement le quota. Chaque dépassement de quota bloque le pipeline pendant une attente obligatoire de 60 secondes. IronOCR n'a pas de limite de débit — le pipeline est uniquement limité par les cœurs du processeur et les threads disponibles.

Approche de Google Cloud Vision :

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

Approche IronOCR :

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;

        // Aucune limite de débit — 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;

        // Aucune limite de débit — 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

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 As New ConcurrentDictionary(Of String, String)()
        Dim completed As Integer = 0

        ' Aucune limite de débit — full parallelism, no retry logic needed
        Parallel.ForEach(imagePaths, Sub(imagePath)
                                         Dim ocr As 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 As 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

La version Google Cloud Visionest séquentielle car les requêtes parallèles multiplieraient l'exposition à la limite de débit. Chaque exception ResourceExhausted ajoute un blocage complet de 60 secondes. Un lot de 5 000 documents qui atteint le quota 10 fois ajoute 10 minutes d'attente inutile. La version d'IronOCR effectue le traitement en parallèle sur tous les cœurs disponibles, sans attente. Pour les lots de longue durée, l'API de suivi de progression fournit des callbacks de progression intégrés sans nécessiter de câblage Interlocked.Increment manuel. Pour les problèmes de qualité d'image dans les numérisations par lots, le guide de correction de la qualité de l'image couvre le pipeline de prétraitement qui peut être ajouté avant chaque appel Read.

Référence de mappage de l'API OCR de Google Cloud Visionvers IronOCR

Google Cloud Vision IronOCR Notes
ImageAnnotatorClient.Create() new IronTesseract() Initialisation du client ; aucun fichier d'accréditation n'est nécessaire
Image.FromFile(path) _ocr.Read(path) ou input.LoadImage(path) Lecture de chemin direct disponible sur IronTesseract
_client.DetectText(image) _ocr.Read(path).Text TEXT_DETECTION équivalent
_client.DetectDocumentText(image) _ocr.Read(path) DOCUMENT_TEXT_DETECTION équivalent; le mode est automatique
response[0].Description result.Text Texte intégral du document
TextAnnotation OcrResult Conteneur de résultats de niveau supérieur
annotation.Text result.Text Chaîne de texte complète
annotation.Pages[i] result.Pages[i] Accès par page
page.Blocks[i].Paragraphs[j] result.Paragraphs[i] IronOCR expose les paragraphes comme une collection plate
paragraph.Words.SelectMany(w => w.Symbols).Select(s => s.Text) paragraph.Text Propriété de chaîne directe ; pas d'itération de symbole
word.BoundingBox.Vertices word.X, word.Y, word.Width, word.Height Propriétés de type entier discret au lieu d'une liste de sommets
word.Confidence word.Confidence Score de confiance par mot
page.Confidence result.Confidence Confiance globale dans les résultats
Feature.Types.Type.DocumentTextDetection Automatique IronOCR sélectionne automatiquement le mode de traitement
BatchAnnotateImagesRequest Parallel.ForEach + new IronTesseract() par thread Traitement local parallèle ; pas de limite de taille de lot
_client.BatchAnnotateImages(requests) new IronTesseract().Read(input) avec OcrInput multi-image Un seul appel pour une entrée multi-images
AsyncBatchAnnotateFilesAsync() input.LoadPdf() ; _ocr.Read(entrée) Le traitement des fichiers PDF est synchrone ; Aucun GCS requis
StorageClient.Create() Pas nécessaire Aucune dépendance à GCS
storageClient.UploadObjectAsync() Pas nécessaire Les fichiers PDF se chargent directement à partir d'un chemin local ou d'un flux.
operation.PollUntilCompletedAsync() Pas nécessaire Le traitement est synchrone
RpcException (StatusCode.ResourceExhausted) Sans objet Aucune limite de débit
RpcException (StatusCode.PermissionDenied) Sans objet Aucune authentification à l'exécution
GOOGLE_APPLICATION_CREDENTIALS env var IronOcr.License.LicenseKey Affectation de chaîne de caractères, et non chemin de fichier

Problèmes de migration courants et solutions

Problème 1 : Le constructeur lève une exception sans GOOGLE_APPLICATION_CREDENTIALS

Google Cloud Vision: ImageAnnotatorClient.Create() lance RpcException avec StatusCode.Unauthenticated ou StatusCode.PermissionDenied si la variable d'environnement n'est pas définie ou pointe vers un fichier invalide. Cet échec survient au démarrage, pas lors du premier appel d'API, ce qui signifie que l'application entière échoue à s'initialiser si les identifiants manquent dans un des environnements.

Solution: Après avoir supprimé les packages de Google Cloud Vision, supprimez toutes les références à GOOGLE_APPLICATION_CREDENTIALS de vos configurations d'environnement, secrets du pipeline CI/CD, secrets Kubernetes et fichiers Docker Compose. Remplacez par une seule variable d'environnement IRONOCR_LICENSE:

// 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

Problème n° 2 : Des erreurs se produisent lors de la concaténation de symboles Protobuf après la suppression de l'espace de noms.

Google Cloud Vision: Chaque emplacement dans votre code source où le texte de paragraphe ou de mot a été extrait en utilisant .SelectMany(w => w.Symbols).Select(s => s.Text) produira des erreurs de compilation après que le namespace Google.Cloud.Vision.V1 soit supprimé. Ces appels sont répartis entre les classes d'assistance ou de service qui ont consommé la réponse de l'API.

Solution: Recherchez tous les modèles SelectMany et w.Symbols dans votre code source et remplacez-les par un accès direct aux propriétés sur les objets de résultat IronOCR. Le guide sur la lecture des résultats couvre chaque propriété disponible sur OcrResult, OcrResult.Page, OcrResult.Paragraph, OcrResult.Line et 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

Remplacez chaque occurrence :

// 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

Problème 3 : Le code de traitement PDF ne compile pas après la suppression de Storage.V1

Google Cloud Vision: Après la suppression de Google.Cloud.Storage.V1, tout code qui fait référence à StorageClient, UploadObjectAsync, DeleteObjectAsync, AsyncAnnotateFileRequest, GcsSource, GcsDestination et PollUntilCompletedAsync échouera à la compilation. Ce code peut couvrir plusieurs classes de service et représente généralement le plus grand bloc unique de modifications.

Solution : Supprimez l'intégralité du pipeline GCS. Remplacez la méthode asynchrone de Plus de 50 lignes par son équivalent IronOCR en trois lignes. Pour le code qui maintenait une signature asynchrone pour la compatibilité des appelants, enveloppez avec 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

Pour le nouveau code, utilisez le support natif de l'OCR asynchrone au lieu de l'enveloppe Task.Run. Le guide d'importation PDF couvre la sélection de la plage de pages et le chargement de PDF protégés par mot de passe.

Problème n°4 : La logique de nouvelle tentative de limitation de débit n'est plus nécessaire.

Google Cloud Vision: Tout code qui attrape RpcException avec StatusCode.ResourceExhausted et implémente un modèle d'attente et de réessai a été écrit pour gérer le quota de 1 800 requêtes par minute. Cette logique de nouvelle tentative peut être intégrée dans un middleware, des étapes de pipeline ou des boucles de traitement par lots.

Solution : Supprimer toute logique de nouvelle tentative associée aux erreurs de quota. IronOCR traite les données localement, sans quota externe. Le contrat du gestionnaire d'erreurs change de cinq cas RpcException à deux :

// 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

Problème 5 : Les fichiers TIFF multipages nécessitent une boucle d'extraction d'images

Google Cloud Vision: Le code de traitement de TIFF existant extrait probablement des cadres en utilisant System.Drawing.Image, enregistre chaque cadre sous forme de JPEG dans un répertoire temporaire, soumet chaque JPEG en tant qu'appel API distinct, puis supprime les fichiers temporaires ensuite. Ce modèle consomme une unité de quota par image et peut laisser des fichiers temporaires orphelins en cas de plantage.

Solution: Remplacez la boucle d'extraction de cadres et la gestion des fichiers temporaires par input.LoadImageFrames(). La boucle d'animation System.Drawing entière a été supprimée :

// 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

Consultez le guide d'entrée TIFF et GIF pour connaître les options de traitement multi-images, notamment la sélection de la plage d'images.

Problème 6 : Rupture des calculs de sommets de BoundingPoly

Google Cloud Vision: Le code qui lisait les coordonnées de la boîte englobante depuis annotation.BoundingPoly.Vertices calculait X, Y, Largeur et Hauteur à partir de l'arithmétique de l'index de vertex : vertices[0].X pour X, vertices[1].X - vertices[0].X pour la Largeur, vertices[2].Y - vertices[0].Y pour la Hauteur. Après la migration, ces expressions n'ont pas d'équivalent dans IronOCR parce que Vertices n'existe pas.

Solution : Remplacer l'arithmétique des sommets par des propriétés entières directes. Aucun calcul n'est nécessaire :

// 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

Liste de contrôle de migration OCR Google Cloud Vision

Pré-migration

Avant d'apporter des modifications, auditez le code source afin d'identifier toutes les dépendances à Google Cloud Vision :

# 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

Notes d'inventaire à compléter avant de commencer :

  • Lister tous les compartiments GCS créés pour l'entrée/sortie OCR — planifier le nettoyage après la migration
  • Documentez l'adresse e-mail du compte de service afin qu'elle puisse être désactivée dans la console GCP après la migration.
  • Identifiez tous les environnements où GOOGLE_APPLICATION_CREDENTIALS est configuré Notez que tout code lisant les ID de projet GCP ou les noms de compartiment à partir de la configuration doit être supprimé après la migration.

Migration de code

  1. Supprimez le package NuGet Google.Cloud.Vision.V1 de tous les projets
  2. Supprimez le package NuGet Google.Cloud.Storage.V1 de tous les projets
  3. Installez le package NuGet IronOcr dans tous les projets qui effectuent l'OCR
  4. Ajoutez l'initialisation IronOcr.License.LicenseKey au démarrage de l'application
  5. Remplacez toutes les importations using Google.Cloud.Vision.V1 par using IronOcr
  6. Remplacez toutes les importations using Google.Cloud.Storage.V1 et using Grpc.Core
  7. Remplacez ImageAnnotatorClient.Create() par new IronTesseract()
  8. Supprimez toutes les méthodes du pipeline GCS (StorageClient, UploadObjectAsync, annotation async, sondage, téléchargement, suppression)
  9. Remplacez input.LoadPdf() pour tous les chemins de traitement PDF (suppression de l'orchestration GCS asynchrone)
  10. Remplacez toutes les boucles de concaténation de symboles Protobuf par un accès direct aux propriétés .Text sur les objets OcrResult
  11. Remplacez les calculs d'index BoundingPoly.Vertices par word.X, word.Y, word.Width, word.Height
  12. Supprimez tous les blocs RpcException pour ResourceExhausted, PermissionDenied, Unavailable, DeadlineExceeded et Unauthenticated
  13. Remplacez la boucle TIFF par cadre par input.LoadImageFrames()
  14. Convertissez les boucles de lots séquentielles en Parallel.ForEach avec des instances IronTesseract par thread
  15. Supprimez GOOGLE_APPLICATION_CREDENTIALS de toutes les configurations d'environnement, des pipelines CI/CD, des fichiers Docker Compose et des secrets Kubernetes

Après la migration

  • Vérifiez qu'aucun type RpcException ou GoogleApiException ne reste référencé dans le code de gestion des erreurs
  • Confirmez que GOOGLE_APPLICATION_CREDENTIALS est absent de toutes les configurations d'environnements de déploiement
  • Exécutez le pipeline OCR sur le même ensemble de documents d'exemple que celui utilisé en production et comparez la qualité du texte obtenu.
  • Tester le traitement PDF sur des documents précédemment traités par le pipeline asynchrone GCS et confirmer que le texte en sortie est identique.
  • Testez les PDF protégés par mot de passe avec input.LoadPdf(path, Password: "...") — précédemment non pris en charge
  • Testez le traitement des TIFF multi-pages en utilisant input.LoadImageFrames() et vérifiez que tous les cadres sont traités
  • Exécutez le traitement par lots sur un échantillon représentatif et vérifiez que la qualité du résultat correspond aux résultats précédents.
  • Confirmez que les valeurs result.Confidence sont dans la plage acceptable pour votre corpus de document
  • Vérifiez la sortie PDF consultable en utilisant result.SaveAsSearchablePdf() pour les documents qui nécessitaient auparavant une bibliothèque PDF distincte
  • Exécutez l'application dans un environnement sans connexion Internet sortante et vérifiez que la reconnaissance optique de caractères (OCR) fonctionne correctement.

Principaux avantages de la migration vers IronOCR

Surface de Credentials Réduite à Zéro Fichiers. Après la migration, il n'y a pas de fichiers de clés JSON, pas de configurations de bucket GCS, pas de rôles IAM, pas de comptes de service, et pas de variables d'environnement GOOGLE_APPLICATION_CREDENTIALS dans votre infrastructure. L'ensemble de la surface d'identification se résume à une variable d'environnement contenant une chaîne de clé de licence. La rotation des clés, qui était une opération périodique obligatoire avec Google Cloud Vision, n'est plus un concept d'actualité. Pour les équipes opérant dans plusieurs régions ou auprès de plusieurs fournisseurs de cloud, la réduction de la complexité de la configuration de déploiement est immédiate.

Traitement des fichiers PDF et TIFF sans dépendances externes. Le pipeline asynchrone GCS et la boucle d'images TIFF System.Drawing sont entièrement supprimés. input.LoadPdf() et input.LoadImageFrames() sont les remplacements — à la fois synchrones, à la fois locaux, à trois lignes de l'appel au résultat. Les PDF protégés par mot de passe, impossibles à créer avec Google Cloud Vision, fonctionnent désormais avec un seul paramètre supplémentaire. Le guide OCR PDF et le guide d'entrée TIFF couvrent l'intégralité de l'API d'entrée.

Traitement par lots à la vitesse du processeur. La suppression du quota de 1 800 requêtes par minute et des délais obligatoires de 60 secondes entre les tentatives permet désormais aux tâches par lots, auparavant limitées en débit, de s'exécuter à la vitesse des cœurs de processeur disponibles. Une machine à 16 cœurs traite simultanément 16 documents sans aucune approbation externe. Le modèle Parallel.ForEach avec des instances IronTesseract par thread est le remplacement direct pour la boucle séquentielle régulée. Le guide d'optimisation de vitesse couvre les options de configuration du moteur qui ajustent le débit pour des types de documents spécifiques.

Données Structurées Sans Protobuf. Chaque OcrResult expose Text, Confidence, Pages, Paragraphs, Lines, Words, et Characters comme propriétés .NET typées sans dépendance au namespace Protobuf, pas de concaténation de symboles, et pas d'arithmétique de vertex pour les boîtes englobantes. Le code qui nécessitait précédemment des boucles imbriquées de 20 lignes pour extraire le texte de paragraphe se réduit à result.Paragraphs.Select(p => p.Text). Pour les cas d'utilisation nécessitant un positionnement au niveau des mots pour l'analyse de la disposition des documents, word.X, word.Y, word.Width et word.Height sont disponibles directement. La page des résultats OCR documente chaque propriété du modèle de résultat.

Sortie PDF consultable intégrée. Google Cloud Visionne renvoie que du texte ; la production d'un PDF consultable nécessitait une bibliothèque de génération de PDF distincte, ajoutant une autre dépendance NuGet , une autre API à apprendre et des licences supplémentaires à évaluer. L'result.SaveAsSearchablePdf(outputPath) d'IronOCR produit un PDF entièrement consultable à partir de tout résultat OCR en une ligne. Pour les workflows d'archivage de documents et les pipelines de découverte légale, cela élimine toute une dépendance. L' exemple de PDF consultable illustre le modèle de bout en bout.

Souveraineté des données pour les industries réglementées. Les documents traités par IronOCR ne quittent jamais le serveur. Pour les dossiers médicaux couverts par la loi HIPAA, les données techniques contrôlées par l'ITAR, les documents des entrepreneurs de défense relevant du champ d'application du CMMC, les documents juridiques confidentiels entre l'avocat et son client et les dossiers financiers relevant du champ d'application du PCI-DSS, l'architecture sur site supprime entièrement la catégorie des sous-traitants de données tiers du champ d'application de la conformité. Il n'y a pas d'accord de partenariat commercial à négocier, pas d'accord de protection des données à signer et pas de politique de conservation des données Google à examiner. Le centre de documentation IronOCR couvre les configurations de déploiement pour les environnements Docker, Linux, Azure et AWS où des exigences de résidence des données s'appliquent.

Veuillez noterGoogle Cloud Vision, Tesseract, et iText sont des marques déposées de leurs propriétaires respectifs. Ce site n'est pas affilié, approuvé ou sponsorisé par Google ou iText Group. Tous les noms de produits, logos et marques appartiennent à leurs propriétaires respectifs. Les comparaisons sont à titre informatif uniquement et reflètent les informations publiquement disponibles au moment de l'écriture.

Questions Fréquemment Posées

Pourquoi devrais-je migrer de Google Cloud Vision API vers IronOcr ?

Parmi les motivations communes, citons l'élimination de la complexité de l'interopérabilité COM, le remplacement de la gestion des licences basée sur les fichiers, l'absence de facturation à la page, l'activation du déploiement Docker/conteneur et l'adoption d'un flux de travail NuGet-natif qui s'intègre à l'outillage .NET standard.

Quels sont les principaux changements de code lors de la migration de Google Cloud Vision API vers IronOcr ?

Remplacer les séquences d'initialisation de Google Cloud Vision par l'instanciation d'IronTesseract, supprimer la gestion du cycle de vie de COM (modèles explicites Create/Load/Close) et mettre à jour les noms des propriétés des résultats. Le résultat est une réduction significative du nombre de lignes de code.

Comment installer IronOCR pour commencer la migration ?

Exécutez "Install-Package IronOcr" dans la console du Package Manager ou "dotnet add package IronOcr" dans le CLI. Les packs de langues sont des paquets distincts : 'dotnet add package IronOcr.Languages.French' pour le français, par exemple.

IronOCR offre-t-il la même précision d'OCR que Google Cloud Vision API pour les documents professionnels standard ?

IronOcr atteint un niveau de précision élevé pour les contenus commerciaux standard, notamment les factures, les contrats, les reçus et les formulaires dactylographiés. Les filtres de prétraitement d'image (désalignement, suppression du bruit, amélioration du contraste) améliorent encore la reconnaissance sur des données dégradées.

Comment IronOCR gère-t-il les données linguistiques que Google Cloud Vision API installe séparément ?

Les données linguistiques de l'IronOcr sont distribuées sous forme de packages NuGet. 'dotnet add package IronOcr.Languages.German' installe la prise en charge de l'allemand. Il n'y a pas de placement manuel de fichiers ou de chemins d'accès aux répertoires.

La migration de Google Cloud Vision API vers IronOCR nécessite-t-elle de modifier l'infrastructure de déploiement ?

IronOCR nécessite moins de changements d'infrastructure que l'API Google Cloud Vision. Il n'y a pas de chemins binaires SDK, de placements de fichiers de licence ou de configurations de serveurs de licence. Le package NuGet contient le moteur OCR complet, et la clé de licence est une chaîne définie dans le code de l'application.

Comment configurer les licences IronOCR après la migration ?

Attribuer IronOcr.License.LicenseKey = "YOUR-KEY" dans le code de démarrage de l'application. Dans Docker ou Kubernetes, stockez la clé dans une variable d'environnement et lisez-la au démarrage. Utilisez License.IsValidLicense pour valider avant d'accepter le trafic.

IronOcr peut-il traiter les PDF de la même manière que Google Cloud Vision ?

Oui, IronOCR lit aussi bien les PDF natifs que les PDF numérisés. Instanciez IronTesseract, appelez ocr.Read(input) où l'input est un chemin PDF ou OcrPdfInput, et itérez les pages OcrResult. Aucun pipeline de rendu PDF séparé n'est nécessaire.

Comment IronOcr gère-t-il le threading dans le cadre d'un traitement à haut volume ?

IronTesseract est sûr pour l'instanciation par thread. Créez une instance par thread dans un Parallel.ForEach ou un Task pool, exécutez l'OCR simultanément et disposez de chaque instance lorsque vous avez terminé. Aucun état global ou verrouillage n'est nécessaire.

Quels formats de sortie IronOCR prend-il en charge après l'extraction du texte ?

IronOCR renvoie des résultats structurés comprenant le texte, les coordonnées des mots, les scores de confiance et la structure des pages. Les options d'exportation comprennent le texte brut, le PDF interrogeable et les objets de résultats structurés pour le traitement en aval.

La tarification d'IronOCR est-elle plus prévisible que celle de Google Cloud Vision API pour la mise à l'échelle des charges de travail ?

IronOCR utilise des licences perpétuelles forfaitaires sans frais par page ou par volume. Que vous traitiez 10 000 ou 10 millions de pages, le coût de la licence reste constant. Les options de licence en volume et en équipe sont disponibles sur la page de tarification d'IronOcr.

Qu'advient-il de mes tests existants après la migration de Google Cloud Vision API vers IronOcr ?

Les tests qui vérifient le contenu du texte extrait doivent continuer à passer après la migration. Les tests qui valident les modèles d'appel d'API ou le cycle de vie des objets COM devront être mis à jour pour refléter le modèle d'initialisation et de résultat plus simple d'IronOcr.

Kannaopat Udonpant
Ingénieur logiciel
Avant de devenir ingénieur logiciel, Kannapat a obtenu un doctorat en ressources environnementales à l'université d'Hokkaido au Japon. Pendant qu'il poursuivait son diplôme, Kannapat est également devenu membre du laboratoire de robotique de véhicules, qui fait partie du département de bioproduction. En 2022, il a utilisé ses compé...
Lire la suite

Équipe de soutien Iron

Nous sommes en ligne 24 heures sur 24, 5 jours sur 7.
Chat
Email
Appelez-moi