Passer au contenu du pied de page
COMPARER à D'AUTRES COMPOSANTS

ABBYY FineReader vs Tesseract : Comparaison de l'OCR

OCR.space ne possède pas de package NuGet . Ce seul fait définit chaque décision d'intégration qu'un développeur .NET prend en le choisissant : vous écrivez un custom HttpClient, encodez en base64 vos documents, POSTez à https://api.ocr.space/parse/image, analysez la réponse JSON manuellement, gérez les erreurs HTTP 429, implémentez une atténuation exponentielle, créez une gestion des limites de taux, et définissez vos propres types d'exception — tout cela avant qu'un seul caractère de texte n'atteigne votre application. Cet article examine le coût réel de cette approche DIY en termes de code, de temps et de fiabilité en production, et le compare directement à IronOCR , une bibliothèque .NET native distribuée sous forme de package NuGet unique.

Comprendre OCR.space

OCR.space est une API REST freemium qui traite les images et les PDF sur des serveurs distants exploités par OCR.space. Le service se positionne autour de son offre gratuite : 25 000 requêtes par mois sans carte de crédit requise, ce qui séduit les développeurs qui expérimentent avec la reconnaissance optique de caractères ou qui développent des projets personnels. Il n'existe aucun package NuGet , aucun kit de développement logiciel .NET officiel, et aucune bibliothèque cliente fortement typée dans aucun langage. Chaque intégration .NET est construite manuellement sur HttpClient.

La réalité architecturale pour les développeurs .NET :

  • Aucun package NuGet : prise en charge .NET de premier ordre inexistante. Pas d'IntelliSense, pas de modèles typés, pas de gestion intégrée des erreurs.
  • Traitement exclusivement dans le cloud : chaque document quitte votre infrastructure. Il n'existe aucune option sur site ni de voie de déploiement auto-hébergée.
  • Intégration REST manuelle : Les développeurs encodent les fichiers en base64, construisent FormUrlEncodedContent ou MultipartFormDataContent, et analysent les réponses JSON brutes.
  • Limitation du débit par les utilisateurs : le niveau gratuit impose 60 requêtes par minute et une limite stricte de 500 requêtes par jour et par adresse IP. Les applications en production doivent implémenter elles-mêmes cette logique.
  • Limites de taille des fichiers : le niveau gratuit refuse les fichiers de plus de 5 Mo. Les fichiers PDF de plusieurs pages dépassent souvent cette limite.
  • Sortie PDF avec filigrane : La génération de PDF consultables avec la version gratuite intègre des filigranes OCR.space, rendant le document inutilisable pour la livraison au client ou l'archivage.
  • Pas de SLA : le niveau gratuit n'offre aucun engagement de disponibilité. Les forfaits payants offrent des limites plus élevées, mais les mêmes contraintes architecturales.

Les développeurs d'intégration REST doivent écrire

La documentation d'OCR.space indique aux développeurs le point de terminaison REST et les laisse se débrouiller pour tout le reste. Le client .NET minimal viable — avant toute prise en compte du renforcement de la sécurité en production — ressemble à ceci :

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

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

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

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

        await _rateLimiter.WaitAsync(cancellationToken);

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

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

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

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

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

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

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

        var result = new OcrResult();

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

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

        return result;
    }

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

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

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

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

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

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

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

        await _rateLimiter.WaitAsync(cancellationToken);

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

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

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

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

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

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

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

        var result = new OcrResult();

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

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

        return result;
    }

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

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

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

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

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

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

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

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

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

        Await _rateLimiter.WaitAsync(cancellationToken)

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

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

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

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

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

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

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

            Dim result = New OcrResult()

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

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

            Return result
        End Using
    End Function

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

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

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

Public Class OcrSpaceException
    Inherits Exception

    Public ReadOnly Property StatusCode As Integer

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

Il s'agit de plus de 90 lignes de code d'infrastructure qui existent avant la première méthode de logique métier. Chaque intégration OCR.space inclut ce code standard (ou un équivalent), et chaque équipe qui l'écrit résout le même problème qu'OCR.space a choisi de ne pas résoudre pour elle.

Comprendre IronOCR

IronOCR est une bibliothèque OCR commerciale pour .NET, installée via le package NuGet IronOcr. Il intègre un moteur Tesseract 5 optimisé avec prétraitement automatique, entrée PDF native, sortie PDF consultable, plus de 125 packs de langues et extraction de résultats structurés — le tout sans dépendance au cloud, sans clés API ni frais par requête. Les documents sont traités localement sur l'infrastructure qui exécute l'application .NET .

