Comment fixer les couleurs d'une image pour la lecture en C#
Ce guide accompagne les développeurs .NET dans le remplacement de l'intégration de l'API REST d'OCR.space par IronOCR , une bibliothèque .NET native fournie sous forme de package NuGet unique. Il couvre l'échange de packages, le nettoyage de l'espace de noms et quatre scénarios concrets de migration de code spécifiques à la transition REST-local : élimination du téléchargement multipart, suppression de l'encodage base64, remplacement de la sélection du moteur OCR et extraction de données structurées. Les développeurs qui ont lu l'article comparatif de la phase 1 trouveront dans ce guide un focus sur les étapes mécaniques de la migration elle-même plutôt que sur la comparaison des fonctionnalités.
Pourquoi migrer depuis OCR.space ?
OCR.space comble un véritable manque : une expérimentation sans frais pour les développeurs qui souhaitent tester l'OCR en un après-midi sans rien installer. Le problème, c'est que la version gratuite est conçue pour le prototypage, et non pour la production. Dès qu'une application .NET passe à des volumes de documents réels, à des exigences de conformité ou au développement d'équipe, chaque caractéristique de l'intégration d'OCR.space devient un frein pour l'application.
L'absence de package NuGet implique l'absence de SDK et d'IntelliSense. OCR.space propose un point de terminaison REST et une documentation. L'intégration .NET — construction du client HTTP, sérialisation des requêtes, désérialisation des réponses, gestion des erreurs et logique de nouvelle tentative — est entièrement de la responsabilité du développeur. Il ne s'agit pas d'un simple désagrément. Le client minimum viable comprend plus de 80 lignes de code d'infrastructure avant même l'écriture de la première méthode de logique métier. Ce code est identique pour toutes les intégrations OCR.space dans chaque base de code .NET , ce qui entraîne une accumulation de bogues et une charge de maintenance croissante au fil du temps.
Les limitations de débit imposent des plafonds artificiels aux applications de production. L'offre gratuite impose 60 requêtes par minute et 500 requêtes par jour et par adresse IP. Les deux limites sont des murs infranchissables. Une application qui dépasse 500 requêtes entre minuit et minuit suivant reçoit des réponses d'erreur jusqu'à ce que le compteur soit réinitialisé. Les systèmes de production exécutés sur des réseaux de bureau partagés ou dans des environnements CI/CD partagés peuvent épuiser leur quota journalier avant la fin des heures de bureau.
Les documents quittent votre infrastructure à chaque appel. OCR.space ne propose aucune option de déploiement sur site. Chaque requête transmet le document (factures, dossiers médicaux, contrats, pièces d'identité) aux serveurs cloud d'OCR.space. Les réglementations HIPAA et RGPD, ainsi que les politiques internes de classification des données qui interdisent la transmission de documents sensibles à des tiers, rendent l'architecture d'OCR.space incompatible, indépendamment des contrôles contractuels.
L'offre gratuite produit des PDF interrogeables avec filigrane. Les applications qui génèrent des PDF interrogeables en tant que livrables (systèmes d'archivage de documents, plateformes de conformité, portails documentaires clients) ne peuvent pas utiliser l'offre gratuite d'OCR.space à cette fin. Le filigrane est intégré au PDF généré et ne peut être supprimé sans un abonnement payant.
Le prix de l'abonnement augmente avec le volume ; le niveau PRO d'OCR.space à 144 $ par an dépasse le prix d'entrée perpétuel d'IronOCR $999 avant la sixième année. Les équipes prévoyant une croissance du volume de documents au-delà du seuil de la version gratuite sont confrontées à des coûts d'abonnement croissants par rapport à une licence perpétuelle fixe. La $999 licence Lite couvre un développeur et un lieu de déploiement sans frais par demande à tout volume. Consultez la page des licences IronOCR pour plus de détails sur les différents niveaux.
Le problème fondamental
OCR.space exige que vous construisiez un client HTTP complet avant de traiter un seul document :
// OCR.space: 80+ lines of infrastructure before business logic
public class OcrSpaceApiClient : IDisposable
{
private readonly HttpClient _httpClient;
private readonly string _apiKey;
private readonly SemaphoreSlim _rateLimiter; // You implement this
public OcrSpaceApiClient(string apiKey)
{
_httpClient = new HttpClient();
_httpClient.Timeout = TimeSpan.FromSeconds(120);
_rateLimiter = new SemaphoreSlim(60, 60); // Free tier: 60/min
}
// ... 70+ more lines of HTTP plumbing follow
}
// OCR.space: 80+ lines of infrastructure before business logic
public class OcrSpaceApiClient : IDisposable
{
private readonly HttpClient _httpClient;
private readonly string _apiKey;
private readonly SemaphoreSlim _rateLimiter; // You implement this
public OcrSpaceApiClient(string apiKey)
{
_httpClient = new HttpClient();
_httpClient.Timeout = TimeSpan.FromSeconds(120);
_rateLimiter = new SemaphoreSlim(60, 60); // Free tier: 60/min
}
// ... 70+ more lines of HTTP plumbing follow
}
Imports System
Imports System.Net.Http
Imports System.Threading
' OCR.space: 80+ lines of infrastructure before business logic
Public Class OcrSpaceApiClient
Implements IDisposable
Private ReadOnly _httpClient As HttpClient
Private ReadOnly _apiKey As String
Private ReadOnly _rateLimiter As SemaphoreSlim ' You implement this
Public Sub New(apiKey As String)
_httpClient = New HttpClient()
_httpClient.Timeout = TimeSpan.FromSeconds(120)
_rateLimiter = New SemaphoreSlim(60, 60) ' Free tier: 60/min
End Sub
' ... 70+ more lines of HTTP plumbing follow
Public Sub Dispose() Implements IDisposable.Dispose
_httpClient.Dispose()
_rateLimiter.Dispose()
End Sub
End Class
IronOCR est un package NuGet . Le client est entièrement rédigé :
// IronOCR: no client to build, no rate limiter to manage
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("document.jpg");
Console.WriteLine(result.Text);
// IronOCR: no client to build, no rate limiter to manage
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("document.jpg");
Console.WriteLine(result.Text);
Imports IronOcr
' IronOCR: no client to build, no rate limiter to manage
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim result = New IronTesseract().Read("document.jpg")
Console.WriteLine(result.Text)
Comparaison des fonctionnalités IronOCR et d'OCR.space
Le tableau ci-dessous établit une correspondance directe entre les concepts et les contraintes d'OCR.space et leurs équivalents IronOCR .
| Fonction | OCR.space | IronOCR |
|---|---|---|
| package NuGet | Aucun — API REST uniquement | IronOcr — .NET natif |
| SDK / IntelliSense | Aucun — JSON manuel | API complète — typée |
| Modèles personnalisés requis | Non | Non |
| Lieu de traitement | Serveurs cloud OCR.space | Local — en cours de traitement |
| dépendance à Internet | Nécessaire pour chaque appel | None |
| Déploiement isolé | Non pris en charge | Entièrement pris en charge |
| Limites de débit | 60/min, 500/jour (gratuit) | None |
| limite de taille de fichier | 5 Mo (niveau gratuit) | Mémoire disponible uniquement |
| Entrée PDF | Oui (limité, 5 Mo) | Oui — natif, sans limite de taille |
| Sortie PDF consultable | Filigrane sur le niveau gratuit | Sortie propre, tous niveaux |
| prétraitement automatique | Côté serveur, sans contrôle du développeur | Redresser, réduire le bruit, contraster, binariser, accentuer |
| Soutien linguistique | ~25 langues | Plus de 125 modules linguistiques disponibles via NuGet |
| Document multilingue | Non pris en charge | Oui — OcrLanguage.French + OcrLanguage.German |
| Sortie structurée (mots, lignes) | Texte brut uniquement | Pages, paragraphes, lignes, mots avec coordonnées |
| scores de confiance au niveau des mots | Non disponible | Oui — word.Confidence |
| OCR basé sur la région | Non pris en charge | Oui — CropRectangle |
| Lecture de codes-barres | Non pris en charge | Oui — ReadBarCodes = true |
| Génération de PDF consultables | Avec filigrane (gratuit), sans filigrane (payant) | Sortie propre — tous les niveaux de licence |
| Conformité HIPAA/RGPD | Risque — données transmises à l'extérieur | Oui — aucune transmission de données externe |
| Modèle de tarification | Abonnement mensuel | perpétuel unique |
| Prix d'entrée | 12 $/mois (144 $/an) | $999 une seule fois |
| Compatibilité .NET | HttpClient — tout .NET |
.NET 4.6.2+, .NET 5/6/7/8/9 |
| Déploiement multiplateforme | Nécessite une connexion internet sortante | Windows, Linux, macOS, Docker, Azure, AWS |
Démarrage rapide : Migration d'OCR.space vers IronOCR
Étape 1 : Remplacer le package NuGet
OCR.space ne possède aucun package NuGet à désinstaller. Supprimez tout le code d'infrastructure lié à OCR.space du projet : la classe wrapper HttpClient, le limiteur de taux SemaphoreSlim, les modèles de résultats personnalisés et les types d'exceptions personnalisés. Tous sont remplacés par le package NuGet d'IronOCR.
Installez IronOCR depuis la page NuGet IronOCR :
dotnet add package IronOcr
Étape 2 : Mise à jour des espaces de noms
Supprimez les espaces de noms HTTP et JSON OCR.space. Ajoutez l'espace de noms IronOCR :
// Before (OCR.space — manually written infrastructure)
using System.Net.Http;
using System.Text.Json;
using System.Threading;
// After (IronOCR)
using IronOcr;
// Before (OCR.space — manually written infrastructure)
using System.Net.Http;
using System.Text.Json;
using System.Threading;
// After (IronOCR)
using IronOcr;
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading
Imports IronOcr
Étape 3 : initialisation de la licence
Ajouter l'initialisation de la licence une seule fois au démarrage de l'application, et non à chaque demande :
// Program.cs or application startup
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
// Program.cs or application startup
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
Imports IronOcr
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Exemples de migration de code
Remplacement des téléchargements de fichiers MultipartFormDataContent
OCR.space nécessite de construire MultipartFormDataContent avec les octets de fichier et la clé API, puis de faire un POST à l'extrémité cloud. Le document laisse votre infrastructure à jour à chaque appel.
Approche OCR.space :
// MultipartFormDataContent: file upload to cloud on every request
public async Task<string> UploadAndExtract(string imagePath)
{
using var content = new MultipartFormDataContent();
var imageBytes = File.ReadAllBytes(imagePath);
// Document is transmitted to OCR.space servers here
content.Add(new ByteArrayContent(imageBytes), "file", Path.GetFileName(imagePath));
content.Add(new StringContent(_apiKey), "apikey");
content.Add(new StringContent("eng"), "language");
content.Add(new StringContent("2"), "OCREngine"); // Select Engine 2
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
// Navigate JSON tree manually — no typed result
return doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("ParsedText")
.GetString() ?? string.Empty;
}
// MultipartFormDataContent: file upload to cloud on every request
public async Task<string> UploadAndExtract(string imagePath)
{
using var content = new MultipartFormDataContent();
var imageBytes = File.ReadAllBytes(imagePath);
// Document is transmitted to OCR.space servers here
content.Add(new ByteArrayContent(imageBytes), "file", Path.GetFileName(imagePath));
content.Add(new StringContent(_apiKey), "apikey");
content.Add(new StringContent("eng"), "language");
content.Add(new StringContent("2"), "OCREngine"); // Select Engine 2
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
// Navigate JSON tree manually — no typed result
return doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("ParsedText")
.GetString() ?? string.Empty;
}
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks
Public Class YourClassName
Private _apiKey As String
Private _httpClient As HttpClient
Public Async Function UploadAndExtract(imagePath As String) As Task(Of String)
Using content As New MultipartFormDataContent()
Dim imageBytes = File.ReadAllBytes(imagePath)
' Document is transmitted to OCR.space servers here
content.Add(New ByteArrayContent(imageBytes), "file", Path.GetFileName(imagePath))
content.Add(New StringContent(_apiKey), "apikey")
content.Add(New StringContent("eng"), "language")
content.Add(New StringContent("2"), "OCREngine") ' Select Engine 2
Dim response = Await _httpClient.PostAsync("https://api.ocr.space/parse/image", content)
response.EnsureSuccessStatusCode()
Dim json As String = Await response.Content.ReadAsStringAsync()
Using doc = JsonDocument.Parse(json)
' Navigate JSON tree manually — no typed result
Return doc.RootElement _
.GetProperty("ParsedResults")(0) _
.GetProperty("ParsedText") _
.GetString() OrElse String.Empty
End Using
End Using
End Function
End Class
Approche IronOCR :
// OcrInput replaces the entire upload + JSON pipeline
public string ExtractFromFile(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath); // Stays local — no network call
var ocr = new IronTesseract();
var result = ocr.Read(input);
return result.Text; // Typed property — no JSON navigation
}
// OcrInput replaces the entire upload + JSON pipeline
public string ExtractFromFile(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath); // Stays local — no network call
var ocr = new IronTesseract();
var result = ocr.Read(input);
return result.Text; // Typed property — no JSON navigation
}
Imports IronTesseract
Public Function ExtractFromFile(ByVal imagePath As String) As String
Using input As New OcrInput()
input.LoadImage(imagePath) ' Stays local — no network call
Dim ocr As New IronTesseract()
Dim result = ocr.Read(input)
Return result.Text ' Typed property — no JSON navigation
End Using
End Function
OcrInput est le remplacement local pour MultipartFormDataContent. Il accepte les chemins de fichiers, les tableaux d'octets, les flux et les fichiers TIFF multipages via une API cohérente. Le HttpClient, l'injection de clé API, et la navigation JSON disparaissent entièrement. Le guide d'utilisation de la saisie d'images couvre tous les formats d'entrée pris en charge.
Suppression de l'encodage Base64
Lorsque les intégrations OCR.space utilisent le paramètre de formulaire base64Image au lieu du paramètre de téléchargement de fichier, le code lit le fichier en octets, le code en Base64, construit une chaîne d'URI de données et l'intègre dans FormUrlEncodedContent. IronOCR accepte directement les octets bruts sans étape d'encodage.
Approche OCR.space :
// base64Image parameter: read → encode → embed in form → POST → parse
public async Task<string> ExtractViaBase64(string imagePath)
{
byte[] imageBytes = await File.ReadAllBytesAsync(imagePath);
string base64Image = Convert.ToBase64String(imageBytes); // Mandatory encoding step
// Embed as data URI — adds 33% overhead to payload size
string mimeType = "image/png";
string dataUri = $"data:{mimeType};base64,{base64Image}";
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", dataUri),
new KeyValuePair<string, string>("language", "eng"),
new KeyValuePair<string, string>("isOverlayRequired", "false")
});
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", formContent);
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
return doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("ParsedText")
.GetString() ?? string.Empty;
}
// base64Image parameter: read → encode → embed in form → POST → parse
public async Task<string> ExtractViaBase64(string imagePath)
{
byte[] imageBytes = await File.ReadAllBytesAsync(imagePath);
string base64Image = Convert.ToBase64String(imageBytes); // Mandatory encoding step
// Embed as data URI — adds 33% overhead to payload size
string mimeType = "image/png";
string dataUri = $"data:{mimeType};base64,{base64Image}";
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", dataUri),
new KeyValuePair<string, string>("language", "eng"),
new KeyValuePair<string, string>("isOverlayRequired", "false")
});
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", formContent);
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.Collections.Generic
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks
Public Class ImageProcessor
Private _apiKey As String
Private _httpClient As HttpClient
Public Sub New(apiKey As String, httpClient As HttpClient)
_apiKey = apiKey
_httpClient = httpClient
End Sub
' base64Image parameter: read → encode → embed in form → POST → parse
Public Async Function ExtractViaBase64(imagePath As String) As Task(Of String)
Dim imageBytes As Byte() = Await File.ReadAllBytesAsync(imagePath)
Dim base64Image As String = Convert.ToBase64String(imageBytes) ' Mandatory encoding step
' Embed as data URI — adds 33% overhead to payload size
Dim mimeType As String = "image/png"
Dim dataUri As String = $"data:{mimeType};base64,{base64Image}"
Dim formContent As New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
New KeyValuePair(Of String, String)("apikey", _apiKey),
New KeyValuePair(Of String, String)("base64Image", dataUri),
New KeyValuePair(Of String, String)("language", "eng"),
New KeyValuePair(Of String, String)("isOverlayRequired", "false")
})
Dim response As HttpResponseMessage = Await _httpClient.PostAsync("https://api.ocr.space/parse/image", formContent)
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
Approche IronOCR :
// LoadImage(bytes): raw bytes accepted directly — no encoding
public string ExtractFromBytes(byte[] imageBytes)
{
using var input = new OcrInput();
input.LoadImage(imageBytes); // Non Base64, no data URI, no overhead
var result = new IronTesseract().Read(input);
return result.Text;
}
// LoadImage(bytes): raw bytes accepted directly — no encoding
public string ExtractFromBytes(byte[] imageBytes)
{
using var input = new OcrInput();
input.LoadImage(imageBytes); // Non Base64, no data URI, no overhead
var result = new IronTesseract().Read(input);
return result.Text;
}
Imports IronOcr
Public Function ExtractFromBytes(imageBytes As Byte()) As String
Using input As New OcrInput()
input.LoadImage(imageBytes) ' Non Base64, no data URI, no overhead
Dim result = New IronTesseract().Read(input)
Return result.Text
End Using
End Function
L'étape d'encodage Base64 n'existe pas dans IronOCR car il n'y a pas de couche de transport HTTP. Les octets bruts vont directement dans OcrInput.LoadImage(). La surcharge liée à l'URI des données (l'encodage Base64 augmente la taille de la charge utile d'environ 33 %) disparaît également. Le guide d'entrée de flux montre le même modèle pour les entrées Stream, ce qui est utile lorsque les octets proviennent d'un gestionnaire de téléchargement ou d'un tampon mémoire plutôt que d'un fichier.
Remplacement de la sélection du moteur OCR par le prétraitement d'images
OCR.space expose deux moteurs OCR via le paramètre de formulaire OCREngine : Le moteur 1 est plus rapide avec une précision inférieure sur les mises en page complexes ; Le moteur 2 est plus lent, mais plus précis pour la plupart des types de documents. Les développeurs choisissent le moteur pour chaque appel en fonction des caractéristiques du document. IronOCR utilise un seul moteur Tesseract 5 optimisé, mais expose des filtres de prétraitement explicites qui s'attaquent à la cause première — la qualité du document — plutôt que de basculer entre les modes du moteur.
Approche OCR.space :
// OCREngine parameter: binary choice, no control over why accuracy differs
public async Task<string> ExtractWithEngineSelection(
string imagePath,
bool useHighAccuracyEngine = true)
{
byte[] imageBytes = await File.ReadAllBytesAsync(imagePath);
string base64 = Convert.ToBase64String(imageBytes);
var formContent = 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"),
// Engine 1 = faster, Engine 2 = higher accuracy — binary choice only
new KeyValuePair<string, string>("OCREngine", useHighAccuracyEngine ? "2" : "1"),
new KeyValuePair<string, string>("scale", "true"),
new KeyValuePair<string, string>("detectOrientation", "true")
});
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", formContent);
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
return doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("ParsedText")
.GetString() ?? string.Empty;
}
// OCREngine parameter: binary choice, no control over why accuracy differs
public async Task<string> ExtractWithEngineSelection(
string imagePath,
bool useHighAccuracyEngine = true)
{
byte[] imageBytes = await File.ReadAllBytesAsync(imagePath);
string base64 = Convert.ToBase64String(imageBytes);
var formContent = 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"),
// Engine 1 = faster, Engine 2 = higher accuracy — binary choice only
new KeyValuePair<string, string>("OCREngine", useHighAccuracyEngine ? "2" : "1"),
new KeyValuePair<string, string>("scale", "true"),
new KeyValuePair<string, string>("detectOrientation", "true")
});
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", formContent);
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.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks
Public Class OCRService
Private _apiKey As String
Private _httpClient As HttpClient
Public Sub New(apiKey As String, httpClient As HttpClient)
_apiKey = apiKey
_httpClient = httpClient
End Sub
' OCREngine parameter: binary choice, no control over why accuracy differs
Public Async Function ExtractWithEngineSelection(imagePath As String, Optional useHighAccuracyEngine As Boolean = True) As Task(Of String)
Dim imageBytes As Byte() = Await File.ReadAllBytesAsync(imagePath)
Dim base64 As String = Convert.ToBase64String(imageBytes)
Dim formContent 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"),
' Engine 1 = faster, Engine 2 = higher accuracy — binary choice only
New KeyValuePair(Of String, String)("OCREngine", If(useHighAccuracyEngine, "2", "1")),
New KeyValuePair(Of String, String)("scale", "true"),
New KeyValuePair(Of String, String)("detectOrientation", "true")
})
Dim response As HttpResponseMessage = Await _httpClient.PostAsync("https://api.ocr.space/parse/image", formContent)
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
Approche IronOCR :
// Preprocessing pipeline: fix the document, not the engine selection
public string ExtractWithPreprocessing(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
// Apply filters that match the document's specific quality issues
input.Deskew(); // Correct rotation — replaces detectOrientation
input.DeNoise(); // Remove noise from fax/photocopier artifacts
input.Contrast(); // Enhance contrast on low-quality scans
input.Scale(200); // Upscale small or low-DPI images
var ocr = new IronTesseract();
var result = ocr.Read(input);
Console.WriteLine($"Confidence: {result.Confidence}%"); // Non equivalent in OCR.space
return result.Text;
}
// Preprocessing pipeline: fix the document, not the engine selection
public string ExtractWithPreprocessing(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
// Apply filters that match the document's specific quality issues
input.Deskew(); // Correct rotation — replaces detectOrientation
input.DeNoise(); // Remove noise from fax/photocopier artifacts
input.Contrast(); // Enhance contrast on low-quality scans
input.Scale(200); // Upscale small or low-DPI images
var ocr = new IronTesseract();
var result = ocr.Read(input);
Console.WriteLine($"Confidence: {result.Confidence}%"); // Non equivalent in OCR.space
return result.Text;
}
Imports IronOcr
Public Class PreprocessingPipeline
' Preprocessing pipeline: fix the document, not the engine selection
Public Function ExtractWithPreprocessing(imagePath As String) As String
Using input As New OcrInput()
input.LoadImage(imagePath)
' Apply filters that match the document's specific quality issues
input.Deskew() ' Correct rotation — replaces detectOrientation
input.DeNoise() ' Remove noise from fax/photocopier artifacts
input.Contrast() ' Enhance contrast on low-quality scans
input.Scale(200) ' Upscale small or low-DPI images
Dim ocr As New IronTesseract()
Dim result = ocr.Read(input)
Console.WriteLine($"Confidence: {result.Confidence}%") ' Non equivalent in OCR.space
Return result.Text
End Using
End Function
End Class
Le paramètre OCREngine d'OCR.space est un proxy pour la qualité du document — lorsque le moteur 1 échoue sur un document, les développeurs passent au moteur 2 en espérant que l'algorithme différent compense. Le pipeline de prétraitement d'IronOCR traite directement le problème de qualité : Deskew() corrige les numérisations inclinées, DeNoise() gère les artefacts de télécopie et Contrast() récupère le texte de photocopies à faible contraste. La propriété Confidence du résultat quantifie la qualité de l'extraction, ce que le basculement OCREngine ne peut fournir. Le guide de correction de qualité d'image et l'assistant de filtres documentent l'effet de chaque filtre sur différents types de documents.
OCR multilingue sans changement de langue par appel
OCR.space accepte un paramètre language par appel d'API. Les documents contenant plusieurs langues nécessitent des appels distincts pour chaque langue, les résultats étant fusionnés manuellement. IronOCR traite plusieurs langues simultanément dans une seule opération de lecture à l'aide de l'opérateur + sur les valeurs OcrLanguage.
Approche OCR.space :
// OCR.space: one language per call — multi-language requires multiple requests
public async Task<string> ExtractMultiLanguage(string imagePath)
{
// First pass: English
string englishText = await ExtractWithLanguage(imagePath, "eng");
// Second pass: French (consumes another rate-limit slot, another API call)
string frenchText = await ExtractWithLanguage(imagePath, "fre");
// Manually merge results — no way to know which text belongs to which language
return $"{englishText}\n{frenchText}";
}
private async Task<string> ExtractWithLanguage(string imagePath, string langCode)
{
byte[] imageBytes = await File.ReadAllBytesAsync(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", langCode) // One language per call
});
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
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: one language per call — multi-language requires multiple requests
public async Task<string> ExtractMultiLanguage(string imagePath)
{
// First pass: English
string englishText = await ExtractWithLanguage(imagePath, "eng");
// Second pass: French (consumes another rate-limit slot, another API call)
string frenchText = await ExtractWithLanguage(imagePath, "fre");
// Manually merge results — no way to know which text belongs to which language
return $"{englishText}\n{frenchText}";
}
private async Task<string> ExtractWithLanguage(string imagePath, string langCode)
{
byte[] imageBytes = await File.ReadAllBytesAsync(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", langCode) // One language per call
});
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
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.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks
Public Class OCRSpace
Private _apiKey As String
Private _httpClient As HttpClient
Public Async Function ExtractMultiLanguage(imagePath As String) As Task(Of String)
' First pass: English
Dim englishText As String = Await ExtractWithLanguage(imagePath, "eng")
' Second pass: French (consumes another rate-limit slot, another API call)
Dim frenchText As String = Await ExtractWithLanguage(imagePath, "fre")
' Manually merge results — no way to know which text belongs to which language
Return $"{englishText}{vbLf}{frenchText}"
End Function
Private Async Function ExtractWithLanguage(imagePath As String, langCode As String) As Task(Of String)
Dim imageBytes As Byte() = Await File.ReadAllBytesAsync(imagePath)
Dim base64 As String = Convert.ToBase64String(imageBytes)
Dim content = 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", langCode) ' One language per call
})
Dim response = Await _httpClient.PostAsync("https://api.ocr.space/parse/image", content)
Dim json As String = Await response.Content.ReadAsStringAsync()
Using doc = JsonDocument.Parse(json)
Return doc.RootElement _
.GetProperty("ParsedResults")(0) _
.GetProperty("ParsedText") _
.GetString() OrElse String.Empty
End Using
End Function
End Class
Approche IronOCR :
// IronOCR: multiple languages in a single read — one pass, correct output
public string ExtractMultiLanguage(string imagePath)
{
var ocr = new IronTesseract();
// Combine languages with + operator — processed simultaneously
ocr.Language = OcrLanguage.English + OcrLanguage.French + OcrLanguage.German;
var result = ocr.Read(imagePath);
return result.Text; // Correctly interleaved multilingual output
}
// IronOCR: multiple languages in a single read — one pass, correct output
public string ExtractMultiLanguage(string imagePath)
{
var ocr = new IronTesseract();
// Combine languages with + operator — processed simultaneously
ocr.Language = OcrLanguage.English + OcrLanguage.French + OcrLanguage.German;
var result = ocr.Read(imagePath);
return result.Text; // Correctly interleaved multilingual output
}
Imports IronOcr
Public Function ExtractMultiLanguage(imagePath As String) As String
Dim ocr As New IronTesseract()
' Combine languages with + operator — processed simultaneously
ocr.Language = OcrLanguage.English + OcrLanguage.French + OcrLanguage.German
Dim result = ocr.Read(imagePath)
Return result.Text ' Correctly interleaved multilingual output
End Function
La contrainte d'une seule langue par appel imposée par OCR.space oblige les développeurs à effectuer N appels API pour un document multilingue et à deviner comment concilier les résultats. IronOCR combine les modèles de langage en une seule passe de moteur, ce qui produit une sortie correctement entrelacée sans post-traitement. Les packs linguistiques s'installent en tant que packages NuGet — IronOcr.Languages.French, IronOcr.Languages.German, et ainsi de suite — et fonctionnent hors ligne. Le guide multi-langues couvre l'installation des packs et la syntaxe de l'opérateur + pour les 125+ langues prises en charge.
Extraction de données structurées à l'aide des coordonnées des mots
OCR.space renvoie du texte brut de ParsedResults[0].ParsedText. Il n'y a pas de données au niveau des mots, pas de cadres de délimitation, pas de limites de lignes et pas de scores de confiance par élément. Les applications qui doivent localiser des champs spécifiques (une date dans le coin supérieur droit d'une facture, un total dans la cellule inférieure droite d'un tableau) ne disposent d'aucune base structurée sur laquelle s'appuyer à partir de la réponse d'OCR.space. IronOCR fournit une hiérarchie complète du document : pages, paragraphes, lignes, mots et caractères, chacun avec ses coordonnées en pixels et son score de confiance.
Approche OCR.space :
// OCR.space: plain text only — no structure, no coordinates
public async Task<string> ExtractInvoiceFields(string invoicePath)
{
byte[] invoiceBytes = await File.ReadAllBytesAsync(invoicePath);
string base64 = Convert.ToBase64String(invoiceBytes);
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>("filetype", "PDF"),
new KeyValuePair<string, string>("language", "eng"),
// isOverlayRequired=true returns word boxes, but only as raw JSON coordinates
new KeyValuePair<string, string>("isOverlayRequired", "true")
});
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
string json = await response.Content.ReadAsStringAsync();
// Navigate deeply-nested JSON to find word boxes — no typed models
using var doc = JsonDocument.Parse(json);
var overlay = doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("TextOverlay");
// Parse word coordinate arrays manually — fragile JSON path traversal
var wordData = new List<(string word, int x, int y)>();
foreach (var line in overlay.GetProperty("Lines").EnumerateArray())
{
foreach (var word in line.GetProperty("Words").EnumerateArray())
{
string wordText = word.GetProperty("WordText").GetString() ?? "";
int left = word.GetProperty("Left").GetInt32();
int top = word.GetProperty("Top").GetInt32();
wordData.Add((wordText, left, top));
}
}
// Reconstruct full text from raw JSON — still no typed result
return string.Join(" ", wordData.Select(w => w.word));
}
// OCR.space: plain text only — no structure, no coordinates
public async Task<string> ExtractInvoiceFields(string invoicePath)
{
byte[] invoiceBytes = await File.ReadAllBytesAsync(invoicePath);
string base64 = Convert.ToBase64String(invoiceBytes);
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>("filetype", "PDF"),
new KeyValuePair<string, string>("language", "eng"),
// isOverlayRequired=true returns word boxes, but only as raw JSON coordinates
new KeyValuePair<string, string>("isOverlayRequired", "true")
});
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
string json = await response.Content.ReadAsStringAsync();
// Navigate deeply-nested JSON to find word boxes — no typed models
using var doc = JsonDocument.Parse(json);
var overlay = doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("TextOverlay");
// Parse word coordinate arrays manually — fragile JSON path traversal
var wordData = new List<(string word, int x, int y)>();
foreach (var line in overlay.GetProperty("Lines").EnumerateArray())
{
foreach (var word in line.GetProperty("Words").EnumerateArray())
{
string wordText = word.GetProperty("WordText").GetString() ?? "";
int left = word.GetProperty("Left").GetInt32();
int top = word.GetProperty("Top").GetInt32();
wordData.Add((wordText, left, top));
}
}
// Reconstruct full text from raw JSON — still no typed result
return string.Join(" ", wordData.Select(w => w.word));
}
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks
Public Class InvoiceProcessor
Private _apiKey As String
Private _httpClient As HttpClient
Public Sub New(apiKey As String, httpClient As HttpClient)
_apiKey = apiKey
_httpClient = httpClient
End Sub
Public Async Function ExtractInvoiceFields(invoicePath As String) As Task(Of String)
Dim invoiceBytes As Byte() = Await File.ReadAllBytesAsync(invoicePath)
Dim base64 As String = Convert.ToBase64String(invoiceBytes)
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)("filetype", "PDF"),
New KeyValuePair(Of String, String)("language", "eng"),
New KeyValuePair(Of String, String)("isOverlayRequired", "true")
})
Dim response As HttpResponseMessage = Await _httpClient.PostAsync("https://api.ocr.space/parse/image", content)
Dim json As String = Await response.Content.ReadAsStringAsync()
Using doc As JsonDocument = JsonDocument.Parse(json)
Dim overlay = doc.RootElement _
.GetProperty("ParsedResults")(0) _
.GetProperty("TextOverlay")
Dim wordData As New List(Of (word As String, x As Integer, y As Integer))()
For Each line In overlay.GetProperty("Lines").EnumerateArray()
For Each word In line.GetProperty("Words").EnumerateArray()
Dim wordText As String = word.GetProperty("WordText").GetString() OrElse ""
Dim left As Integer = word.GetProperty("Left").GetInt32()
Dim top As Integer = word.GetProperty("Top").GetInt32()
wordData.Add((wordText, left, top))
Next
Next
Return String.Join(" ", wordData.Select(Function(w) w.word))
End Using
End Function
End Class
Approche IronOCR :
// IronOCR: full document hierarchy — typed, no JSON, no coordinates parsing
public void ExtractInvoiceFields(string invoicePath)
{
var ocr = new IronTesseract();
var result = ocr.Read(invoicePath);
// Access the full document hierarchy — all strongly typed
foreach (var page in result.Pages)
{
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($"Paragraph at ({paragraph.X}, {paragraph.Y}): {paragraph.Text}");
}
foreach (var word in page.Words)
{
// Word-level confidence — identify low-quality extractions
if (word.Confidence < 70)
Console.WriteLine($"Low confidence word '{word.Text}' at ({word.X}, {word.Y})");
}
}
// Or use region-based OCR to target specific invoice zones directly
var totalRegion = new CropRectangle(400, 700, 200, 50); // Bottom-right total field
using var input = new OcrInput();
input.LoadImage(invoicePath, totalRegion);
string totalText = ocr.Read(input).Text;
Console.WriteLine($"Invoice total: {totalText}");
}
// IronOCR: full document hierarchy — typed, no JSON, no coordinates parsing
public void ExtractInvoiceFields(string invoicePath)
{
var ocr = new IronTesseract();
var result = ocr.Read(invoicePath);
// Access the full document hierarchy — all strongly typed
foreach (var page in result.Pages)
{
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($"Paragraph at ({paragraph.X}, {paragraph.Y}): {paragraph.Text}");
}
foreach (var word in page.Words)
{
// Word-level confidence — identify low-quality extractions
if (word.Confidence < 70)
Console.WriteLine($"Low confidence word '{word.Text}' at ({word.X}, {word.Y})");
}
}
// Or use region-based OCR to target specific invoice zones directly
var totalRegion = new CropRectangle(400, 700, 200, 50); // Bottom-right total field
using var input = new OcrInput();
input.LoadImage(invoicePath, totalRegion);
string totalText = ocr.Read(input).Text;
Console.WriteLine($"Invoice total: {totalText}");
}
Imports IronOcr
Public Sub ExtractInvoiceFields(invoicePath As String)
Dim ocr As New IronTesseract()
Dim result = ocr.Read(invoicePath)
' Access the full document hierarchy — all strongly typed
For Each page In result.Pages
For Each paragraph In page.Paragraphs
Console.WriteLine($"Paragraph at ({paragraph.X}, {paragraph.Y}): {paragraph.Text}")
Next
For Each word In page.Words
' Word-level confidence — identify low-quality extractions
If word.Confidence < 70 Then
Console.WriteLine($"Low confidence word '{word.Text}' at ({word.X}, {word.Y})")
End If
Next
Next
' Or use region-based OCR to target specific invoice zones directly
Dim totalRegion As New CropRectangle(400, 700, 200, 50) ' Bottom-right total field
Using input As New OcrInput()
input.LoadImage(invoicePath, totalRegion)
Dim totalText As String = ocr.Read(input).Text
Console.WriteLine($"Invoice total: {totalText}")
End Using
End Sub
Le drapeau isOverlayRequired=true d'OCR.space renvoie les coordonnées de mots JSON, mais la structure de réponse nécessite de naviguer dans des tableaux JSON imbriqués avec un accès aux propriétés par clé de chaîne — pas de modèle typé, pas d'IntelliSense, et un parcours de chemin fragile qui échoue si la structure de réponse change. Les result.Pages, result.Words, et result.Lines d'IronOCR sont des objets .NET typés. L'approche CropRectangle cible directement des régions spécifiques de documents plutôt que d'extraire le document complet et de filtrer par coordonnées ensuite. Le guide pratique sur les résultats de lecture et le guide OCR basé sur la région couvrent les deux modèles en détail.
Référence de mappage de l'API OCR.space vers IronOCR
| Concept OCR.space | Équivalent d'IronOCR |
|---|---|
| Aucun package NuGet | dotnet add package IronOcr |
HttpClient construction |
Inutile — aucune couche HTTP |
SemaphoreSlim limiteur de taux |
Inutile — aucune limite de débit |
FormUrlEncodedContent / MultipartFormDataContent |
OcrInput |
base64Image paramètre URI de données |
input.LoadImage(bytes) |
file paramètre de téléchargement |
input.LoadImage(path) |
apikey en-tête / champ de formulaire |
IronOcr.License.LicenseKey (une fois au démarrage) |
language paramètre (un par appel) |
ocr.Language = OcrLanguage.English + OcrLanguage.French |
OCREngine=1 (rapide) |
Moteur par défaut (Tesseract 5 optimisé) |
OCREngine=2 (haute précision) |
input.Deskew(); input.DeNoise(); input.Contrast(); |
scale=true paramètre |
input.Scale(200) |
detectOrientation=true paramètre |
input.Deskew() |
isOverlayRequired=true paramètre |
result.Pages[n].Words (toujours disponible, typé) |
isCreateSearchablePdf=true paramètre |
result.SaveAsSearchablePdf("output.pdf") |
filetype=PDF paramètre |
input.LoadPdf(path) |
ParsedResults[0].ParsedText |
result.Text |
ParsedResults[n] (texte par page) |
result.Pages[n].Text |
TextOverlay.Lines[n].Words[n].WordText |
result.Pages[n].Words[n].Text |
TextOverlay.Lines[n].Words[n].Left/Top |
result.Pages[n].Words[n].X / .Y |
IsErroredOnProcessing drapeau JSON |
Exception standard avec message |
FileParseExitCode drapeau par page |
Exception standard avec message |
| HTTP 429 Trop de requêtes | Sans objet — aucune limite de débit |
OcrResult POCO personnalisé (défini par l'utilisateur) |
IronOcr.OcrResult (fourni par NuGet) |
OcrSpaceException personnalisé (défini par l'utilisateur) |
types d'exceptions .NET standard |
Problèmes de migration courants et solutions
Problème 1 : Code asynchrone existant uniquement pour HTTP
OCR.space : Chaque appel OCR est async car il implique un aller-retour HTTP vers le cloud. Les méthodes de service, les actions du contrôleur et les tâches en arrière-plan ont été rendues asynchrones afin d'éviter de bloquer le thread lors de l'attente réseau.
Solution : La méthode Read() d'IronOCR est synchrone. Supprimez await des méthodes qui étaient asynchrones uniquement parce qu'OCR.space l'exigeait. Dans des contextes ASP.NET Core où l'exécution non bloquante est importante, encapsulez l'appel synchrone dans Task.Run() ou utilisez les modèles asynchrones documentés dans le guide OCR asynchrone. N'ajoutez pas de manière réflexe await aux appels IronOCR — ce n'est pas nécessaire et cela ajoute une surcharge inutile dans les contextes non web.
// Before: async because OCR.space required network I/O
public async Task<string> ProcessDocumentAsync(string path)
{
return await _ocrSpaceClient.ExtractTextAsync(path); // Network wait
}
// After: synchronous — no network, no async needed
public string ProcessDocument(string path)
{
return _ocr.Read(path).Text; // Local execution
}
// Before: async because OCR.space required network I/O
public async Task<string> ProcessDocumentAsync(string path)
{
return await _ocrSpaceClient.ExtractTextAsync(path); // Network wait
}
// After: synchronous — no network, no async needed
public string ProcessDocument(string path)
{
return _ocr.Read(path).Text; // Local execution
}
Imports System.Threading.Tasks
' Before: async because OCR.space required network I/O
Public Async Function ProcessDocumentAsync(path As String) As Task(Of String)
Return Await _ocrSpaceClient.ExtractTextAsync(path) ' Network wait
End Function
' After: synchronous — no network, no async needed
Public Function ProcessDocument(path As String) As String
Return _ocr.Read(path).Text ' Local execution
End Function
Problème 2 : Infrastructure de stockage et de rotation des clés API
OCR.space : La clé API doit être injectée dans chaque requête. Les équipes la stockent généralement dans appsettings.json ou des variables d'environnement, l'injectent via IOptions<t> ou l'injection de constructeur, et la remplacent lorsqu'elle est exposée. La rotation des clés nécessite la mise à jour de chaque environnement de déploiement et le redémarrage de l'application.
Solution : La clé de licence IronOCR est définie une seule fois au démarrage et n'est plus jamais utilisée pendant l'exécution. Supprimez le modèle d'injection de clé par requête. Supprimez la classe de configuration IOptions<OcrSpaceSettings>. Le modèle d'initialisation clé tient sur une seule ligne :
// Startup.cs or Program.cs — once only
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY");
// Startup.cs or Program.cs — once only
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY");
Imports System
' Startup.vb or Program.vb — once only
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
Il n'y a pas d'injection d'identifiants par requête, pas de procédure de rotation des clés et aucun risque d'enregistrement accidentel de la clé dans les traces de requêtes.
Problème 3 : Logique de prévalidation de la taille des fichiers
OCR.space : La version gratuite rejette les fichiers de plus de 5 Mo et renvoie une réponse d'erreur. Le code de production ajoute une vérification de la taille du fichier avant chaque requête afin d'éviter de gaspiller un emplacement de limite de débit sur un appel qui échouera :
// OCR.space: pre-validation to avoid wasting quota on large files
var fileInfo = new FileInfo(filePath);
if (fileInfo.Length > 5 * 1024 * 1024)
throw new InvalidOperationException("File exceeds 5MB free tier limit.");
// OCR.space: pre-validation to avoid wasting quota on large files
var fileInfo = new FileInfo(filePath);
if (fileInfo.Length > 5 * 1024 * 1024)
throw new InvalidOperationException("File exceeds 5MB free tier limit.");
Dim fileInfo As New FileInfo(filePath)
If fileInfo.Length > 5 * 1024 * 1024 Then
Throw New InvalidOperationException("File exceeds 5MB free tier limit.")
End If
Solution : Supprimez complètement cette case à cocher. Les OcrInput.LoadPdf() et OcrInput.LoadImage() d'IronOCR n'ont aucune limite de taille au-delà de la mémoire système disponible. Ce seuil artificiel de 5 Mo n'existe que parce que le niveau gratuit d'OCR.space l'impose pour des raisons de capacité serveur. Un fichier PDF numérisé de 50 Mo se charge de la même manière qu'un fichier de 500 Ko.
Problème 4 : Fragilité de la navigation dans les réponses JSON
OCR.space : L'analyse de la réponse repose sur la navigation JsonDocument avec un accès aux propriétés par clé de chaîne. Le code comme doc.RootElement.GetProperty("ParsedResults")[0].GetProperty("ParsedText") déclenche KeyNotFoundException si la forme de la réponse change et IndexOutOfRangeException si ParsedResults est vide. Les deux nécessitent des blocs try-catch ou des vérifications de valeur nulle tout au long du processus.
Solution : IronOCR renvoie un objet OcrResult typé. La propriété .Text est toujours string — jamais nulle, jamais manquante. Si l'OCR ne produit aucun résultat (page blanche, image illisible), result.Text est une chaîne vide. Il n'y a pas de JSON à naviguer et pas de fragilité de chemin de propriété à protéger. Pour le filtrage basé sur la confiance, result.Confidence renvoie double que vous comparez directement :
// IronOCR: typed result — no JSON path fragility
var result = new IronTesseract().Read("document.jpg");
if (result.Confidence < 50)
Console.WriteLine("Low confidence — consider preprocessing");
else
Console.WriteLine(result.Text);
// IronOCR: typed result — no JSON path fragility
var result = new IronTesseract().Read("document.jpg");
if (result.Confidence < 50)
Console.WriteLine("Low confidence — consider preprocessing");
else
Console.WriteLine(result.Text);
Imports IronOcr
Dim result = New IronTesseract().Read("document.jpg")
If result.Confidence < 50 Then
Console.WriteLine("Low confidence — consider preprocessing")
Else
Console.WriteLine(result.Text)
End If
Le guide pratique sur les scores de confiance aborde les seuils de confiance par mot et par document.
Problème n° 5 : Épuisement des limites de débit des adresses IP partagées dans l'intégration continue et le déploiement continu (CI/CD)
OCR.space : Les pipelines CI/CD qui exécutent des tests d'intégration sur OCR.space utilisent la même adresse IP sortante que le réseau du bureau de développement. Les comptes gratuits sont limités à 500 requêtes par jour et par adresse IP. Un pipeline traitant 200 documents de test par exécution peut épuiser ce quota journalier avant même qu'un développeur n'ait lancé un test manuel. Pour contourner ce problème, les équipes simulent les réponses d'OCR.space dans les tests, ce qui compromet l'objectif des tests d'intégration.
Solution : IronOCR traite les données localement. La suite de tests appelle new IronTesseract().Read(testImagePath).Text directement — pas de simulation requise, pas de quota à épuiser, pas de dépendance réseau. Les tests d'intégration sont exécutés en CI/CD avec les mêmes résultats OCR réels que la production, sans aucune gestion de limite de débit ni modèle d'isolation des tests.
Problème 6 : IDisposable Modèle de gestion HttpClient
OCR.space : La classe wrapper HttpClient implémente IDisposable pour libérer le pool de connexions HTTP. Tout consommateur du service OCR doit soit injecter un singleton, utiliser des blocs using, ou l'enregistrer avec le cycle de vie d'élimination du conteneur DI. Oublier de décharger les batteries peut entraîner une surchauffe des sockets sous charge.
Solution : IronTesseract ne gère pas les connexions réseau. Il n'implémente pas IDisposable. Créez une instance par thread (ou par demande dans ASP.NET), appelez .Read(), et laissez le GC la collecter. La classe OcrInput implémente IDisposable et doit être encapsulée dans des blocs using lors de l'application du prétraitement, mais la classe principale IronTesseract n'a besoin d'aucune gestion du cycle de vie. Supprimez l'implémentation IDisposable de votre wrapper de service OCR et simplifiez l'enregistrement DI de scoped/transient avec élimination à une simple fabrique ou singleton.
Liste de contrôle de migration OCR.space
Tâches préalables à la migration
Auditez le code source pour identifier tous les points d'intégration d'OCR.space :
# Find all OCR.space HTTP calls
grep -rn "ocr.space" --include="*.cs" .
# Find all base64 encoding related to OCR
grep -rn "base64Image\|Convert.ToBase64String" --include="*.cs" .
# Find rate limiter and retry logic
grep -rn "SemaphoreSlim\|TooManyRequests\|exponential" --include="*.cs" .
# Find JSON parsing for OCR responses
grep -rn "ParsedResults\|IsErroredOnProcessing\|FileParseExitCode" --include="*.cs" .
# Find custom OCR models and exception types
grep -rn "OcrSpaceException\|OcrResult\b" --include="*.cs" .
# Find API key configuration
grep -rn "apikey\|OcrSpaceApiKey\|ocr_space" --include="*.cs" --include="*.json" .
# Find all OCR.space HTTP calls
grep -rn "ocr.space" --include="*.cs" .
# Find all base64 encoding related to OCR
grep -rn "base64Image\|Convert.ToBase64String" --include="*.cs" .
# Find rate limiter and retry logic
grep -rn "SemaphoreSlim\|TooManyRequests\|exponential" --include="*.cs" .
# Find JSON parsing for OCR responses
grep -rn "ParsedResults\|IsErroredOnProcessing\|FileParseExitCode" --include="*.cs" .
# Find custom OCR models and exception types
grep -rn "OcrSpaceException\|OcrResult\b" --include="*.cs" .
# Find API key configuration
grep -rn "apikey\|OcrSpaceApiKey\|ocr_space" --include="*.cs" --include="*.json" .
Documentez la liste des fichiers contenant le code OCR.space. Notez quelles méthodes sont async uniquement à cause de la dépendance HTTP d'OCR.space — celles-ci peuvent être rendues synchrones après la migration.
Tâches de mise à jour du code
- Installez le package NuGet
IronOcr:dotnet add package IronOcr - Ajoutez
IronOcr.License.LicenseKey = "..."au démarrage de l'application - Supprimez la classe
OcrSpaceApiClientet toute l'infrastructure de support - Supprimez le POCO personnalisé
OcrResult(remplacé parIronOcr.OcrResult) - Supprimez la classe personnalisée
OcrSpaceException(remplacée par des exceptions .NET standard) - Supprimez le limiteur de taux
SemaphoreSlimet la logique associée Task.Delay - Supprimez tous les appels
Convert.ToBase64String()utilisés pour le codage des images OCR - Remplacez la construction
FormUrlEncodedContent/MultipartFormDataContentparOcrInput - Remplacez les appels
_httpClient.PostAsync(...)parnew IronTesseract().Read(input) - Remplacez l'analyse
JsonDocumentdeParsedResults[0].ParsedTextparresult.Text - Remplacez l'analyse des coordonnées JSON
TextOverlayparresult.Pages[n].Words - Remplacez le basculement des paramètres
OCREnginepar les filtres de prétraitement appropriés - Remplacez les chaînes de paramètres
languagepar des valeurs d'énumérationOcrLanguage - Suppression des contrôles de prévalidation de la taille des fichiers (la limite de 5 Mo n'est plus applicable)
- Convertissez les méthodes OCR
async Task<string>enstringsynchrones là où HTTP était la seule raison asynchrone - Supprimez la clé API OCR.space des fichiers de configuration et de la configuration des variables d'environnement.
Test de post-migration
- Vérifier que l'extraction de texte produit une précision équivalente ou supérieure sur les mêmes documents de test
- Confirmer le traitement des fichiers volumineux (plus de 5 Mo) sans erreur
- Testez les documents en plusieurs langues avec
OcrLanguage.English + OcrLanguage.Frenchet vérifiez le résultat entrelacé - Exécutez le pipeline CI/CD avec de véritables appels OCR — vérifiez l'absence d'erreurs de limitation de débit quel que soit le volume de documents.
- Vérifier que le PDF consultable ne comporte aucun filigrane
- Vérifiez que les actions du contrôleur précédemment asynchrones répondent toujours correctement après la conversion synchrone
- Vérifier que les environnements de déploiement isolés (sans connexion réseau) traitent les documents sans erreur.
- Confirmez que les valeurs
result.Confidencesont acceptables pour les documents qui nécessitaient auparavantOCREngine=2 - Vérifiez que les coordonnées
result.Pages[n].Wordscorrespondent aux positions de champ prévues dans les documents structurés - Vérifiez que l'initialisation de la licence de démarrage de l'application réussit avant le premier appel OCR
Principaux avantages de la migration vers IronOCR
La taxe sur l'infrastructure de plus de 80 lignes disparaît. Chaque intégration OCR.space inclut un client HTTP, un limiteur de débit, un désérialiseur JSON, des types d'exceptions personnalisés et des modèles de résultats personnalisés. Ce code n'apporte aucune fonctionnalité réellement nécessaire à l'application ; il existe uniquement pour pallier l'absence de SDK chez OCR.space. Après la migration, ce code est supprimé. La surface de l'OCR dans la base de code se réduit à new IronTesseract().Read(path).Text au site d'appel et une ligne d'initialisation de licence au démarrage.
La vitesse de traitement des documents dépend du matériel local. OCR.space intègre la latence réseau, la profondeur de la file d'attente du serveur OCR.space et le temps d'aller-retour géographique dans chaque opération de traitement. IronOCR s'exécute en interne. Un poste de travail local traite les documents plus rapidement que n'importe quelle API cloud, quel que soit le débit, sans la limite de 60 requêtes par minute qui sérialise le traitement par lots. Le traitement parallèle avec Parallel.ForEach à travers plusieurs instances IronTesseract évolue avec les cœurs de processeur — voir l'exemple de multithreading.
Les documents sensibles restent en permanence au sein de votre infrastructure. Après la migration, les dossiers médicaux, les documents financiers, les contrats juridiques et les pièces d'identité ne quittent jamais le serveur d'application. Les audits de conformité aux normes HIPAA, RGPD, SOC 2 et aux politiques internes de classification des données n'ont plus besoin d'inclure les pratiques de gestion des données d'OCR.space dans leur périmètre. La surface d'audit se réduit à votre propre infrastructure. Le guide de déploiement Docker et le guide de déploiement Azure couvrent le déploiement IronOCR dans des environnements conteneurisés et cloud qui exigent la conformité en matière de résidence des données.
La sortie structurée permet des applications d'intelligence documentaire. La chaîne ParsedText d'OCR.space est la fin du chemin pour l'analyse documentaire. Les result.Pages, result.Words et result.Lines d'IronOCR avec coordonnées et scores de confiance par mot permettent aux applications de localiser des champs spécifiques, valider la qualité de l'extraction, extraire des données de tableau, et construire des pipelines d'intelligence documentaire en aval. Les fonctionnalités qui nécessitaient auparavant la création d'une analyse de mise en page personnalisée à partir de la sortie en texte brut d'OCR.space deviennent des appels API directs. Le guide d'extraction de tableaux et le guide de traitement des documents numérisés démontrent ce que permet cette base structurée.
Le coût devient fixe et prévisible quel que soit le volume. L'offre gratuite d'OCR.space couvre 25 000 requêtes par mois. Au-delà, le coût de l'abonnement augmente en fonction de l'utilisation. La licence perpétuelle Lite d'IronOCR $999 ne comporte aucune charge par document à aucun volume. Une équipe traitant 100 000 documents par mois paie les mêmes frais de licence qu'une équipe traitant 1 000 documents par mois. Les prévisions budgétaires pour les applications de traitement de documents deviennent un coût annuel fixe plutôt qu'un poste de dépense variable qui augmente avec la réussite de l'entreprise. La page produit IronOCR propose un essai gratuit permettant aux équipes de valider la précision sur leurs types de documents spécifiques avant tout achat.
Questions Fréquemment Posées
Pourquoi devrais-je migrer de l'API OCR.space 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 l'API OCR.space vers IronOCR ?
Remplacer les séquences d'initialisation d'OCR.space 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 l'API OCR.space pour les documents commerciaux 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 l'API OCR.space 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 l'API OCR.space vers IronOCR nécessite-t-elle des changements au niveau de l'infrastructure de déploiement ?
IronOcr nécessite moins de changements d'infrastructure que l'API OCR.space. 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 d'OCR complet, et la clé de licence est une chaîne de caractères 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 qu'OCR.space ?
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 l'API OCR.space 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 l'API OCR.space 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.