Caractéristiques principales :

  • Package NuGet unique : dotnet add package IronOcr est l'installation complète. Aucun binaire natif à gérer, aucun répertoire tessdata, aucune variable d'environnement.
  • API fortement typée : IronTesseract, OcrInput, OcrResult, et CropRectangle sont des types .NET de premier ordre avec un support IntelliSense complet.
  • Prétraitement automatique : les filtres Deskew, DeNoise, Contrast, Binarize et EnhanceResolution s'appliquent automatiquement ou à la demande sans bibliothèques externes.
  • Prise en charge native du format PDF : la lecture des PDF numérisés et la génération de PDF consultables sont intégrées. Aucune limite de taille au-delà de la mémoire disponible.
  • Exécution locale : aucun appel réseau n'est effectué pendant la reconnaissance optique de caractères (OCR). La bibliothèque s'exécute entièrement dans le même processus. Les environnements isolés fonctionnent sans modification de configuration.
  • Sécurité des threads : Plusieurs instances IronTesseract fonctionnent simultanément sans verrouillage ni gestion de contention.
  • Licences perpétuelles : $999 Lite / $1,499 Plus / $2,999 Professional. Aucuns frais par requête, quel que soit le volume.

Comparaison des fonctionnalités

Fonction OCR.space IronOCR
package NuGet Aucun — API REST uniquement IronOcr — support complet .NET
Lieu de traitement Serveurs cloud OCR.space Local — votre infrastructure
Niveau gratuit 25 000 demandes/mois (limité) Essai disponible
Prix ​​payant Contactez OCR.space pour les tarifs actuels $2,999 perpétuel unique
limitation de débit 60 requêtes/min, 500/jour (gratuit) None
Fonctionnalités hors ligne Non Oui — entièrement entrefer
confidentialité des données Documents envoyés à un tiers Les documents restent sur vos serveurs

Comparaison détaillée des fonctionnalités

Fonction OCR.space IronOCR
Intégration
Paquet NuGet None Oui (IronOcr)
Modèles fortement typés Aucun — analyse JSON manuelle Oui (OcrResult, OcrInput)
Assistance IntelliSense None Complet
types d'exceptions personnalisés Vous devez vous définir vous-même Intégré
logique de nouvelle tentative Vous devez vous construire vous-même Intégré
Traitement
Lieu de traitement Serveurs cloud Traitement local en cours
Internet requis Oui, chaque appel Non
Support isolé Non Oui
Limites de débit Oui — tous les forfaits None
Limites de taille de fichier 5 Mo (niveau gratuit) Mémoire uniquement
Capacité OCR
OCR d'image Oui Oui
OCR PDF Oui (avec des limites) Oui (natif, sans limite de taille)
Sortie PDF consultable Filigrane sur le niveau gratuit Sortie propre, sans filigrane
Prétraitement automatique Aucun (côté serveur uniquement) Redresser, réduire le bruit, contraster, binariser, améliorer la résolution
Contrôle manuel du prétraitement None API de filtrage complet
Soutien linguistique ~25 langues Plus de 125 modules linguistiques disponibles via NuGet
Document multilingue Limité Oui — langues primaires et secondaires
Sortie structurée (mots/lignes/pages) Non — uniquement du texte brut Oui — pages, paragraphes, lignes, mots avec des coordonnées
scores de confiance Non Oui — par résultat et par mot
OCR basé sur la région Non Oui — CropRectangle
Lecture de codes-barres lors de la reconnaissance optique de caractères (OCR) Non Oui — ReadBarCodes = true
Déploiement
Docker Nécessite une connexion internet sortante Fonctionne dès le départ
Linux Nécessite une connexion internet sortante Entièrement pris en charge
Conforme à la loi HIPAA Risque — les documents vous échappent. Oui — aucune transmission externe
Conforme au RGPD Risque – Transfert de données de l'UE incertain Oui — aucun traitement de données par des tiers
Tarification
Modèle de tarification Abonnement mensuel Licence perpétuelle à usage unique
Prix ​​d'entrée Contactez OCR.space pour les tarifs actuels $999 unique
Coût par document à l'échelle Oui None
SLA Aucun (gratuit), variable (payant) SLAEnterprise disponible

Profondeur du SDK vs. HTTP brut : le coût de l'intégration

L'écart entre OCR.space et un SDK natif n'est pas une question de préférence stylistique. Elle se mesure en heures de développement, en surface d'exposition aux bogues et en charge de maintenance pour chaque déploiement.

Approche OCR.space

Chaque développeur .NET utilisant OCR.space écrit son propre client HTTP. L'implémentation la plus simple possible — extraction de texte basique sans logique de nouvelle tentative, sans prise en charge des lots et sans prétraitement — compte tout de même plus de 50 lignes :

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

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

    public async Task<string> ExtractText(string imagePath)
    {
        // Read file and encode — document leaves your infrastructure
        byte[] imageBytes = File.ReadAllBytes(imagePath);
        string base64 = Convert.ToBase64String(imageBytes);

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

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

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

        // Parse JSON manually — no typed models
        string json = await response.Content.ReadAsStringAsync();
        using var doc = JsonDocument.Parse(json);

        return doc.RootElement
            .GetProperty("ParsedResults")[0]
            .GetProperty("ParsedText")
            .GetString() ?? string.Empty;
    }
}
// OCR.space: minimum viable extraction — 50 ans et plus lines before business logic
public class OcrSpaceBasicExtraction
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;

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

    public async Task<string> ExtractText(string imagePath)
    {
        // Read file and encode — document leaves your infrastructure
        byte[] imageBytes = File.ReadAllBytes(imagePath);
        string base64 = Convert.ToBase64String(imageBytes);

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

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

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

        // Parse JSON manually — no typed models
        string json = await response.Content.ReadAsStringAsync();
        using var doc = JsonDocument.Parse(json);

        return doc.RootElement
            .GetProperty("ParsedResults")[0]
            .GetProperty("ParsedText")
            .GetString() ?? string.Empty;
    }
}
Imports System
Imports System.Net.Http
Imports System.Threading.Tasks
Imports System.IO
Imports System.Collections.Generic
Imports System.Text.Json

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

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

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

        Dim content As New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
            New KeyValuePair(Of String, String)("apikey", _apiKey),
            New KeyValuePair(Of String, String)("base64Image", $"data:image/png;base64,{base64}"),
            New KeyValuePair(Of String, String)("language", "eng")
        })

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

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

        ' Parse JSON manually — no typed models
        Dim json As String = Await response.Content.ReadAsStringAsync()
        Using doc As JsonDocument = JsonDocument.Parse(json)
            Return doc.RootElement _
                .GetProperty("ParsedResults")(0) _
                .GetProperty("ParsedText") _
                .GetString() OrElse String.Empty
        End Using
    End Function
End Class
$vbLabelText   $csharpLabel

Ajoutez le traitement par lots et le nombre de lignes grimpe à plus de 80, car chaque requête par lots nécessite une limitation du débit, une logique de nouvelle tentative par requête avec un délai exponentiel et une gestion explicite des réponses HTTP 429. Ajoutez le traitement PDF et vous ajoutez une vérification de la taille des fichiers de 5 Mo avant chaque appel, car le niveau gratuit rejettera les fichiers plus volumineux avec une erreur plutôt qu'un avertissement.

Approche d'IronOCR

L'installation IronOCR à partir de NuGet remplace tout ce code d'infrastructure par des appels de méthodes déjà existants :

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

La classe IronTesseract gère la lecture de fichiers, le prétraitement, l'exécution du moteur et la construction du résultat. Aucun client HTTP. Pas d'encodage base64. Aucune analyse JSON. Aucune clé API. Aucun limiteur de débit. Le tutoriel sur la lecture de texte à partir d'images couvre les différentes variations d'entrée (chemins de fichiers, tableaux d'octets, flux, bitmaps) qui suivent toutes le même modèle d'appel unique.

Les chiffres de complexité du code tirés des fichiers sources ne sont pas hypothétiques :

Scénario Lignes d'espacement OCR Lignes IronOCR
Extraction de base 50 ans et plus 3
Traitement PDF 60+ 12
Traitement par lots 80+ 15
Gestion des erreurs 70+ 15
Infrastructure client complète Plus de 200 0 (NuGet le fournit)

Traitement par lots et limites de débit

Le problème de limitation du débit est le point faible le plus évident de l'offre gratuite d'OCR.space en conditions de production réelles.

Approche OCR.space

Le traitement par lots sous OCR.space nécessite de construire et de gérer un SemaphoreSlim pour rester dans la limite de 60 requêtes par minute, plus une logique de nouveau essai avec atténuation exponentielle pour les réponses HTTP 429 qui arrivent quand la logique de sémaphore est imprécise ou quand une infrastructure partagée partage une adresse IP :

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

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

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

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

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

        return results;
    }

    private async Task<string> ProcessWithRetry(string imagePath, int maxRetries = 3)
    {
        for (int attempt = 0; attempt < maxRetries; attempt++)
        {
            try
            {
                byte[] imageBytes = File.ReadAllBytes(imagePath);
                string base64 = Convert.ToBase64String(imageBytes);

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

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

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

                response.EnsureSuccessStatusCode();

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

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

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

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

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

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

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

        return results;
    }

    private async Task<string> ProcessWithRetry(string imagePath, int maxRetries = 3)
    {
        for (int attempt = 0; attempt < maxRetries; attempt++)
        {
            try
            {
                byte[] imageBytes = File.ReadAllBytes(imagePath);
                string base64 = Convert.ToBase64String(imageBytes);

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

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

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

                response.EnsureSuccessStatusCode();

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

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

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

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

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

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

        For Each imagePath In imagePaths
            Await _rateLimiter.WaitAsync()

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

        Return results
    End Function

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

                Dim content As New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
                    New KeyValuePair(Of String, String)("apikey", _apiKey),
                    New KeyValuePair(Of String, String)("base64Image", $"data:image/png;base64,{base64}"),
                    New KeyValuePair(Of String, String)("language", "eng")
                })

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

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

                response.EnsureSuccessStatusCode()

                Dim json As String = Await response.Content.ReadAsStringAsync()
                Using doc As JsonDocument = JsonDocument.Parse(json)
                    Return doc.RootElement _
                        .GetProperty("ParsedResults")(0) _
                        .GetProperty("ParsedText") _
                        .GetString() OrElse String.Empty
                End Using
            Catch ex As HttpRequestException When attempt < maxRetries - 1
                Await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)))
            End Try
        Next

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

La limite de 500 requêtes par jour imposée par le niveau gratuit restreint encore davantage cette possibilité. Une application traitant 600 documents en une seule journée échouera en cours de traitement sans report automatique jusqu'au jour calendaire suivant.

Approche d'IronOCR

IronOCR n'a pas de limite de débit. Le traitement par lots est une boucle :

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

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

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

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

    return results;
}
Imports System.Collections.Generic

' IronOCR batch: 8 lines, no rate limits, no retries needed
Public Function ProcessBatch(imagePaths As String()) As Dictionary(Of String, String)
    Dim results As New Dictionary(Of String, String)()
    Dim ocr As New IronTesseract()

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

    Return results
End Function
$vbLabelText   $csharpLabel

Réutiliser l'instance IronTesseract à travers le lot évite les surcharges répétées d'initialisation du moteur. La vitesse de traitement est limitée par le processeur local et les E/S disque, et non par la politique de limitation d'une API distante. L' exemple de multithreading montre comment paralléliser le traitement sur plusieurs cœurs lorsque le débit est important.

Traitement de PDF

La prise en charge du format PDF illustre la deuxième lacune structurelle majeure.

Approche OCR.space

OCR.space accepte les téléchargements de fichiers PDF sur les abonnements payants et partiellement sur la version gratuite, mais la limite de taille de fichier de 5 Mo sur les comptes gratuits exclut la plupart des documents de plusieurs pages. La sortie PDF consultable de la version gratuite intègre un filigrane. La mise en œuvre nécessite l'extraction des résultats par page et une vérification explicite de la taille des fichiers avant chaque requête :

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

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

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

    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("apikey", _apiKey),
        new KeyValuePair<string, string>("base64Image", $"data:application/pdf;base64,{base64}"),
        new KeyValuePair<string, string>("language", "eng"),
        new KeyValuePair<string, string>("filetype", "PDF"),
        new KeyValuePair<string, string>("isCreateSearchablePdf", "false") // Watermarked on free tier
    });

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

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

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

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

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

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

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

    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("apikey", _apiKey),
        new KeyValuePair<string, string>("base64Image", $"data:application/pdf;base64,{base64}"),
        new KeyValuePair<string, string>("language", "eng"),
        new KeyValuePair<string, string>("filetype", "PDF"),
        new KeyValuePair<string, string>("isCreateSearchablePdf", "false") // Watermarked on free tier
    });

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

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

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

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

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

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

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

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

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

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

        Dim content As New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
            New KeyValuePair(Of String, String)("apikey", _apiKey),
            New KeyValuePair(Of String, String)("base64Image", $"data:application/pdf;base64,{base64}"),
            New KeyValuePair(Of String, String)("language", "eng"),
            New KeyValuePair(Of String, String)("filetype", "PDF"),
            New KeyValuePair(Of String, String)("isCreateSearchablePdf", "false") ' Watermarked on free tier
        })

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

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

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

        Return pageTexts
    End Function
End Class
$vbLabelText   $csharpLabel

Approche d'IronOCR

IronOCR lit les fichiers PDF nativement. La limite est la mémoire disponible, et non un seuil arbitraire de taille de fichier. La génération de fichiers PDF consultables produit des fichiers propres, sans filigrane, quel que soit le niveau de licence :

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

    using var input = new OcrInput();
    input.LoadPdf(pdfPath); // Non 5MB ceiling

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

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

    return pageTexts;
}

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

    using var input = new OcrInput();
    input.LoadPdf(pdfPath); // Non 5MB ceiling

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

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

    return pageTexts;
}

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

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

    Using input As New OcrInput()
        input.LoadPdf(pdfPath) ' Non 5MB ceiling

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

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

    Return pageTexts
End Function

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

Les PDFs protégés par mot de passe sont un seul paramètre : input.LoadPdf("encrypted.pdf", Password: "secret"). Le guide pratique sur la reconnaissance optique de caractères (OCR) des fichiers PDF couvre en détail la sélection de la plage de pages et la gestion des mots de passe. Pour connaître les modèles de génération de PDF consultables, consultez le guide pratique sur les PDF consultables .

Gestion des erreurs

OCR.space délivre les erreurs par deux canaux séparés : les codes de statut HTTP et un indicateur JSON IsErroredOnProcessing. Le code de production doit vérifier les deux, analyser le tableau des erreurs JSON quand IsErroredOnProcessing est vrai, et inspecter les valeurs par page de FileParseExitCode pour des échecs partiels. Rien de tout cela n'est typé — tout est de l'analyse de chaîne depuis JsonDocument :

// OCR.space: two error layers, all string-based
public async Task<string> SafeExtract(HttpClient client, string apiKey, string imagePath)
{
    try
    {
        byte[] imageBytes = File.ReadAllBytes(imagePath);
        string base64 = Convert.ToBase64String(imageBytes);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            Dim content As New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
                New KeyValuePair(Of String, String)("apikey", apiKey),
                New KeyValuePair(Of String, String)("base64Image", $"data:image/png;base64,{base64}")
            })

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

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

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

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

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

IronOCR génère des exceptions .NET standard avec des messages clairs. Pas d'analyse JSON, pas de changement de code d'état HTTP, pas d'extraction d'erreurs par couches :

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

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

La référence API IronTesseract et la référence API OcrResult documentent la structure de résultat typé, y compris la propriété Confidence pour évaluer la qualité d'extraction.

Référence de mappage d'API

Concept OCR.space Équivalent d'IronOCR
POST https://api.ocr.space/parse/image new IronTesseract().Read(path)
FormUrlEncodedContent / MultipartFormDataContent OcrInput
base64Image paramètre input.LoadImage(path) ou input.LoadImage(bytes)
language paramètre ocr.Language = OcrLanguage.English
OCREngine paramètre Moteur géré en interne
isOverlayRequired paramètre result.Words / result.Lines (toujours disponible)
isCreateSearchablePdf paramètre result.SaveAsSearchablePdf(outputPath)
filetype=PDF paramètre input.LoadPdf(path)
ParsedResults[0].ParsedText result.Text
ParsedResults[n] (par page) result.Pages[n].Text
IsErroredOnProcessing indicateur JSON Exception standard .NET
FileParseExitCode indicateur par page Exception standard .NET
ProcessingTimeInMilliseconds Implicite — aucun traitement supplémentaire nécessaire
HTTP 429 / limitation de débit Sans objet — aucune limite de débit
Custom OcrResult POCO (défini par l'utilisateur) IronOcr.OcrResult (fourni par NuGet)
Custom OcrSpaceException (défini par l'utilisateur) types d'exceptions .NET standard
SemaphoreSlim limiteur de taux (construit par l'utilisateur) Pas nécessaire
Clé API dans chaque requête IronOcr.License.LicenseKey (une fois au démarrage)

Quand les équipes envisagent de passer d'OCR.space à IronOCR

Dépassement du niveau gratuit lors des tests de charge

La limite de 500 requêtes par jour et par adresse IP imposée par OCR.space est une limite stricte, et non une simple recommandation. Les équipes découvrent ce problème lors des tests de charge ou des déploiements en phase de test, lorsque plusieurs développeurs partagent une même adresse IP de bureau. Un pipeline CI/CD partagé qui exécute des tests d'intégration sur OCR.space épuisera le quota quotidien avant la fin de la journée de travail. À ce stade, l'application échoue non pas parce que le code est erroné, mais parce qu'un compteur tiers a atteint un seuil arbitraire. Le passage au traitement local élimine totalement ce type de défaillance : il n'y a pas de quota à épuiser puisque le traitement s'exécute en interne.

Examens de conformité et de classification des données

Les analyses de sécurité qui classent les documents comme PII, PHI ou financièrement sensibles créent un problème binaire pour OCR.space : le service n'a pas de voie de déploiement sur site. Il n'existe aucun équivalent documenté d'un accord de partenariat commercial (BAA) pour les scénarios HIPAA, aucune structure d'accord de traitement des données qui régisse clairement les transferts de données de l'UE en vertu du RGPD, et aucun mécanisme technique pour empêcher la transmission de documents même avec des contrôles contractuels en place. Les équipes recevant des constats de non-conformité concernant le traitement des documents transmis via le cloud ont besoin d'une solution locale. IronOCR répond à cette exigence par sa conception même : les documents ne quittent jamais le serveur d'application. La page relative aux licences documente la structure de la licence perpétuelle, ce qui simplifie également la documentation de conformité en éliminant l'examen du fournisseur de cloud du périmètre.

Charge de maintenance du code d'intégration

Les intégrations OCR.space accumulent une dette technique proportionnelle à leurs exigences fonctionnelles. Le client HTTP initial est gérable. L'équipe ajoute ensuite une logique de nouvelle tentative. Ils ajoutent ensuite un suivi des limites de débit par adresse IP, car plusieurs services partagent une même adresse. Ils ajoutent ensuite une étape de prévalidation de la taille du fichier. Puis, ils constatent que la structure des erreurs JSON diffère entre les réponses du moteur 1 et celles du moteur 2 et ajoutent une branche. Six mois plus tard, l'intégration d'OCR.space représente 400 lignes de code d'infrastructure que chaque nouveau membre de l'équipe doit comprendre avant de toucher à quoi que ce soit en rapport avec l'OCR. Lorsque cette équipe évalue le coût de maintenance par rapport à une licence IronOCR $999 à paiement unique, l'arithmétique est simple.

Exigences de sortie structurée

OCR.space renvoie du texte brut et rien d'autre. La réponse ne comporte ni coordonnées des mots, ni limites de lignes, ni segmentation en paragraphes, ni scores de confiance par mot. Les applications qui ont besoin d'extraire des champs spécifiques des factures (nom du fournisseur à un emplacement connu, montant total dans le coin inférieur droit) ne disposent d'aucune base structurée sur laquelle s'appuyer à partir des données de sortie d'OCR.space. IronOCR expose une hiérarchie complète du document avec des coordonnées et des valeurs de confiance à chaque niveau de granularité : pages, paragraphes, lignes et mots individuels. Le traitement par région permet de cibler des zones spécifiques d'un document sans post-traitement de la totalité de la page. Le guide pratique sur les résultats de lecture et le guide OCR basé sur la région couvrent ces modèles en détail.

Croissance du volume au-delà du niveau gratuit

Avec 25 000 requêtes par mois, le niveau gratuit est fonctionnel pour les scénarios à faible volume. Au-delà de cela, les niveaux payants s'appliquent — contactez OCR.space pour les tarifs actuels. Ces coûts s'accumulent au fil du temps jusqu'à ce que la comparaison soit faite avec une licence perpétuelle $999 sans frais par requête jamais. Les équipes qui prévoient une croissance du volume de documents supérieure à 25 000 par mois ont une analyse de coûts simple en faveur du traitement local.

Considérations courantes en matière de migration

Remplacer le client HTTP par un appel de méthode

La migration d'OCR.space vers IronOCR est architecturalement simple car les deux systèmes renvoient du texte à partir de documents. Le client HTTP, l'analyseur JSON, le limiteur de débit et les types d'exceptions personnalisés disparaissent. Ce qui les remplace, c'est un simple package NuGet et des appels de méthode. Avant/après à la limite de service :

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        Dim content As New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
            New KeyValuePair(Of String, String)("apikey", _apiKey),
            New KeyValuePair(Of String, String)("base64Image", $"data:image/png;base64,{base64}")
        })

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

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

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

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

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

L'implémentation IDisposable disparaît parce qu'il n'y a pas de HttpClient à gérer.

Suppression de la clé API

OCR.space requiert une clé API pour chaque requête HTTP. Cette clé doit être stockée en toute sécurité, renouvelée lors de son utilisation et exclue du système de gestion de versions. IronOCR utilise une clé de licence définie une seule fois au démarrage de l'application, généralement à partir d'une variable d'environnement ou d'un système de configuration :

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

Après la migration, le processus de rotation des clés API, la configuration de la gestion des secrets et la logique d'injection d'une clé par requête deviennent tous inutiles.

Prétraitement après migration

OCR.space applique un traitement côté serveur avant de renvoyer les résultats, mais les développeurs n'ont aucun contrôle sur ce que ce traitement inclut ou non. IronOCR expose des méthodes de prétraitement explicites. Si la qualité des documents est un problème (numérisations déformées, photocopies à faible contraste, télécopies bruitées), le pipeline de prétraitement comprend trois à cinq appels de méthode :

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

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

Le guide de correction de la qualité d'image et le guide de correction des couleurs d'image documentent chaque filtre disponible avec des exemples illustrant les différences de précision avant/après.

Asynchrone vs. Synchrone

Les appels OCR.space sont intrinsèquement asynchrones car ils impliquent des allers-retours HTTP. Les appels IronOCR sont synchrones par défaut, ce qui simplifie le code dans les contextes non Web. Pour ASP.NET et les scénarios serveur nécessitant une exécution non bloquante, IronOCR prend en charge l'opération asynchrone via Task.Run wrapping ou API asynchrone directe. Le guide OCR asynchrone décrit les modèles. Supprimer await d'une méthode de service qui attendait auparavant un appel cloud simplifie la pile d'appels dans les contextes où l'asynchronicité a été adoptée uniquement parce que OCR.space l'exigeait.

Fonctionnalités supplémentaires d'IronOCR

Fonctionnalités non abordées dans les sections précédentes, chacune représentant une fonctionnalité sans équivalent dans OCR.space :

  • Exportation hOCR : result.SaveAsHocrFile("output.hocr") produit une sortie hOCR conforme aux normes pour les pipelines de traitement de documents en aval.
  • Suivi de la progression : Abonnez-vous aux événements de progression pour les opérations par lots multipages de longue durée, activant les barres de progression et les calculs d'ETA dans les applications d'interface utilisateur.
  • Extraction de tableaux : Les données structurées des tableaux sont accessibles via le modèle de résultat, prenant en charge des cas d'utilisation tels que l'extraction des lignes de factures qui nécessitent un alignement des colonnes.
  • Lecture de documents spécialisée : Les zones MRZ de passeport, les plaques d'immatriculation, les lignes de chèques MICR et la reconnaissance d'écriture manuscrite sont des capacités conçues sur mesure accessibles via la même API IronTesseract.

Compatibilité .NET et préparation à l'avenir

IronOCR cible .NET 6, .NET 7, .NET 8 et .NET 9, avec une prise en charge active de chaque version LTS et STS. La bibliothèque fonctionne sous Windows x64, Windows x86, Linuxx64, macOS, Docker, AWS Lambda et Azure App Service, le tout à partir du même package NuGet sans configuration spécifique à la plateforme. OCR.space exige le protocole HTTPS sortant depuis tous les environnements de déploiement, ce qui exclut les réseaux isolés, les proxys d'entreprise restrictifs et les infrastructures où le trafic sortant vers les API tierces est bloqué par politique. IronOCR ne nécessite aucune connexion réseau lors de son exécution ; Il s'exécute entièrement en interne. Alors que .NET 10 approche de sa sortie fin 2026, l'expérience d'IronOCR en matière de maintien de la compatibilité entre les principales versions de .NET assure la continuité sans modifications d'intégration.

Conclusion

OCR.space occupe un créneau réel et utile : les développeurs qui ont besoin de prototyper une fonctionnalité OCR en un week-end, les étudiants qui explorent les concepts d'extraction de texte ou les outils personnels à faible volume (moins de 25 000 documents par mois) sans exigences de conformité. Pour ce public, le niveau gratuit offre une réelle valeur ajoutée.

Le problème, c'est que les développeurs .NET ne créent généralement pas de prototypes le week-end. Ils développent des systèmes de facturation, des logiciels de traitement de dossiers médicaux, des chaînes de traitement d'archivage de documents et des applications métier soumises à des exigences de conformité strictes. Dans ces contextes, l'absence du package NuGet d'OCR.space n'est pas un inconvénient, mais une incompatibilité structurelle. Chaque heure passée à développer le client HTTP, le limiteur de débit, l'analyseur JSON et l'infrastructure de nouvelle tentative est une heure de moins consacrée aux besoins réels de l'application. Ce coût est concentré au départ, mais les coûts de maintenance persistent pendant toute la durée de l'intégration.

IronOCR s'attaque au problème spécifique qui caractérise toute intégration OCR.space : l'absence d'un véritable SDK. Un seul package NuGet , un seul appel de méthode, aucune infrastructure HTTP, aucune limite de débit, aucun document transmis à des serveurs externes. Le prix d'entrée $999 est un coût unique ; les coûts d'abonnement récurrents OCR.space le dépasseront avec le temps, surtout à mesure que le volume augmente. Pour les équipes ayant des exigences de conformité, le traitement local est la seule option quel qu'en soit le coût.

La comparaison se résume finalement à une seule question : l'application a-t-elle besoin de l'OCR en tant que dépendance REST externe ou en tant que fonction de bibliothèque ? Pour les applications .NET de production, la réponse est presque toujours la seconde.

Veuillez noterOCR.space et Tesseract sont des marques déposées de leurs propriétaires respectifs. Ce site n'est pas affilié à, approuvé par ou sponsorisé par Google ou OCR.space. Tous les noms de produits, logos et marques sont la propriété de 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

Qu'est-ce que l'API OCR.space ?

L'API OCR.space est une solution d'OCR utilisée par les développeurs et les entreprises pour extraire du texte à partir d'images et de documents. Il s'agit de l'une des nombreuses options d'OCR évaluées aux côtés d'IronOCR pour le développement d'applications .NET.

Comment IronOCR se compare-t-il à l'API OCR.space pour les développeurs .NET ?

IronOCR est une bibliothèque OCR .NET native de NuGet qui utilise IronTesseract comme moteur principal. Par rapport à l'API OCR.space, elle offre un déploiement plus simple (pas d'installateurs SDK), un prix forfaitaire et une API C# propre sans interopérabilité COM ou dépendances cloud.

IronOcr est-il plus facile à configurer que l'API OCR.space ?

IronOCR s'installe via un seul package NuGet. Il n'y a pas d'installateur SDK, de fichiers de licence à copier, de composants COM à enregistrer ou de binaires d'exécution séparés à gérer. L'ensemble du moteur d'OCR est inclus dans le package.

Quelles sont les différences de précision entre l'API OCR.space et IronOCR ?

IronOcr atteint une grande précision de reconnaissance pour les documents commerciaux standard, les factures, les reçus et les formulaires numérisés. Pour les documents très dégradés ou les scripts peu courants, la précision varie en fonction de la qualité de la source. IronOCR comprend des filtres de prétraitement d'image pour améliorer la reconnaissance sur des entrées de faible qualité.

IronOCR prend-il en charge l'extraction de texte au format PDF ?

Oui. IronOCR extrait le texte des PDF natifs et des images PDF numérisées en un seul appel. Il prend également en charge les fichiers TIFF multipages, les images et les flux. Pour les PDF numérisés, l'OCR est appliquée page par page avec des objets de résultat par page.

Comment la licence API d'OCR.space se compare-t-elle à celle d'IronOCR ?

IronOCR utilise une licence perpétuelle forfaitaire sans frais par page ou par scan. Les organisations qui traitent de gros volumes de documents paient le même coût de licence, quel que soit le volume. Les détails et la tarification au volume se trouvent sur la page de licence d'IronOCR.

Quelles langues IronOCR prend-il en charge ?

IronOcr prend en charge 127 langues via des packs linguistiques NuGet distincts. L'ajout d'une langue nécessite une seule commande "dotnet add package IronOcr.Languages.{Language}". Il n'est pas nécessaire de placer manuellement des fichiers ou de configurer des chemins d'accès.

Comment installer IronOCR dans un projet .NET ?

Installation via NuGet : 'Install-Package IronOcr' dans la console du Package Manager ou 'dotnet add package IronOcr' dans le CLI. Les packs de langues supplémentaires sont installés de la même manière. Aucun programme d'installation du SDK n'est nécessaire.

IronOCR est-il adapté à Docker et aux déploiements conteneurisés, contrairement à OCR.space ?

Oui. IronOCR fonctionne dans les conteneurs Docker via son package NuGet. La clé de licence est définie via une variable d'environnement. Aucun fichier de licence, chemin d'accès au SDK ou montage de volume n'est nécessaire pour le moteur OCR lui-même.

Puis-je essayer IronOCR avant de l'acheter, par rapport à OCR.space ?

Oui. Le mode d'essai d'IronOcr traite les documents et renvoie les résultats de l'OCR avec un filigrane en surimpression sur la sortie. Vous pouvez vérifier la précision sur vos propres documents avant d'acheter une licence.

IronOCR prend-il en charge la lecture de codes-barres parallèlement à l'extraction de texte ?

IronOCR se concentre sur l'extraction de texte et l'OCR. Pour la lecture de codes-barres, Iron Software propose IronBarcode comme bibliothèque d'accompagnement. Les deux sont disponibles individuellement ou dans le cadre de l'offre groupée Iron Suite.

Est-il facile de migrer de l'API OCR.space vers IronOcr ?

La migration de l'API OCR.space vers IronOCR implique généralement le remplacement des séquences d'initialisation par l'instanciation d'IronTesseract, la suppression de la gestion du cycle de vie de COM et la mise à jour des appels d'API. La plupart des migrations réduisent considérablement la complexité du code.

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