Umstellung von Azure Computer Vision OCR auf
Dieser Leitfaden führt .NET -Entwickler durch die Ersetzung von Azure Computer VisionOCRdurch IronOCR , eine lokale OCR-Bibliothek, die Dokumente lokal ohne Cloud-Infrastruktur verarbeitet. Es umfasst die mechanischen Schritte des Austauschs des NuGet Pakets und der Namespaces, die Übersetzung des Azure-Async-Polling-Modells in synchrone lokale Aufrufe und die Behandlung der spezifischen Muster – Dependency-Injection-Verkabelung, Formularerkennungsschleifen, mehrseitige TIFF-Verarbeitung und Batch-Durchsatz –, die während der Migration die größte Aufmerksamkeit erfordern.
Warum von Azure Computer VisionOCRmigrieren?
Die Argumente für Migration sind nicht abstrakt. Die Teams treffen diese Entscheidung in der Regel, nachdem sie im Produktivbetrieb auf ein oder mehrere konkrete operative Probleme mit Azure Computer Visiongestoßen sind.
Die Verwaltung von Endpunkten und API-Schlüsseln ist ein fortlaufender Prozess. Jede Bereitstellungsumgebung – Entwicklung, Staging, Produktion, Notfallwiederherstellung – erfordert eine bereitgestellte Azure Cognitive Services-Ressource, eine Endpunkt-URL und mindestens einen API-Schlüssel. Die Schlüssel müssen gedreht werden. Endpunkte ändern sich, wenn Ressourcen in andere Regionen verschoben werden. Jede Umgebung benötigt ausgehende Firewall-Regeln, um cognitiveservices.azure.com zu erreichen. Mit jeder Umgebung und jedem Entwickler im Team wächst auch der operative Bereich.IronOCR ersetzt all das durch einen einzigen Lizenzschlüssel, der einmalig beim Start der Anwendung festgelegt wird – ohne Rotationsplan und ohne ausgehende Netzwerkverbindung.
Die Abrechnung pro Seite benachteiligt mehrseitige Dokumente. Azure Computer Visionzählt jede PDF-Seite als separate Transaktion. Ein 20-seitiger Vertrag entspricht 20 abrechnungsfähigen Anrufen. Bei 1,00 US-Dollar pro 1.000 Transaktionen generiert ein Team, das 50.000 mehrseitige Dokumente pro Monat mit durchschnittlich jeweils 4 Seiten verarbeitet, 200.000 Transaktionen – 195 US-Dollar pro Monat nach Ablauf des kostenlosen Kontingents, 2.340 US-Dollar pro Jahr. Das ist der Break-even-Punkt gegen IronOCR Lite ($999) in weniger als vier Monaten, danach kostet jede zusätzliche Seite nichts.
Die asynchrone Ausführung breitet sich durch den gesamten Aufrufstapel aus. Azure Computer Visionkann nicht synchron zurückkehren – Cloud-E/A weist Netzwerklatenz auf. Die Anforderung await bei AnalyzeAsync zwingt jede aufrufende Methode dazu, asynchron zu sein, wodurch das Muster von der Service-Schicht bis hin zu den Controllern, Hintergrundarbeitern und jedem synchronen Code, der umstrukturiert werden muss, um dem gerecht zu werden, weitergegeben wird. Die polling-basierten Operationen des Formularerkennungsgeräts verstärken dies: WaitUntil.Completed blockiert den Thread, und echtes nicht-blockierendes Verhalten erfordert das manuelle Verwalten von UpdateStatusAsync-Polling-Schleifen.
Bei jedem Anruf verlassen Dokumente das Netzwerk. Für Teams, die HIPAA-geschützte Gesundheitsdaten, ITAR-kontrollierte Verteidigungsdokumente, vertrauliche Anwalts-Mandanten-Kommunikation oder Dokumente jeglicher Art, die den Regeln zur Datenspeicherung unterliegen, verarbeiten, stellt die obligatorische Cloud-Übertragung eine architektonische Inkompatibilität und keinen Kompromiss dar. Es gibt keinen Azure Computer Vision-Modus, der die Übertragung von Dokumentinhalten an Microsoft-Rechenzentren vermeidet.
Ratenbegrenzungen führen zu Durchsatzobergrenzen. Die S1-Stufe von Azure Computer Visionist auf 10 Transaktionen pro Sekunde begrenzt. Ein Stapelverarbeitungsauftrag, der 3.600 Bilder pro Stunde verarbeitet, stößt exakt an die Obergrenze. Wird dieser Wert überschritten, werden HTTP-429-Antworten zurückgegeben, was eine Wiederholungslogik mit exponentiellem Backoff in jedem Aufrufpfad erfordert. Die Durchsatzgrenze von IronOCR ist die der Host-Hardware – es gibt keine vom Dienst auferlegte Begrenzung, es ist keine Wiederholungsinfrastruktur erforderlich.
Bild-OCR und PDF-OCR erfordern zwei separate Dienste. Standardbild-OCR verwendet ImageAnalysisClient von Azure.AI.Vision.ImageAnalysis. Vollständige PDF-Verarbeitung erfordert DocumentAnalysisClient von Azure.AI.FormRecognizer.DocumentAnalysis — ein anderes NuGet-Paket, eine andere Azure-Ressource, einen anderen Endpunkt und ein anderes Ergebnisschema. Jede Anwendung, die sowohl Bilder als auch PDFs verarbeitet, bringt diesen doppelten Konfigurationsaufwand mit sich.IronOCR verarbeitet beides mit IronTesseract.Read() und einem einzigen OcrInput-Loader.
Das grundsätzliche Problem
// Azure: endpoint URL + API key + async + nested block traversal — before a single character
var client = new ImageAnalysisClient(new Uri(endpoint), new AzureKeyCredential(apiKey));
using var stream = File.OpenRead(imagePath);
var result = await client.AnalyzeAsync(BinaryData.FromStream(stream), VisualFeatures.Read);
var text = string.Join("\n", result.Value.Read.Blocks.SelectMany(b => b.Lines).Select(l => l.Text));
// IronOCR: no endpoint, no key rotation, no async, no traversal
var text = new IronTesseract().Read(imagePath).Text;
// Azure: endpoint URL + API key + async + nested block traversal — before a single character
var client = new ImageAnalysisClient(new Uri(endpoint), new AzureKeyCredential(apiKey));
using var stream = File.OpenRead(imagePath);
var result = await client.AnalyzeAsync(BinaryData.FromStream(stream), VisualFeatures.Read);
var text = string.Join("\n", result.Value.Read.Blocks.SelectMany(b => b.Lines).Select(l => l.Text));
// IronOCR: no endpoint, no key rotation, no async, no traversal
var text = new IronTesseract().Read(imagePath).Text;
Imports System
Imports System.IO
Imports Azure
Imports Azure.AI.Vision
Imports IronOcr
' Azure: endpoint URL + API key + async + nested block traversal — before a single character
Dim client As New ImageAnalysisClient(New Uri(endpoint), New AzureKeyCredential(apiKey))
Using stream As FileStream = File.OpenRead(imagePath)
Dim result = Await client.AnalyzeAsync(BinaryData.FromStream(stream), VisualFeatures.Read)
Dim text As String = String.Join(vbLf, result.Value.Read.Blocks.SelectMany(Function(b) b.Lines).Select(Function(l) l.Text))
End Using
' IronOCR: no endpoint, no key rotation, no async, no traversal
Dim text As String = New IronTesseract().Read(imagePath).Text
IronOCR vs. Azure Computer VisionOCR: Funktionsvergleich
Die folgende Tabelle beschreibt die wichtigsten Funktionen für Teams, die diese Migration bewerten.
| Feature | Azure Computer VisionOCR | IronOCR |
|---|---|---|
| Verarbeitungsort | Microsoft Azure Cloud | Lokal, vor Ort |
| Internet erforderlich | Ja, jede Anfrage | Nein |
| Azure-Abonnement erforderlich | Ja | Nein |
| Preismodell | Pro Transaktion (1,00 $ pro 1.000) | Unbefristete Lizenz (von $999) |
| Abrechnung pro Seite bei mehrseitigen PDFs | Ja – jede Seite = 1 Transaktion | Keine Kosten pro Seite |
| Kostenloses Tarif | 5.000 Transaktionen/Monat | Testmodus (mit Wasserzeichen) |
| Bild-OCR-API | AnalyzeAsync (nur asynchron) |
Read() (synchron) |
| PDF-OCR | Separater Formularerkennungsdienst | Eingebaut, gleicher Read()-Aufruf |
| Passwortgeschütztes PDF | Über Formularerkennung | input.LoadPdf(path, Password: "x") |
| Durchsuchbare PDF-Ausgabe | Manuelle Konstruktion | result.SaveAsSearchablePdf() |
| Mehrseitiges TIFF | Nicht unterstützt | input.LoadImageFrames() |
| Automatische Bildvorverarbeitung | Undurchsichtige Serverseite, nicht konfigurierbar | Entzerren, Rauschen entfernen, Kontrast erhöhen, Binärisierung, Schärfen, Skalieren |
| Tiefengeräuschentfernung | Nein | input.DeepCleanBackgroundNoise() |
| Barcode-Lesung während der OCR | Separate Bildanalysefunktion | ocr.Configuration.ReadBarCodes = true |
| Regionsbasierte OCR | Nicht direkt (manuelles Zuschneiden vor dem Hochladen) | CropRectangle auf OcrInput |
| Ratenbegrenzungen | 10 TPS auf S1-Tier | Nur hardwaregebunden |
| Wiederholungslogik erforderlich | Ja (HTTP 429, 5xx) | Nein |
| Air-Gapped-Bereitstellung | Unmöglich | Vollständig unterstützt |
| Unterstützte Sprachen | 164+ (serververwaltet) | 125+ (NuGet Sprachpakete) |
| Mehrsprachige Simultanübertragung | Ja | Ja (OcrLanguage.French + OcrLanguage.German) |
| Wortbegrenzungsrahmen | Polygon (variable Eckpunktanzahl) | Rechteck (x, y, Breite, Höhe) |
| Konfidenzbewertung | Gleitkommazahl pro Wort (0,0–1,0) | Pro Wort und insgesamt (Skala 0–100) |
| hOCR-Export | Nein | result.SaveAsHocrFile() |
| Strukturierte Ausgabehierarchie | Blöcke / Linien / Wörter | Seiten / Absätze / Zeilen / Wörter / Zeichen |
| .NET -Kompatibilität | .NET Standard 2.0+ | .NET Framework 4.6.2+, .NET Core, .NET 5-9 |
| Plattformübergreifend | Windows, Linux, macOS (via Cloud) | Windows, Linux, macOS, Docker, ARM64 |
| Kommerzielle Unterstützung | Azure-Supportpläne | IronOCR Unterstützung ist in der Lizenz enthalten. |
Schnellstart: Migration von Azure Computer VisionOCRzu IronOCR
Schritt 1: Ersetzen des NuGet-Pakets
Entfernen Sie das Azure Computer Vision-Paket:
dotnet remove package Azure.AI.Vision.ImageAnalysis
dotnet remove package Azure.AI.Vision.ImageAnalysis
Falls das Projekt auch Form Recognizer zur PDF-Verarbeitung verwendet, entfernen Sie dieses Paket ebenfalls:
dotnet remove package Azure.AI.FormRecognizer
dotnet remove package Azure.AI.FormRecognizer
Installieren Sie IronOCR über NuGet :
dotnet add package IronOcr
Schritt 2: Namespaces aktualisieren
// Before (Azure Computer Vision)
using Azure;
using Azure.AI.Vision.ImageAnalysis;
// For PDF processing:
// using Azure.AI.FormRecognizer.DocumentAnalysis;
// After (IronOCR)
using IronOcr;
// Before (Azure Computer Vision)
using Azure;
using Azure.AI.Vision.ImageAnalysis;
// For PDF processing:
// using Azure.AI.FormRecognizer.DocumentAnalysis;
// After (IronOCR)
using IronOcr;
Imports IronOcr
' Before (Azure Computer Vision)
' Imports Azure
' Imports Azure.AI.Vision.ImageAnalysis
' For PDF processing:
' Imports Azure.AI.FormRecognizer.DocumentAnalysis
' After (IronOCR)
Schritt 3: Lizenz initialisieren
Fügen Sie den Lizenzschlüssel einmalig beim Start der Anwendung hinzu, bevor ein OCR-Aufruf erfolgt:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Speichern Sie den Schlüssel in einer Umgebungsvariablen in der Produktionsumgebung:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
Imports System
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
Beispiele für die Code-Migration
Ersetzen der Dependency-Injection-Azure-Clientkonfiguration
Teams, die dem empfohlenen Azure SDK-Muster folgen, registrieren ImageAnalysisClient im DI-Container unter Verwendung von IOptions<AzureComputerVisionOptions> oder direkter IConfiguration-Bindung. Diese Verkabelung zieht Endpunkt-URLs und API-Schlüssel von appsettings.json ab und erfordert eine ausgehende Netzwerkkonfiguration in jeder Einsatzumgebung.
Azure Computer Vision Ansatz:
// appsettings.json binds to this class
public class AzureComputerVisionOptions
{
public string Endpoint { get; set; } // "https://your-resource.cognitiveservices.azure.com/"
public string ApiKey { get; set; } // rotated periodically
}
// Program.cs / Startup.cs
services.Configure<AzureComputerVisionOptions>(
configuration.GetSection("AzureComputerVision"));
services.AddSingleton<ImageAnalysisClient>(sp =>
{
var opts = sp.GetRequiredService<IOptions<AzureComputerVisionOptions>>().Value;
return new ImageAnalysisClient(
new Uri(opts.Endpoint),
new AzureKeyCredential(opts.ApiKey));
});
services.AddScoped<IOcrService, AzureOcrService>();
// appsettings.json binds to this class
public class AzureComputerVisionOptions
{
public string Endpoint { get; set; } // "https://your-resource.cognitiveservices.azure.com/"
public string ApiKey { get; set; } // rotated periodically
}
// Program.cs / Startup.cs
services.Configure<AzureComputerVisionOptions>(
configuration.GetSection("AzureComputerVision"));
services.AddSingleton<ImageAnalysisClient>(sp =>
{
var opts = sp.GetRequiredService<IOptions<AzureComputerVisionOptions>>().Value;
return new ImageAnalysisClient(
new Uri(opts.Endpoint),
new AzureKeyCredential(opts.ApiKey));
});
services.AddScoped<IOcrService, AzureOcrService>();
' appsettings.json binds to this class
Public Class AzureComputerVisionOptions
Public Property Endpoint As String ' "https://your-resource.cognitiveservices.azure.com/"
Public Property ApiKey As String ' rotated periodically
End Class
' Program.vb / Startup.vb
services.Configure(Of AzureComputerVisionOptions)(
configuration.GetSection("AzureComputerVision"))
services.AddSingleton(Of ImageAnalysisClient)(Function(sp)
Dim opts = sp.GetRequiredService(Of IOptions(Of AzureComputerVisionOptions))().Value
Return New ImageAnalysisClient(
New Uri(opts.Endpoint),
New AzureKeyCredential(opts.ApiKey))
End Function)
services.AddScoped(Of IOcrService, AzureOcrService)()
// AzureOcrService.cs
public class AzureOcrService : IOcrService
{
private readonly ImageAnalysisClient _client;
public AzureOcrService(ImageAnalysisClient client)
{
_client = client;
}
public async Task<string> ReadAsync(string imagePath)
{
using var stream = File.OpenRead(imagePath);
var data = BinaryData.FromStream(stream);
var result = await _client.AnalyzeAsync(data, VisualFeatures.Read);
return string.Join("\n",
result.Value.Read.Blocks
.SelectMany(b => b.Lines)
.Select(l => l.Text));
}
}
// AzureOcrService.cs
public class AzureOcrService : IOcrService
{
private readonly ImageAnalysisClient _client;
public AzureOcrService(ImageAnalysisClient client)
{
_client = client;
}
public async Task<string> ReadAsync(string imagePath)
{
using var stream = File.OpenRead(imagePath);
var data = BinaryData.FromStream(stream);
var result = await _client.AnalyzeAsync(data, VisualFeatures.Read);
return string.Join("\n",
result.Value.Read.Blocks
.SelectMany(b => b.Lines)
.Select(l => l.Text));
}
}
Imports System.IO
Imports System.Threading.Tasks
Public Class AzureOcrService
Implements IOcrService
Private ReadOnly _client As ImageAnalysisClient
Public Sub New(client As ImageAnalysisClient)
_client = client
End Sub
Public Async Function ReadAsync(imagePath As String) As Task(Of String) Implements IOcrService.ReadAsync
Using stream = File.OpenRead(imagePath)
Dim data = BinaryData.FromStream(stream)
Dim result = Await _client.AnalyzeAsync(data, VisualFeatures.Read)
Return String.Join(vbLf, result.Value.Read.Blocks _
.SelectMany(Function(b) b.Lines) _
.Select(Function(l) l.Text))
End Using
End Function
End Class
IronOCR Ansatz:
// Program.cs / Startup.cs
// One-time license key — no endpoint, no credential class, no options binding
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
// Register IronTesseract as a singleton — it is thread-safe
services.AddSingleton<IronTesseract>();
services.AddScoped<IOcrService, IronOcrService>();
// Program.cs / Startup.cs
// One-time license key — no endpoint, no credential class, no options binding
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
// Register IronTesseract as a singleton — it is thread-safe
services.AddSingleton<IronTesseract>();
services.AddScoped<IOcrService, IronOcrService>();
' Program.vb / Startup.vb
' One-time license key — no endpoint, no credential class, no options binding
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
' Register IronTesseract as a singleton — it is thread-safe
services.AddSingleton(Of IronTesseract)()
services.AddScoped(Of IOcrService, IronOcrService)()
// IronOcrService.cs
public class IronOcrService : IOcrService
{
private readonly IronTesseract _ocr;
public IronOcrService(IronTesseract ocr)
{
_ocr = ocr;
}
public string Read(string imagePath)
{
return _ocr.Read(imagePath).Text;
}
}
// IronOcrService.cs
public class IronOcrService : IOcrService
{
private readonly IronTesseract _ocr;
public IronOcrService(IronTesseract ocr)
{
_ocr = ocr;
}
public string Read(string imagePath)
{
return _ocr.Read(imagePath).Text;
}
}
' IronOcrService.vb
Public Class IronOcrService
Implements IOcrService
Private ReadOnly _ocr As IronTesseract
Public Sub New(ocr As IronTesseract)
_ocr = ocr
End Sub
Public Function Read(imagePath As String) As String Implements IOcrService.Read
Return _ocr.Read(imagePath).Text
End Function
End Class
Die DI-Verkabelung reduziert von zwei Konfigurationsklassen (Optionen + Clientfabrik) auf einen einzigen AddSingleton<IronTesseract>()-Aufruf. Der appsettings.json-Azure-Abschnitt, Schlüsselbundverweise und ausgehende Firewall-Regeln für cognitiveservices.azure.com werden alle entfernt. Die Konfigurationsoptionen für die Singleton-Instanz finden Sie im IronTesseract-Setup-Leitfaden .
Eliminierung der Formularerkennungs-Abfrageschleife
Die AnalyzeDocumentAsync des Formularerkennungsgeräts gibt eine LongRunningOperation zurück. WaitUntil.Completed blockiert den aufrufenden Thread, bis der Cloud-Job abgeschlossen ist — typischerweise 2–10 Sekunden pro Dokument. Für nicht-blockierendes Verhalten schreiben Teams eine UpdateStatusAsync-Polling-Schleife mit einer Verzögerung zwischen den Polls und fügen 30–50 Zeilen Infrastrukturecode hinzu, die keine OCR-Logik enthalten.
Azure Computer Vision Ansatz:
// DocumentAnalysisClient — separate from ImageAnalysisClient, separate resource
public class FormRecognizerPdfService
{
private readonly DocumentAnalysisClient _client;
public FormRecognizerPdfService(string endpoint, string apiKey)
{
_client = new DocumentAnalysisClient(
new Uri(endpoint),
new AzureKeyCredential(apiKey));
}
// Blocking wait — thread is held for the duration of cloud processing
public async Task<string> ExtractPdfTextBlocking(string pdfPath)
{
using var stream = File.OpenRead(pdfPath);
var operation = await _client.AnalyzeDocumentAsync(
WaitUntil.Completed, // blocks until Azure finishes
"prebuilt-read",
stream);
var docResult = operation.Value;
var sb = new StringBuilder();
foreach (var page in docResult.Pages)
{
foreach (var line in page.Lines)
{
sb.AppendLine(line.Content); // .Content, not .Text
}
}
return sb.ToString();
}
// True async — manual polling loop required
public async Task<string> ExtractPdfTextNonBlocking(string pdfPath)
{
using var stream = File.OpenRead(pdfPath);
var operation = await _client.AnalyzeDocumentAsync(
WaitUntil.Started, // returns immediately, not complete yet
"prebuilt-read",
stream);
// Poll every 500ms until the operation finishes
while (!operation.HasCompleted)
{
await Task.Delay(500);
await operation.UpdateStatusAsync();
}
var docResult = operation.Value;
var sb = new StringBuilder();
foreach (var page in docResult.Pages)
{
foreach (var line in page.Lines)
{
sb.AppendLine(line.Content);
}
}
return sb.ToString();
}
}
// DocumentAnalysisClient — separate from ImageAnalysisClient, separate resource
public class FormRecognizerPdfService
{
private readonly DocumentAnalysisClient _client;
public FormRecognizerPdfService(string endpoint, string apiKey)
{
_client = new DocumentAnalysisClient(
new Uri(endpoint),
new AzureKeyCredential(apiKey));
}
// Blocking wait — thread is held for the duration of cloud processing
public async Task<string> ExtractPdfTextBlocking(string pdfPath)
{
using var stream = File.OpenRead(pdfPath);
var operation = await _client.AnalyzeDocumentAsync(
WaitUntil.Completed, // blocks until Azure finishes
"prebuilt-read",
stream);
var docResult = operation.Value;
var sb = new StringBuilder();
foreach (var page in docResult.Pages)
{
foreach (var line in page.Lines)
{
sb.AppendLine(line.Content); // .Content, not .Text
}
}
return sb.ToString();
}
// True async — manual polling loop required
public async Task<string> ExtractPdfTextNonBlocking(string pdfPath)
{
using var stream = File.OpenRead(pdfPath);
var operation = await _client.AnalyzeDocumentAsync(
WaitUntil.Started, // returns immediately, not complete yet
"prebuilt-read",
stream);
// Poll every 500ms until the operation finishes
while (!operation.HasCompleted)
{
await Task.Delay(500);
await operation.UpdateStatusAsync();
}
var docResult = operation.Value;
var sb = new StringBuilder();
foreach (var page in docResult.Pages)
{
foreach (var line in page.Lines)
{
sb.AppendLine(line.Content);
}
}
return sb.ToString();
}
}
Imports System
Imports System.IO
Imports System.Text
Imports System.Threading.Tasks
Imports Azure
Imports Azure.AI.FormRecognizer.DocumentAnalysis
' DocumentAnalysisClient — separate from ImageAnalysisClient, separate resource
Public Class FormRecognizerPdfService
Private ReadOnly _client As DocumentAnalysisClient
Public Sub New(endpoint As String, apiKey As String)
_client = New DocumentAnalysisClient(
New Uri(endpoint),
New AzureKeyCredential(apiKey))
End Sub
' Blocking wait — thread is held for the duration of cloud processing
Public Async Function ExtractPdfTextBlocking(pdfPath As String) As Task(Of String)
Using stream = File.OpenRead(pdfPath)
Dim operation = Await _client.AnalyzeDocumentAsync(
WaitUntil.Completed, ' blocks until Azure finishes
"prebuilt-read",
stream)
Dim docResult = operation.Value
Dim sb = New StringBuilder()
For Each page In docResult.Pages
For Each line In page.Lines
sb.AppendLine(line.Content) ' .Content, not .Text
Next
Next
Return sb.ToString()
End Using
End Function
' True async — manual polling loop required
Public Async Function ExtractPdfTextNonBlocking(pdfPath As String) As Task(Of String)
Using stream = File.OpenRead(pdfPath)
Dim operation = Await _client.AnalyzeDocumentAsync(
WaitUntil.Started, ' returns immediately, not complete yet
"prebuilt-read",
stream)
' Poll every 500ms until the operation finishes
While Not operation.HasCompleted
Await Task.Delay(500)
Await operation.UpdateStatusAsync()
End While
Dim docResult = operation.Value
Dim sb = New StringBuilder()
For Each page In docResult.Pages
For Each line In page.Lines
sb.AppendLine(line.Content)
Next
Next
Return sb.ToString()
End Using
End Function
End Class
IronOCR Ansatz:
// One class handles both images and PDFs — no second client or second resource
public class IronOcrDocumentService
{
private readonly IronTesseract _ocr;
public IronOcrDocumentService(IronTesseract ocr)
{
_ocr = ocr;
}
// Synchronous — returns immediately when local processing completes
public string ExtractPdfText(string pdfPath)
{
using var input = new OcrInput();
input.LoadPdf(pdfPath);
return _ocr.Read(input).Text;
}
// Specific page range — no per-page billing penalty
public string ExtractPageRange(string pdfPath, int startPage, int endPage)
{
using var input = new OcrInput();
input.LoadPdfPages(pdfPath, startPage, endPage);
return _ocr.Read(input).Text;
}
// If an async signature is required by an interface or controller
public Task<string> ExtractPdfTextAsync(string pdfPath)
{
return Task.Run(() => ExtractPdfText(pdfPath));
}
}
// One class handles both images and PDFs — no second client or second resource
public class IronOcrDocumentService
{
private readonly IronTesseract _ocr;
public IronOcrDocumentService(IronTesseract ocr)
{
_ocr = ocr;
}
// Synchronous — returns immediately when local processing completes
public string ExtractPdfText(string pdfPath)
{
using var input = new OcrInput();
input.LoadPdf(pdfPath);
return _ocr.Read(input).Text;
}
// Specific page range — no per-page billing penalty
public string ExtractPageRange(string pdfPath, int startPage, int endPage)
{
using var input = new OcrInput();
input.LoadPdfPages(pdfPath, startPage, endPage);
return _ocr.Read(input).Text;
}
// If an async signature is required by an interface or controller
public Task<string> ExtractPdfTextAsync(string pdfPath)
{
return Task.Run(() => ExtractPdfText(pdfPath));
}
}
Imports System.Threading.Tasks
' One class handles both images and PDFs — no second client or second resource
Public Class IronOcrDocumentService
Private ReadOnly _ocr As IronTesseract
Public Sub New(ocr As IronTesseract)
_ocr = ocr
End Sub
' Synchronous — returns immediately when local processing completes
Public Function ExtractPdfText(pdfPath As String) As String
Using input As New OcrInput()
input.LoadPdf(pdfPath)
Return _ocr.Read(input).Text
End Using
End Function
' Specific page range — no per-page billing penalty
Public Function ExtractPageRange(pdfPath As String, startPage As Integer, endPage As Integer) As String
Using input As New OcrInput()
input.LoadPdfPages(pdfPath, startPage, endPage)
Return _ocr.Read(input).Text
End Using
End Function
' If an async signature is required by an interface or controller
Public Function ExtractPdfTextAsync(pdfPath As String) As Task(Of String)
Return Task.Run(Function() ExtractPdfText(pdfPath))
End Function
End Class
Die Abfrageschleife und ihre Verzögerungslogik verschwinden vollständig. LoadPdfPages behandelt die Seitenauswahl — kein separater Aufruf pro Seite, keine Transaktionszählung. Der PDF-Eingabeleitfaden behandelt Stream-Eingabe, Byte-Array-Laden und Seitenbereichsparameter im Detail.
Zuordnung von Azure-Ergebnissen auf Wortebene zu strukturierter IronOCR Ausgabe
Azure Computer Vision gibt Wortbegrenzungsrahmen als Polygone mit einer variablen Anzahl von Eckpunkten zurück – typischerweise vier, aber nicht garantiert. Die Ergebnishierarchie ist Blocks → Lines → Words und das Vertrauen ist eine float auf einer Skala von 0,0–1,0. Code, der Wortpositionen liest, muss das Polygon-Eckpunkt-Array verarbeiten und die Konfidenzskala für alle Schwellenwertvergleiche normalisieren.
Azure Computer Vision Ansatz:
public async Task<List<WordResult>> ExtractWordPositionsAsync(string imagePath)
{
using var stream = File.OpenRead(imagePath);
var imageData = BinaryData.FromStream(stream);
var response = await _client.AnalyzeAsync(imageData, VisualFeatures.Read);
var words = new List<WordResult>();
foreach (var block in response.Value.Read.Blocks)
{
foreach (var line in block.Lines)
{
foreach (var word in line.Words)
{
// BoundingPolygon is a list of ImagePoint — variable vertex count
var polygon = word.BoundingPolygon;
int minX = polygon.Min(p => p.X);
int minY = polygon.Min(p => p.Y);
int maxX = polygon.Max(p => p.X);
int maxY = polygon.Max(p => p.Y);
words.Add(new WordResult
{
Text = word.Text,
// Azure confidence: 0.0 to 1.0 — multiply by 100 for comparison
Confidence = (double)word.Confidence * 100.0,
X = minX,
Y = minY,
Width = maxX - minX,
Height = maxY - minY
});
}
}
}
return words;
}
public record WordResult(string Text, double Confidence, int X, int Y, int Width, int Height);
public async Task<List<WordResult>> ExtractWordPositionsAsync(string imagePath)
{
using var stream = File.OpenRead(imagePath);
var imageData = BinaryData.FromStream(stream);
var response = await _client.AnalyzeAsync(imageData, VisualFeatures.Read);
var words = new List<WordResult>();
foreach (var block in response.Value.Read.Blocks)
{
foreach (var line in block.Lines)
{
foreach (var word in line.Words)
{
// BoundingPolygon is a list of ImagePoint — variable vertex count
var polygon = word.BoundingPolygon;
int minX = polygon.Min(p => p.X);
int minY = polygon.Min(p => p.Y);
int maxX = polygon.Max(p => p.X);
int maxY = polygon.Max(p => p.Y);
words.Add(new WordResult
{
Text = word.Text,
// Azure confidence: 0.0 to 1.0 — multiply by 100 for comparison
Confidence = (double)word.Confidence * 100.0,
X = minX,
Y = minY,
Width = maxX - minX,
Height = maxY - minY
});
}
}
}
return words;
}
public record WordResult(string Text, double Confidence, int X, int Y, int Width, int Height);
Imports System.IO
Imports System.Threading.Tasks
Imports System.Linq
Public Class ImageAnalyzer
Private _client As SomeClientType ' Replace with the actual type of _client
Public Async Function ExtractWordPositionsAsync(imagePath As String) As Task(Of List(Of WordResult))
Using stream = File.OpenRead(imagePath)
Dim imageData = BinaryData.FromStream(stream)
Dim response = Await _client.AnalyzeAsync(imageData, VisualFeatures.Read)
Dim words = New List(Of WordResult)()
For Each block In response.Value.Read.Blocks
For Each line In block.Lines
For Each word In line.Words
' BoundingPolygon is a list of ImagePoint — variable vertex count
Dim polygon = word.BoundingPolygon
Dim minX = polygon.Min(Function(p) p.X)
Dim minY = polygon.Min(Function(p) p.Y)
Dim maxX = polygon.Max(Function(p) p.X)
Dim maxY = polygon.Max(Function(p) p.Y)
words.Add(New WordResult With {
.Text = word.Text,
' Azure confidence: 0.0 to 1.0 — multiply by 100 for comparison
.Confidence = CDbl(word.Confidence) * 100.0,
.X = minX,
.Y = minY,
.Width = maxX - minX,
.Height = maxY - minY
})
Next
Next
Next
Return words
End Using
End Function
End Class
Public Class WordResult
Public Property Text As String
Public Property Confidence As Double
Public Property X As Integer
Public Property Y As Integer
Public Property Width As Integer
Public Property Height As Integer
End Class
IronOCR Ansatz:
public List<WordResult> ExtractWordPositions(string imagePath)
{
var result = new IronTesseract().Read(imagePath);
var words = new List<WordResult>();
foreach (var page in result.Pages)
{
foreach (var line in page.Lines)
{
foreach (var word in line.Words)
{
// Rectangle-based bounding box — no polygon math required
// Confidence is already 0–100, matching the converted Azure scale
words.Add(new WordResult
{
Text = word.Text,
Confidence = word.Confidence, // 0–100, no conversion needed
X = word.X,
Y = word.Y,
Width = word.Width,
Height = word.Height
});
}
}
}
return words;
}
// Filter to only high-confidence words — common post-processing pattern
public IEnumerable<string> ExtractHighConfidenceWords(string imagePath, double threshold = 80.0)
{
var result = new IronTesseract().Read(imagePath);
return result.Words
.Where(w => w.Confidence >= threshold)
.Select(w => w.Text);
}
public record WordResult(string Text, double Confidence, int X, int Y, int Width, int Height);
public List<WordResult> ExtractWordPositions(string imagePath)
{
var result = new IronTesseract().Read(imagePath);
var words = new List<WordResult>();
foreach (var page in result.Pages)
{
foreach (var line in page.Lines)
{
foreach (var word in line.Words)
{
// Rectangle-based bounding box — no polygon math required
// Confidence is already 0–100, matching the converted Azure scale
words.Add(new WordResult
{
Text = word.Text,
Confidence = word.Confidence, // 0–100, no conversion needed
X = word.X,
Y = word.Y,
Width = word.Width,
Height = word.Height
});
}
}
}
return words;
}
// Filter to only high-confidence words — common post-processing pattern
public IEnumerable<string> ExtractHighConfidenceWords(string imagePath, double threshold = 80.0)
{
var result = new IronTesseract().Read(imagePath);
return result.Words
.Where(w => w.Confidence >= threshold)
.Select(w => w.Text);
}
public record WordResult(string Text, double Confidence, int X, int Y, int Width, int Height);
Imports System.Collections.Generic
Imports System.Linq
Public Class WordExtractor
Public Function ExtractWordPositions(imagePath As String) As List(Of WordResult)
Dim result = New IronTesseract().Read(imagePath)
Dim words = New List(Of WordResult)()
For Each page In result.Pages
For Each line In page.Lines
For Each word In line.Words
' Rectangle-based bounding box — no polygon math required
' Confidence is already 0–100, matching the converted Azure scale
words.Add(New WordResult With {
.Text = word.Text,
.Confidence = word.Confidence, ' 0–100, no conversion needed
.X = word.X,
.Y = word.Y,
.Width = word.Width,
.Height = word.Height
})
Next
Next
Next
Return words
End Function
' Filter to only high-confidence words — common post-processing pattern
Public Function ExtractHighConfidenceWords(imagePath As String, Optional threshold As Double = 80.0) As IEnumerable(Of String)
Dim result = New IronTesseract().Read(imagePath)
Return result.Words _
.Where(Function(w) w.Confidence >= threshold) _
.Select(Function(w) w.Text)
End Function
End Class
Public Class WordResult
Public Property Text As String
Public Property Confidence As Double
Public Property X As Integer
Public Property Y As Integer
Public Property Width As Integer
Public Property Height As Integer
End Class
Die Umwandlung von Polygonen in Rechtecke entfällt. Die Konfidenzwerte stimmen direkt überein, sobald die Azure-Werte von 0,0 bis 1,0 mit 100 multipliziert werden – jede bestehende Schwellenwertlogik benötigt diese eine Anpassung. Der Leitfaden zur Ausgabe strukturierter Daten dokumentiert die vollständige Hierarchie und die Koordinateneigenschaften. Für das Konfidenzbewertungsmodell im Detail siehe den Leitfaden zu Konfidenzbewertungen .
Mehrseitige TIFF-Verarbeitung ohne Cloud-Upload
Azure Computer Vision's ImageAnalysisClient akzeptiert Einzelbilder. Bei Multi-Frame-TIFF-Dateien – die häufig in Dokumentenscanning-Workflows, Faxarchiven und medizinischen Bildgebungspipelines verwendet werden – ist es entweder erforderlich, die TIFF-Datei vor dem Hochladen in einzelne Bilder aufzuteilen (eine Transaktion pro Frame) oder auf Form Recognizer mit seiner separaten Konfiguration umzuschalten. Keiner der beiden Wege ist sauber.
Azure Computer Vision Ansatz:
// Azure does not support multi-frame TIFF directly via ImageAnalysisClient
// Must split frames manually and upload each as a separate transaction
public async Task<string> ExtractMultiFrameTiffAsync(string tiffPath)
{
// Load TIFF using System.Drawing or a third-party library
using var bitmap = new System.Drawing.Bitmap(tiffPath);
int frameCount = bitmap.GetFrameCount(
System.Drawing.Imaging.FrameDimension.Page);
var allText = new StringBuilder();
for (int i = 0; i < frameCount; i++)
{
// Select frame, save to temporary PNG, upload to Azure
bitmap.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, i);
using var ms = new MemoryStream();
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
ms.Position = 0;
// Each frame = 1 Azure transaction = $0.001
var imageData = BinaryData.FromStream(ms);
var result = await _client.AnalyzeAsync(imageData, VisualFeatures.Read);
foreach (var block in result.Value.Read.Blocks)
foreach (var line in block.Lines)
allText.AppendLine(line.Text);
}
return allText.ToString();
}
// Azure does not support multi-frame TIFF directly via ImageAnalysisClient
// Must split frames manually and upload each as a separate transaction
public async Task<string> ExtractMultiFrameTiffAsync(string tiffPath)
{
// Load TIFF using System.Drawing or a third-party library
using var bitmap = new System.Drawing.Bitmap(tiffPath);
int frameCount = bitmap.GetFrameCount(
System.Drawing.Imaging.FrameDimension.Page);
var allText = new StringBuilder();
for (int i = 0; i < frameCount; i++)
{
// Select frame, save to temporary PNG, upload to Azure
bitmap.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, i);
using var ms = new MemoryStream();
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
ms.Position = 0;
// Each frame = 1 Azure transaction = $0.001
var imageData = BinaryData.FromStream(ms);
var result = await _client.AnalyzeAsync(imageData, VisualFeatures.Read);
foreach (var block in result.Value.Read.Blocks)
foreach (var line in block.Lines)
allText.AppendLine(line.Text);
}
return allText.ToString();
}
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.IO
Imports System.Text
Imports System.Threading.Tasks
Public Class TiffProcessor
Private _client As ImageAnalysisClient
Public Async Function ExtractMultiFrameTiffAsync(tiffPath As String) As Task(Of String)
' Load TIFF using System.Drawing or a third-party library
Using bitmap As New Bitmap(tiffPath)
Dim frameCount As Integer = bitmap.GetFrameCount(FrameDimension.Page)
Dim allText As New StringBuilder()
For i As Integer = 0 To frameCount - 1
' Select frame, save to temporary PNG, upload to Azure
bitmap.SelectActiveFrame(FrameDimension.Page, i)
Using ms As New MemoryStream()
bitmap.Save(ms, Imaging.ImageFormat.Png)
ms.Position = 0
' Each frame = 1 Azure transaction = $0.001
Dim imageData As BinaryData = BinaryData.FromStream(ms)
Dim result = Await _client.AnalyzeAsync(imageData, VisualFeatures.Read)
For Each block In result.Value.Read.Blocks
For Each line In block.Lines
allText.AppendLine(line.Text)
Next
Next
End Using
Next
Return allText.ToString()
End Using
End Function
End Class
IronOCR Ansatz:
//IronOCR handles multi-frame TIFF natively — single call, no frame splitting
public string ExtractMultiFrameTiff(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // all frames loaded automatically
var result = new IronTesseract().Read(input);
return result.Text;
}
// Access per-page data for frame-level reporting
public void ExtractTiffWithPageStats(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath);
var ocr = new IronTesseract();
var result = ocr.Read(input);
Console.WriteLine($"Total frames processed: {result.Pages.Length}");
foreach (var page in result.Pages)
{
Console.WriteLine($"Frame {page.PageNumber}: " +
$"{page.Words.Length} words, " +
$"confidence {page.Confidence:F1}%");
}
}
// Combine with preprocessing for scanned TIFF archives
public string ExtractLowQualityTiffArchive(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath);
input.Deskew();
input.DeNoise();
input.Contrast();
var result = new IronTesseract().Read(input);
return result.Text;
}
//IronOCR handles multi-frame TIFF natively — single call, no frame splitting
public string ExtractMultiFrameTiff(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // all frames loaded automatically
var result = new IronTesseract().Read(input);
return result.Text;
}
// Access per-page data for frame-level reporting
public void ExtractTiffWithPageStats(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath);
var ocr = new IronTesseract();
var result = ocr.Read(input);
Console.WriteLine($"Total frames processed: {result.Pages.Length}");
foreach (var page in result.Pages)
{
Console.WriteLine($"Frame {page.PageNumber}: " +
$"{page.Words.Length} words, " +
$"confidence {page.Confidence:F1}%");
}
}
// Combine with preprocessing for scanned TIFF archives
public string ExtractLowQualityTiffArchive(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath);
input.Deskew();
input.DeNoise();
input.Contrast();
var result = new IronTesseract().Read(input);
return result.Text;
}
Imports System
' IronOCR handles multi-frame TIFF natively — single call, no frame splitting
Public Function ExtractMultiFrameTiff(tiffPath As String) As String
Using input As New OcrInput()
input.LoadImageFrames(tiffPath) ' all frames loaded automatically
Dim result = New IronTesseract().Read(input)
Return result.Text
End Using
End Function
' Access per-page data for frame-level reporting
Public Sub ExtractTiffWithPageStats(tiffPath As String)
Using input As New OcrInput()
input.LoadImageFrames(tiffPath)
Dim ocr As New IronTesseract()
Dim result = ocr.Read(input)
Console.WriteLine($"Total frames processed: {result.Pages.Length}")
For Each page In result.Pages
Console.WriteLine($"Frame {page.PageNumber}: " &
$"{page.Words.Length} words, " &
$"confidence {page.Confidence:F1}%")
Next
End Using
End Sub
' Combine with preprocessing for scanned TIFF archives
Public Function ExtractLowQualityTiffArchive(tiffPath As String) As String
Using input As New OcrInput()
input.LoadImageFrames(tiffPath)
input.Deskew()
input.DeNoise()
input.Contrast()
Dim result = New IronTesseract().Read(input)
Return result.Text
End Using
End Function
Die Transaktionskosten pro Frame sinken auf null. Die System.Drawing-Rahmen-Extraktionsschleife und der temporäre PNG-Serialisierungsschritt werden vollständig beseitigt. Für Faxarchivierungs-Workflows, bei denen die Dokumentqualität variiert, beschreibt der Leitfaden zur TIFF- und GIF-Eingabe die Optionen zur Frame-Auswahl, und der Leitfaden zur Bildqualitätskorrektur dokumentiert den kompletten Satz der Vorverarbeitungsfilter.
Parallele Stapelverarbeitung ohne Ratenbegrenzung
Die S1-Ebene von Azure Computer Visionbegrenzt den Durchsatz auf 10 Transaktionen pro Sekunde. Batch-Jobs, die diese Rate überschreiten, erhalten HTTP-429-Antworten. Produktionsimplementierungen erfordern eine Ratenbegrenzungsumhüllung, ein Semaphor oder eine Warteschlange, um innerhalb der Grenze zu bleiben. Die IronOCR-API ist threadsicher im Design — erstellen Sie eine IronTesseract-Instanz pro Thread und laufen Sie mit Parallel.ForEach.
Azure Computer Vision Ansatz:
// Must throttle to 10 TPS to avoid 429 errors on S1 tier
public class ThrottledAzureBatchProcessor
{
private readonly ImageAnalysisClient _client;
// Semaphore limits concurrent Azure calls to stay under 10 TPS
private readonly SemaphoreSlim _throttle = new SemaphoreSlim(10, 10);
public async Task<Dictionary<string, string>> ProcessBatchAsync(
IEnumerable<string> imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
var tasks = imagePaths.Select(async path =>
{
await _throttle.WaitAsync();
try
{
using var stream = File.OpenRead(path);
var data = BinaryData.FromStream(stream);
var response = await _client.AnalyzeAsync(data, VisualFeatures.Read);
var text = string.Join("\n",
response.Value.Read.Blocks
.SelectMany(b => b.Lines)
.Select(l => l.Text));
results[path] = text;
// Respect 1-second window for the 10 TPS ceiling
await Task.Delay(100);
}
catch (RequestFailedException ex) when (ex.Status == 429)
{
// Rate limited despite throttling — back off and retry
await Task.Delay(2000);
// Re-queue or log failure — simplified here
results[path] = string.Empty;
}
finally
{
_throttle.Release();
}
});
await Task.WhenAll(tasks);
return new Dictionary<string, string>(results);
}
}
// Must throttle to 10 TPS to avoid 429 errors on S1 tier
public class ThrottledAzureBatchProcessor
{
private readonly ImageAnalysisClient _client;
// Semaphore limits concurrent Azure calls to stay under 10 TPS
private readonly SemaphoreSlim _throttle = new SemaphoreSlim(10, 10);
public async Task<Dictionary<string, string>> ProcessBatchAsync(
IEnumerable<string> imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
var tasks = imagePaths.Select(async path =>
{
await _throttle.WaitAsync();
try
{
using var stream = File.OpenRead(path);
var data = BinaryData.FromStream(stream);
var response = await _client.AnalyzeAsync(data, VisualFeatures.Read);
var text = string.Join("\n",
response.Value.Read.Blocks
.SelectMany(b => b.Lines)
.Select(l => l.Text));
results[path] = text;
// Respect 1-second window for the 10 TPS ceiling
await Task.Delay(100);
}
catch (RequestFailedException ex) when (ex.Status == 429)
{
// Rate limited despite throttling — back off and retry
await Task.Delay(2000);
// Re-queue or log failure — simplified here
results[path] = string.Empty;
}
finally
{
_throttle.Release();
}
});
await Task.WhenAll(tasks);
return new Dictionary<string, string>(results);
}
}
Imports System.Collections.Concurrent
Imports System.IO
Imports System.Threading
Imports System.Threading.Tasks
' Must throttle to 10 TPS to avoid 429 errors on S1 tier
Public Class ThrottledAzureBatchProcessor
Private ReadOnly _client As ImageAnalysisClient
' Semaphore limits concurrent Azure calls to stay under 10 TPS
Private ReadOnly _throttle As New SemaphoreSlim(10, 10)
Public Async Function ProcessBatchAsync(imagePaths As IEnumerable(Of String)) As Task(Of Dictionary(Of String, String))
Dim results As New ConcurrentDictionary(Of String, String)()
Dim tasks = imagePaths.Select(Async Function(path)
Await _throttle.WaitAsync()
Try
Using stream = File.OpenRead(path)
Dim data = BinaryData.FromStream(stream)
Dim response = Await _client.AnalyzeAsync(data, VisualFeatures.Read)
Dim text = String.Join(vbLf,
response.Value.Read.Blocks _
.SelectMany(Function(b) b.Lines) _
.Select(Function(l) l.Text))
results(path) = text
' Respect 1-second window for the 10 TPS ceiling
Await Task.Delay(100)
End Using
Catch ex As RequestFailedException When ex.Status = 429
' Rate limited despite throttling — back off and retry
Await Task.Delay(2000)
' Re-queue or log failure — simplified here
results(path) = String.Empty
Finally
_throttle.Release()
End Try
End Function)
Await Task.WhenAll(tasks)
Return New Dictionary(Of String, String)(results)
End Function
End Class
IronOCR Ansatz:
//Neinrate limiting needed — throughput is hardware-bound
public Dictionary<string, string> ProcessBatch(IEnumerable<string> imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
Parallel.ForEach(imagePaths, imagePath =>
{
// Each thread gets its own IronTesseract instance — fully thread-safe
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
results[imagePath] = result.Text;
});
return new Dictionary<string, string>(results);
}
// With controlled parallelism for memory-constrained environments
public Dictionary<string, string> ProcessBatchControlled(
IEnumerable<string> imagePaths, int maxDegreeOfParallelism = 4)
{
var results = new ConcurrentDictionary<string, string>();
var options = new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism };
Parallel.ForEach(imagePaths, options, imagePath =>
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
results[imagePath] = result.Text;
});
return new Dictionary<string, string>(results);
}
//Neinrate limiting needed — throughput is hardware-bound
public Dictionary<string, string> ProcessBatch(IEnumerable<string> imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
Parallel.ForEach(imagePaths, imagePath =>
{
// Each thread gets its own IronTesseract instance — fully thread-safe
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
results[imagePath] = result.Text;
});
return new Dictionary<string, string>(results);
}
// With controlled parallelism for memory-constrained environments
public Dictionary<string, string> ProcessBatchControlled(
IEnumerable<string> imagePaths, int maxDegreeOfParallelism = 4)
{
var results = new ConcurrentDictionary<string, string>();
var options = new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism };
Parallel.ForEach(imagePaths, options, imagePath =>
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
results[imagePath] = result.Text;
});
return new Dictionary<string, string>(results);
}
Imports System.Collections.Concurrent
Imports System.Collections.Generic
Imports System.Threading.Tasks
Public Class ImageProcessor
' Neinrate limiting needed — throughput is hardware-bound
Public Function ProcessBatch(imagePaths As IEnumerable(Of String)) As Dictionary(Of String, String)
Dim results = New ConcurrentDictionary(Of String, String)()
Parallel.ForEach(imagePaths, Sub(imagePath)
' Each thread gets its own IronTesseract instance — fully thread-safe
Dim ocr = New IronTesseract()
Dim result = ocr.Read(imagePath)
results(imagePath) = result.Text
End Sub)
Return New Dictionary(Of String, String)(results)
End Function
' With controlled parallelism for memory-constrained environments
Public Function ProcessBatchControlled(imagePaths As IEnumerable(Of String), Optional maxDegreeOfParallelism As Integer = 4) As Dictionary(Of String, String)
Dim results = New ConcurrentDictionary(Of String, String)()
Dim options = New ParallelOptions With {.MaxDegreeOfParallelism = maxDegreeOfParallelism}
Parallel.ForEach(imagePaths, options, Sub(imagePath)
Dim ocr = New IronTesseract()
Dim result = ocr.Read(imagePath)
results(imagePath) = result.Text
End Sub)
Return New Dictionary(Of String, String)(results)
End Function
End Class
Das Semaphor, die 100-ms-Verzögerung und der HTTP-429-Catch-Block wurden entfernt. Die Parallelität ist nur durch die CPU-Kerne und den verfügbaren Speicher begrenzt, nicht durch eine Serviceebene. Das Beispiel zur Multithreading-Anwendung zeigt das vollständige Muster mit Zeitvergleichen, und der Leitfaden zur Geschwindigkeitsoptimierung behandelt die Konfigurationsoptimierung der Engine für Batch-Workloads.
Vorverarbeitung von Scans minderer Qualität, die Azure ablehnt
Azure Computer Vision führt eine serverseitige Bildverbesserung durch, ist aber undurchsichtig und nicht konfigurierbar. Dokumente, die zu verzerrt, zu verrauscht oder zu kontrastarm sind, liefern unzuverlässige Ergebnisse oder leeren Text, gegen den man nichts tun kann.IronOCR stellt die Vorverarbeitungspipeline direkt auf OcrInput zur Verfügung.
Azure Computer Vision Ansatz:
//Neinclient-side preprocessing API — must preprocess externally before upload
public async Task<string> ExtractFromLowQualityScanAsync(string imagePath)
{
// Option 1: Accept whatever Azure returns (may be empty or low-quality)
using var stream = File.OpenRead(imagePath);
var imageData = BinaryData.FromStream(stream);
var result = await _client.AnalyzeAsync(imageData, VisualFeatures.Read);
//Neinway to know if the server applied enhancement
//Neinconfidence on the overall result — only per-word
var text = string.Join("\n",
result.Value.Read.Blocks
.SelectMany(b => b.Lines)
.Select(l => l.Text));
if (string.IsNullOrWhiteSpace(text))
{
// Option 2: Apply external preprocessing using System.Drawing, SkiaSharp,
// or ImageMagick, re-serialize to stream, re-upload — second billable transaction
throw new Exception("Azure returned empty result; manual preprocessing needed");
}
return text;
}
//Neinclient-side preprocessing API — must preprocess externally before upload
public async Task<string> ExtractFromLowQualityScanAsync(string imagePath)
{
// Option 1: Accept whatever Azure returns (may be empty or low-quality)
using var stream = File.OpenRead(imagePath);
var imageData = BinaryData.FromStream(stream);
var result = await _client.AnalyzeAsync(imageData, VisualFeatures.Read);
//Neinway to know if the server applied enhancement
//Neinconfidence on the overall result — only per-word
var text = string.Join("\n",
result.Value.Read.Blocks
.SelectMany(b => b.Lines)
.Select(l => l.Text));
if (string.IsNullOrWhiteSpace(text))
{
// Option 2: Apply external preprocessing using System.Drawing, SkiaSharp,
// or ImageMagick, re-serialize to stream, re-upload — second billable transaction
throw new Exception("Azure returned empty result; manual preprocessing needed");
}
return text;
}
Imports System.IO
Imports System.Threading.Tasks
Public Class Example
Public Async Function ExtractFromLowQualityScanAsync(imagePath As String) As Task(Of String)
' Option 1: Accept whatever Azure returns (may be empty or low-quality)
Using stream = File.OpenRead(imagePath)
Dim imageData = BinaryData.FromStream(stream)
Dim result = Await _client.AnalyzeAsync(imageData, VisualFeatures.Read)
' Neinway to know if the server applied enhancement
' Neinconfidence on the overall result — only per-word
Dim text = String.Join(vbLf, result.Value.Read.Blocks.SelectMany(Function(b) b.Lines).Select(Function(l) l.Text))
If String.IsNullOrWhiteSpace(text) Then
' Option 2: Apply external preprocessing using System.Drawing, SkiaSharp,
' or ImageMagick, re-serialize to stream, re-upload — second billable transaction
Throw New Exception("Azure returned empty result; manual preprocessing needed")
End If
Return text
End Using
End Function
End Class
IronOCR Ansatz:
// Preprocessing is part of the same call — no re-upload, no second transaction
public string ExtractFromLowQualityScan(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
// Correct common scanning defects before OCR
input.Deskew(); // Fix rotated documents
input.DeNoise(); // Remove scanner noise
input.Contrast(); // Improve contrast for faded documents
input.Binarize(); // Convert to black and white
var ocr = new IronTesseract();
var result = ocr.Read(input);
Console.WriteLine($"Confidence after preprocessing: {result.Confidence:F1}%");
return result.Text;
}
// For severely degraded documents
public string ExtractFromDegradedDocument(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
input.DeepCleanBackgroundNoise(); // Deep learning-based noise removal
input.Deskew();
input.Scale(150); // Upscale for better character resolution
var result = new IronTesseract().Read(input);
return result.Text;
}
// Preprocessing is part of the same call — no re-upload, no second transaction
public string ExtractFromLowQualityScan(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
// Correct common scanning defects before OCR
input.Deskew(); // Fix rotated documents
input.DeNoise(); // Remove scanner noise
input.Contrast(); // Improve contrast for faded documents
input.Binarize(); // Convert to black and white
var ocr = new IronTesseract();
var result = ocr.Read(input);
Console.WriteLine($"Confidence after preprocessing: {result.Confidence:F1}%");
return result.Text;
}
// For severely degraded documents
public string ExtractFromDegradedDocument(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
input.DeepCleanBackgroundNoise(); // Deep learning-based noise removal
input.Deskew();
input.Scale(150); // Upscale for better character resolution
var result = new IronTesseract().Read(input);
return result.Text;
}
Imports System
Public Class OcrProcessor
' Preprocessing is part of the same call — no re-upload, no second transaction
Public Function ExtractFromLowQualityScan(imagePath As String) As String
Using input As New OcrInput()
input.LoadImage(imagePath)
' Correct common scanning defects before OCR
input.Deskew() ' Fix rotated documents
input.DeNoise() ' Remove scanner noise
input.Contrast() ' Improve contrast for faded documents
input.Binarize() ' Convert to black and white
Dim ocr As New IronTesseract()
Dim result = ocr.Read(input)
Console.WriteLine($"Confidence after preprocessing: {result.Confidence:F1}%")
Return result.Text
End Using
End Function
' For severely degraded documents
Public Function ExtractFromDegradedDocument(imagePath As String) As String
Using input As New OcrInput()
input.LoadImage(imagePath)
input.DeepCleanBackgroundNoise() ' Deep learning-based noise removal
input.Deskew()
input.Scale(150) ' Upscale for better character resolution
Dim result = New IronTesseract().Read(input)
Return result.Text
End Using
End Function
End Class
Die externe Vorverarbeitungsabhängigkeit — System.Drawing, SkiaSharp oder ImageMagick — wird entfernt. Die Kosten für den erneuten Upload und die zweite Transaktion entfallen. Die Vorverarbeitungspipeline ist Teil des OcrInput-Lebenszyklus, sodass sie angewendet wird, bevor die OCR-Engine das Bild sieht. Das Tutorial zu den Bildfiltern erläutert jeden Filter anhand von Vorher-/Nachher-Vergleichen der Genauigkeit.
Azure Computer VisionOCRAPI zu IronOCR Mapping-Referenz
| Azure Computer Vision | IronOCR-Äquivalent |
|---|---|
ImageAnalysisClient |
IronTesseract |
new AzureKeyCredential(apiKey) |
IronOcr.License.LicenseKey = key |
new Uri(endpoint) |
Nicht erforderlich |
client.AnalyzeAsync(data, VisualFeatures.Read) |
ocr.Read(imagePath) |
BinaryData.FromStream(stream) |
input.LoadImage(stream) |
BinaryData.FromBytes(bytes) |
input.LoadImage(bytes) |
result.Value.Read.Blocks |
result.Pages[i].Paragraphs |
block.Lines |
result.Pages[i].Lines |
line.Text |
line.Text |
line.Words |
line.Words |
word.Text |
word.Text |
word.Confidence (0,0–1,0 Float) |
word.Confidence (0–100 Double) |
word.BoundingPolygon |
word.X, word.Y, word.Width, word.Height |
DocumentAnalysisClient |
IronTesseract + OcrInput |
AnalyzeDocumentAsync(WaitUntil.Completed, "prebuilt-read", stream) |
ocr.Read(input) mit input.LoadPdf(path) |
operation.Value.Pages |
result.Pages |
page.Lines[i].Content |
result.Lines[i].Text |
UpdateStatusAsync()-Polling-Schleife |
Nicht erforderlich – synchrones Ergebnis |
RequestFailedException (Status 429) |
Nicht anwendbar – keine Ratenbegrenzungen |
RequestFailedException (Status 5xx) |
Nicht zutreffend – keine Servicefehler |
Enum-Flag VisualFeatures.Read |
Implizit — Read() extrahiert immer Text |
Formularerkennungs-prebuilt-read-Modell |
Eingebauter OCR-Prozessor (keine Modellauswahl) |
Azure-Endpunkt-URL in appsettings.json |
Nicht erforderlich |
| API-Schlüsselrotationsverfahren | Nicht erforderlich |
Gängige Migrationsprobleme und Lösungen
Problem 1: Asynchrone Schnittstellenverträge, die sich nicht ändern können
Azure Computer Vision: Dienstschnittstellen deklarieren oft Task<string>-Rückgabewerte, weil Azure Async erzwingt. Aufrufender Code, Controller und Hintergrundprozesse sind alle als asynchrone Methoden geschrieben. Durch den Wechsel zu IronOCR entfällt die Notwendigkeit von Asynchronität in der OCR-Schicht, aber die Änderung jeder einzelnen Schnittstellensignatur ist in einer großen Codebasis nicht immer praktikabel.
Lösung: Umhüllen Sie den synchronen IronOCR-Aufruf in Task.Run, um das bestehende Interface zu befriedigen, ohne umfassende Refaktorisierungen:
// Existing interface — do not change it
public interface IOcrService
{
Task<string> ReadAsync(string imagePath);
}
// New IronOCR implementation — fulfills the contract
public class IronOcrService : IOcrService
{
private readonly IronTesseract _ocr;
public IronOcrService(IronTesseract ocr) => _ocr = ocr;
public Task<string> ReadAsync(string imagePath)
{
// Task.Run offloads to thread pool — no await chain needed
return Task.Run(() => _ocr.Read(imagePath).Text);
}
}
// Existing interface — do not change it
public interface IOcrService
{
Task<string> ReadAsync(string imagePath);
}
// New IronOCR implementation — fulfills the contract
public class IronOcrService : IOcrService
{
private readonly IronTesseract _ocr;
public IronOcrService(IronTesseract ocr) => _ocr = ocr;
public Task<string> ReadAsync(string imagePath)
{
// Task.Run offloads to thread pool — no await chain needed
return Task.Run(() => _ocr.Read(imagePath).Text);
}
}
Imports System.Threading.Tasks
' Existing interface — do not change it
Public Interface IOcrService
Function ReadAsync(imagePath As String) As Task(Of String)
End Interface
' New IronOCR implementation — fulfills the contract
Public Class IronOcrService
Implements IOcrService
Private ReadOnly _ocr As IronTesseract
Public Sub New(ocr As IronTesseract)
_ocr = ocr
End Sub
Public Function ReadAsync(imagePath As String) As Task(Of String) Implements IOcrService.ReadAsync
' Task.Run offloads to thread pool — no await chain needed
Return Task.Run(Function() _ocr.Read(imagePath).Text)
End Function
End Class
Dies ist ein sinnvoller Zwischenschritt. Der Leitfaden zur asynchronen OCR beschreibt die in IronOCR integrierte asynchrone Unterstützung für Szenarien, in denen eine vollständige asynchrone Integration bevorzugt wird.
Problem 2: Logik der Konfidenzschwellenwerte führt zu falschen Ergebnissen
Azure Computer Vision: Azure gibt das Wortvertrauen als einen float zwischen 0,0 und 1,0 zurück. Bestehender Filtercode verwendet Schwellenwerte wie word.Confidence > 0.85f. Nach der Migration werden diese Vergleiche immer als falsch ausgewertet, da das IronOCR Konfidenzintervall 0–100 und nicht 0–1 beträgt.
Lösung: Multiplizieren Sie die vorhandenen Azure-Schwellenwerte mit 100, wenn Sie die Filterlogik aktualisieren:
// Before: Azure threshold (0.0 - 1.0 scale)
var highConfidenceWords = azureWords
.Where(w => w.Confidence > 0.85f)
.Select(w => w.Text);
// After:IronOCR threshold (0 - 100 scale)
var result = new IronTesseract().Read(imagePath);
var highConfidenceWords = result.Words
.Where(w => w.Confidence > 85.0)
.Select(w => w.Text);
// Overall document confidence — also on 0-100 scale
if (result.Confidence < 70.0)
{
// Document may need preprocessing or manual review
}
// Before: Azure threshold (0.0 - 1.0 scale)
var highConfidenceWords = azureWords
.Where(w => w.Confidence > 0.85f)
.Select(w => w.Text);
// After:IronOCR threshold (0 - 100 scale)
var result = new IronTesseract().Read(imagePath);
var highConfidenceWords = result.Words
.Where(w => w.Confidence > 85.0)
.Select(w => w.Text);
// Overall document confidence — also on 0-100 scale
if (result.Confidence < 70.0)
{
// Document may need preprocessing or manual review
}
Imports System.Linq
' Before: Azure threshold (0.0 - 1.0 scale)
Dim highConfidenceWords = azureWords _
.Where(Function(w) w.Confidence > 0.85F) _
.Select(Function(w) w.Text)
' After: IronOCR threshold (0 - 100 scale)
Dim result = New IronTesseract().Read(imagePath)
Dim highConfidenceWords = result.Words _
.Where(Function(w) w.Confidence > 85.0) _
.Select(Function(w) w.Text)
' Overall document confidence — also on 0-100 scale
If result.Confidence < 70.0 Then
' Document may need preprocessing or manual review
End If
Problem 3: Die Extraktion vordefinierter Modellfelder des Formularerkenners hat kein direktes IronOCR Äquivalent.
Azure Computer Vision: Die vorgebauten Modelle des Formularerkennungsgeräts für Rechnungen und Quittungen extrahieren benannte Felder automatisch — InvoiceTotal, VendorName, InvoiceDate — ohne anzugeben, wo diese Felder auf der Seite erscheinen. Die Extraktionslogik ist im Azure-Modell eingebettet.
Lösung: Ersetzen Sie die modellbasierte Feldeextraktion durch eine regionenbasierte OCR mit CropRectangle. Dies erfordert Kenntnisse über das Dokumentenlayout, aber die meisten realen Implementierungen verwenden bereits feste Vorlagen:
var ocr = new IronTesseract();
// Define extraction zones for a known invoice template
var headerZone = new CropRectangle(50, 40, 400, 60);
var totalZone = new CropRectangle(350, 600, 250, 50);
var dateZone = new CropRectangle(400, 100, 200, 40);
string header, total, date;
using (var input = new OcrInput())
{
input.LoadImage("invoice.jpg", headerZone);
header = ocr.Read(input).Text.Trim();
}
using (var input = new OcrInput())
{
input.LoadImage("invoice.jpg", totalZone);
total = ocr.Read(input).Text.Trim();
}
using (var input = new OcrInput())
{
input.LoadImage("invoice.jpg", dateZone);
date = ocr.Read(input).Text.Trim();
}
var ocr = new IronTesseract();
// Define extraction zones for a known invoice template
var headerZone = new CropRectangle(50, 40, 400, 60);
var totalZone = new CropRectangle(350, 600, 250, 50);
var dateZone = new CropRectangle(400, 100, 200, 40);
string header, total, date;
using (var input = new OcrInput())
{
input.LoadImage("invoice.jpg", headerZone);
header = ocr.Read(input).Text.Trim();
}
using (var input = new OcrInput())
{
input.LoadImage("invoice.jpg", totalZone);
total = ocr.Read(input).Text.Trim();
}
using (var input = new OcrInput())
{
input.LoadImage("invoice.jpg", dateZone);
date = ocr.Read(input).Text.Trim();
}
Imports IronOcr
Dim ocr As New IronTesseract()
' Define extraction zones for a known invoice template
Dim headerZone As New CropRectangle(50, 40, 400, 60)
Dim totalZone As New CropRectangle(350, 600, 250, 50)
Dim dateZone As New CropRectangle(400, 100, 200, 40)
Dim header As String
Dim total As String
Dim date As String
Using input As New OcrInput()
input.LoadImage("invoice.jpg", headerZone)
header = ocr.Read(input).Text.Trim()
End Using
Using input As New OcrInput()
input.LoadImage("invoice.jpg", totalZone)
total = ocr.Read(input).Text.Trim()
End Using
Using input As New OcrInput()
input.LoadImage("invoice.jpg", dateZone)
date = ocr.Read(input).Text.Trim()
End Using
Der regionsbasierte OCR-Leitfaden behandelt Details zum Koordinatensystem und zur regionsübergreifenden Stapelverarbeitung.
Problem 4: Fehlende hOCR und strukturierter Export
Azure Computer Vision: Azure stellt keine hOCR-Ausgabe bereit. Teams, die standardisierte Layoutdaten für nachgelagerte Dokumentenanalysetools benötigen, extrahieren Begrenzungsrahmen manuell aus der Azure-Antwort und erstellen ihr eigenes Ausgabeformat.
Lösung:IronOCR erzeugt standardkonforme hOCR in einem einzigen Aufruf:
var result = new IronTesseract().Read("document.jpg");
// Write hOCR file — recognized by most document analysis tools
result.SaveAsHocrFile("document.hocr");
// Searchable PDF — alternative for archive and search indexing workflows
result.SaveAsSearchablePdf("document-searchable.pdf");
var result = new IronTesseract().Read("document.jpg");
// Write hOCR file — recognized by most document analysis tools
result.SaveAsHocrFile("document.hocr");
// Searchable PDF — alternative for archive and search indexing workflows
result.SaveAsSearchablePdf("document-searchable.pdf");
Dim result = (New IronTesseract()).Read("document.jpg")
' Write hOCR file — recognized by most document analysis tools
result.SaveAsHocrFile("document.hocr")
' Searchable PDF — alternative for archive and search indexing workflows
result.SaveAsSearchablePdf("document-searchable.pdf")
Problem 5: Versionskonflikte des Azure SDK mit anderen Azure-Paketen
Azure Computer Vision: Projekte, die mehrere Azure SDK-Pakete verwenden (Azure.Storage.Blobs, Azure.Identity, Azure.KeyVault.Secrets) können Versionskonflikte zwischen Azure.Core-transitiven Abhängigkeiten feststellen. Die Monorepo-Versionsrichtlinie des Azure SDK hilft zwar, beseitigt aber nicht alle Konflikte, insbesondere beim Mischen von GA- und Preview-SDK-Versionen.
Lösung: Entfernen von Azure.AI.Vision.ImageAnalysis und Azure.AI.FormRecognizer eliminiert diese SDK-Pakete aus dem Abhängigkeitsbaum. Wenn das Projekt Azure nur für OCR verwendet, wird das gesamte Azure.*-Abhängigkeitenset entfernt. Wenn andere Azure-Dienste erhalten bleiben, verringert die reduzierte Paketanzahl die Angriffsfläche für Konflikte:
# Remove only the OCR-related Azure packages
dotnet remove package Azure.AI.Vision.ImageAnalysis
dotnet remove package Azure.AI.FormRecognizer
# Verify remaining Azure packages have no new conflicts
dotnet restore
dotnet build
# Remove only the OCR-related Azure packages
dotnet remove package Azure.AI.Vision.ImageAnalysis
dotnet remove package Azure.AI.FormRecognizer
# Verify remaining Azure packages have no new conflicts
dotnet restore
dotnet build
Problem 6: Qualität gescannter Dokumente unterhalb des Azure-Akzeptanzschwellenwerts
Azure Computer Vision: Bei Bildern mit sehr niedriger Auflösung (unter ~150 DPI) oder stark verzerrten Scans liefert die serverseitige Pipeline von Azure minimalen oder leeren Text ohne Rückmeldung darüber, welche Verbesserung versucht wurde. Der Aufrufer hat ohne externe Vorverarbeitung keine Möglichkeit, das Ergebnis zu verbessern.
Lösung: Verwenden Sie die Vorverarbeitungspipeline von IronOCR, um das Bild vor der OCR vorzubereiten:
using var input = new OcrInput();
input.LoadImage("low-quality-scan.jpg");
input.Deskew();
input.DeNoise();
input.Scale(200); // Upscale to improve character resolution
input.Contrast();
input.Sharpen();
var result = new IronTesseract().Read(input);
Console.WriteLine($"Extraction confidence: {result.Confidence:F1}%");
using var input = new OcrInput();
input.LoadImage("low-quality-scan.jpg");
input.Deskew();
input.DeNoise();
input.Scale(200); // Upscale to improve character resolution
input.Contrast();
input.Sharpen();
var result = new IronTesseract().Read(input);
Console.WriteLine($"Extraction confidence: {result.Confidence:F1}%");
Imports IronOcr
Using input As New OcrInput()
input.LoadImage("low-quality-scan.jpg")
input.Deskew()
input.DeNoise()
input.Scale(200) ' Upscale to improve character resolution
input.Contrast()
input.Sharpen()
Dim result = New IronTesseract().Read(input)
Console.WriteLine($"Extraction confidence: {result.Confidence:F1}%")
End Using
Der Leitfaden zur Bildausrichtungskorrektur behandelt insbesondere die Erkennung von Schräglage und Drehung.
Azure Computer VisionOCRMigrations-Checkliste
Vor der Migration
Suchen Sie alle Verwendungen von Azure Computer Visionund Form Recognizer im Quellcode:
# Find all Azure OCR-related using statements
grep -r "Azure.AI.Vision.ImageAnalysis" --include="*.cs" .
grep -r "Azure.AI.FormRecognizer" --include="*.cs" .
grep -r "ImageAnalysisClient" --include="*.cs" .
grep -r "DocumentAnalysisClient" --include="*.cs" .
grep -r "AnalyzeAsync" --include="*.cs" .
grep -r "AnalyzeDocumentAsync" --include="*.cs" .
grep -r "VisualFeatures.Read" --include="*.cs" .
grep -r "WaitUntil.Completed" --include="*.cs" .
grep -r "UpdateStatusAsync" --include="*.cs" .
grep -r "AzureKeyCredential" --include="*.cs" .
# Find all Azure OCR-related using statements
grep -r "Azure.AI.Vision.ImageAnalysis" --include="*.cs" .
grep -r "Azure.AI.FormRecognizer" --include="*.cs" .
grep -r "ImageAnalysisClient" --include="*.cs" .
grep -r "DocumentAnalysisClient" --include="*.cs" .
grep -r "AnalyzeAsync" --include="*.cs" .
grep -r "AnalyzeDocumentAsync" --include="*.cs" .
grep -r "VisualFeatures.Read" --include="*.cs" .
grep -r "WaitUntil.Completed" --include="*.cs" .
grep -r "UpdateStatusAsync" --include="*.cs" .
grep -r "AzureKeyCredential" --include="*.cs" .
Identifizieren Sie Konfigurationsdateien, die Azure OCR-Endpunkte und -Schlüssel enthalten:
grep -r "cognitiveservices.azure.com" --include="*.json" .
grep -r "AzureComputerVision\|FormRecognizer" --include="*.json" .
grep -r "ComputerVision\|FormRecognizer" --include="appsettings*.json" .
grep -r "cognitiveservices.azure.com" --include="*.json" .
grep -r "AzureComputerVision\|FormRecognizer" --include="*.json" .
grep -r "ComputerVision\|FormRecognizer" --include="appsettings*.json" .
Inventargegenstände vor Beginn der Codierung:
- Anzahl der Klassen, die Azure OCR-Dienstmuster implementieren
- Anzahl der asynchronen Methodenketten, die von OCR-Aufrufen ausgehen
- Ermitteln Sie mithilfe der Azure-Skala (0,0–1,0) alle Wortkonfidenzschwellenwerte.
- Identifizierung der Verwendung vordefinierter Formularerkennungsmodelle (Rechnung, Quittung, Ausweis), die eine regionsbasierte Ersetzung erfordern
- Identifizieren Sie TIFF-Eingaben mit mehreren Einzelbildern, die derzeit für den Upload pro Einzelbild aufgeteilt sind.
- Überprüfen Sie die Docker- und CI/CD-Konfigurationen auf ausgehende Azure-Netzwerkregeln, die nicht mehr benötigt werden.
Code-Migration
- Entfernen Sie das
Azure.AI.Vision.ImageAnalysis-NuGet-Paket aus allen Projekten, die es verwenden - Entfernen Sie das
Azure.AI.FormRecognizer-NuGet-Paket aus allen Projekten, die es verwenden - Führen Sie
dotnet add package IronOcrin jedem betroffenen Projekt aus - Fügen Sie
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")beim Start der Anwendung hinzu - Ersetzen Sie
using Azure; using Azure.AI.Vision.ImageAnalysis;withusing IronOcr; - Registrieren Sie
IronTesseractals Singleton in DI (services.AddSingleton<IronTesseract>()) - Entfernen Sie
AzureComputerVisionOptionsoder gleichwertige Konfigurationsklassen; entfernen Sie dieappsettings.json-Azure-OCR-Bereiche - Ersetzen Sie die
ImageAnalysisClient-Konstruktor-Injektion durchIronTesseract-Injektion in allen Dienstklassen - Ersetzen Sie
AnalyzeAsync(BinaryData.FromStream(stream), VisualFeatures.Read)-Aufrufe durchocr.Read(imagePath)oderocr.Read(input)mitOcrInput, wie geeignet - Entfernen Sie alle
UpdateStatusAsync-Polling-Schleifen undWaitUntil.Completed-Muster; ersetzen SieDocumentAnalysisClientmitIronTesseract+OcrInput.LoadPdf() - Aktualisierung der Wortkonfidenzschwellenwerte: Multiplikation aller Azure-Werte (0,0–1,0) mit 100
- Ersetzen Sie den Polygonbegrenzungsrahmenzugang (
word.BoundingPolygon.Min/Max) durch direkteword.X,word.Y,word.Width,word.Height-Eigenschaften - Ersetzen Sie Multi-Frame-TIFF-Uploads pro Frame durch
input.LoadImageFrames(tiffPath) - Konvertieren Sie die vorgefertigte Modellfeldeextraktion der Formularerkennung in
CropRectangle-basierte Regionen-OCR für bekannte Dokumentvorlagen - Entfernen Sie die
RequestFailedException-Fangblöcke für HTTP 429 und 5xx; die Fehlerbehandlung auf Dateisystem- und Eingabevalidierungsausnahmen beschränken
Nach der Migration
- Überprüfen Sie, ob die Ausgabe der Klartextextraktion mit den Azure-Ergebnissen für eine repräsentative Stichprobe von mehr als 20 Dokumenten übereinstimmt oder diese übertrifft.
- Bestätigen, dass die Verarbeitung mehrseitiger PDFs Text für alle Seiten erzeugt, ohne dass im Monitoring Seitenzähler verwendet werden.
- Der Test der Mehrbild-TIFF-Verarbeitung liefert die gleiche Seitenzahl wie die Quellbildzahl.
- Sicherstellen, dass die Wortkonfidenzwerte im Bereich von 0 bis 100 liegen und dass Schwellenwertvergleiche korrekt funktionieren.
- Überprüfen Sie, ob die Koordinaten der Wortbegrenzungsrahmen korrekt mit dem Koordinatensystem des Quellbildes übereinstimmen.
- Führen Sie den Batchverarbeitungspfad aus und bestätigen Sie, dass er ohne Ratenbegrenzungsfehler oder Semaphorkonflikte abgeschlossen wird.
- Testen Sie in einer Umgebung ohne ausgehenden Internetzugang, um zu bestätigen, dass keine Azure-Endpunktaufrufe erfolgen.
- Bestätigen Sie, dass Docker- und Container-Bereitstellungen ohne ausgehende Firewall-Regeln für
cognitiveservices.azure.comerfolgreich starten - Prüfen Sie, dass der DI-Containerbau erfolgreich ist und dass
IronTesseract-Singleton korrekt aufgelöst wird - Überprüfen Sie, dass die
IRONOCR_LICENSE-Umgebungsvariable in allen Bereitstellungsumgebungen gesetzt ist und dass OCR lizenzierte (nicht wassergezeichnete) Ergebnisse produziert
Wichtigste Vorteile der Migration zu IronOCR
Die Kosten werden vorhersehbar. Die transaktionsbezogene Abrechnung von Azure Computer Visionläuft kontinuierlich. Ein einziger Monat mit hohem Dokumentenaufkommen kann die jährlichen Kosten einer IronOCR Lizenz übersteigen. Nach der Migration ist das OCR-Budget ein fester Budgetposten. Das Verarbeitungsvolumen kann wachsen - durch Geschäftsausweitung, Massenverarbeitung oder temporäre Spitzen - ohne eine größere Rechnung zu erzeugen. Die IronOCR-Lizenzierungsseite zeigt Tarifoptionen von $999 für eine Einzelentwicklerlizenz bis $2,999 für unbegrenzte Entwickler.
Der Anwendungsstack wird einfacher. Das Entfernen von Azure.AI.Vision.ImageAnalysis und Azure.AI.FormRecognizer beseitigt zwei NuGet-Pakete, zwei Azure-Ressourcenkonfigurationen, zwei Satz von Anmeldeinformationen und die gesamte asynchrone Polling-Infrastruktur. Dienstklassen, die zuvor aufgrund von Cloud-I/O eine async Task<string>-Signatur benötigten, werden zu synchronen Methoden. Die Fehlerbehandlung beschränkt sich nicht mehr nur auf Netzwerkausfälle, Ratenbegrenzungen und die Verfügbarkeit von Diensten, sondern konzentriert sich auch auf das Dateisystem und die Eingabevalidierung. Jeder zukünftige Entwickler, der mit dieser Codebasis arbeitet, muss weniger Code verstehen.
Die Dokumentendaten bleiben unter der Kontrolle der Organisation. Nach der Migration erfolgt die OCR-Verarbeitung lokal. Die Dokumente überschreiten keine Organisationsgrenzen, erscheinen nicht in der Azure-Telemetrie und unterliegen nicht den Datenaufbewahrungs- oder -verarbeitungsrichtlinien von Microsoft. HIPAA-pflichtige Einrichtungen, ITAR-Auftragnehmer, DSGVO-regulierte Organisationen und alle Teams mit Datenspeicherungsanforderungen können Dokumente ohne Compliance-Prüfung durch einen Cloud-Drittanbieter verarbeiten. Die Anleitungen zur Linux-Bereitstellung und zur Docker-Bereitstellung zeigen, wie IronOCR in eingeschränkten Umgebungen eingesetzt werden kann.
Der Batch-Durchsatz skaliert mit der Hardware. Die Obergrenze von 10 Transaktionen pro Sekunde (TPS) der Azure-S1-Stufe ist eine feste Grenze, die durch Warteschlangen, Drosselung oder ein Upgrade der Stufe umgangen werden kann. Nach der Migration sättigen gleichzeitige OCR-Jobs die verfügbaren CPU-Kerne, ohne dass eine von Diensten auferlegte Begrenzung besteht. Ein Vier-Kern-Server kann vier parallele IronTesseract-Instanzen gleichzeitig ausführen. Das Beispiel mit Multithreading veranschaulicht das Muster und zeigt, dass der Durchsatz mit der Anzahl der Kerne skaliert.
Vorverarbeitungsfehler lassen sich im Code beheben. Die serverseitige Bildverbesserung von Azure Computer Visionist eine Blackbox. Wenn ein Scan leere oder wenig vertrauenswürdige Ergebnisse liefert, gibt es nur die Optionen, diese zu akzeptieren oder extern vorzuverarbeiten, bevor sie mit zusätzlichen Kosten erneut hochgeladen werden. Die OcrInput-Pipeline von IronOCR stellt Entzerrung, Rauschreduktion, Kontrast, Binarisierung, Skalierung, Schärfen und tiefe Rauschentfernung als erstklassige Methoden zur Verfügung. Problematische Scan-Typen werden zu anpassbaren Parametern. Auf der Seite mit den Vorverarbeitungsfunktionen ist der vollständige Filtersatz aufgelistet, mit Hinweisen darauf, welche Filter welche Scanfehler beheben.
Häufig gestellte Fragen
Warum sollte ich von Azure Computer Vision OCR zu IronOCR migrieren?
Zu den häufigsten Gründen gehören die Beseitigung der Komplexität der COM-Interoperabilität, die Ablösung der dateibasierten Lizenzverwaltung, die Vermeidung der seitenweisen Abrechnung, die Ermöglichung der Docker-/Container-Bereitstellung und die Einführung eines NuGet-nativen Workflows, der sich in die Standard-.NET-Werkzeuge integriert.
Was sind die wichtigsten Code-Änderungen bei der Migration von Azure Computer Vision OCR zu IronOCR?
Ersetzen Sie die Initialisierungssequenzen von Azure Computer Vision durch die Instanziierung von IronTesseract, entfernen Sie das COM-Lebenszyklusmanagement (explizite Create/Load/Close-Muster) und aktualisieren Sie die Namen der Ergebniseigenschaften. Das Ergebnis sind deutlich weniger Boilerplate-Zeilen.
Wie installiere ich IronOCR, um die Migration zu beginnen?
Führen Sie 'Install-Package IronOcr' in der Paketmanager-Konsole oder 'dotnet add package IronOcr' in der CLI aus. Sprachpakete sind separate Pakete: 'dotnet add package IronOcr.Languages.French' für Französisch, zum Beispiel.
Kann IronOCR die OCR-Genauigkeit von Azure Computer Vision OCR für Standardgeschäftsdokumente erreichen?
IronOCR erzielt eine hohe Genauigkeit bei Standardgeschäftsinhalten wie Rechnungen, Verträgen, Quittungen und getippten Formularen. Bildvorverarbeitungsfilter (Schräglagenkorrektur, Rauschunterdrückung, Kontrastverbesserung) verbessern die Erkennungsgenauigkeit bei verschlechterten Eingaben weiter.
Wie geht IronOCR mit den Sprachdaten um, die von Azure Computer Vision OCR separat installiert werden?
Die Sprachdaten in IronOCR werden als NuGet-Pakete verteilt. mit 'dotnet add package IronOcr.Languages.German' wird die deutsche Unterstützung installiert. Es sind keine manuellen Dateiplatzierungen oder Verzeichnispfade erforderlich.
Erfordert die Migration von Azure Computer Vision OCR zu IronOCR Änderungen an der Bereitstellungsinfrastruktur?
IronOCR erfordert weniger Änderungen an der Infrastruktur als Azure Computer Vision OCR. Es gibt keine SDK-Binärpfade, keine Platzierung von Lizenzdateien oder Lizenzserverkonfigurationen. Das NuGet-Paket enthält die komplette OCR-Engine, und der Lizenzschlüssel ist eine im Anwendungscode festgelegte Zeichenfolge.
Wie konfiguriere ich die IronOCR-Lizenzierung nach der Migration?
Weisen Sie IronOcr.License.LicenseKey = "YOUR-KEY" im Startup-Code der Anwendung zu. In Docker oder Kubernetes speichern Sie den Schlüssel als Umgebungsvariable und lesen ihn beim Starten aus. Verwenden Sie License.IsValidLicense zur Validierung, bevor Sie den Datenverkehr akzeptieren.
Kann IronOCR PDFs auf die gleiche Weise verarbeiten wie Azure Computer Vision?
Ja, IronOCR liest sowohl native als auch gescannte PDFs. Instanziieren Sie IronTesseract, rufen Sie ocr.Read(input) auf, wobei input ein PDF-Pfad oder OcrPdfInput ist, und iterieren Sie die OcrResult-Seiten. Es ist keine separate PDF-Rendering-Pipeline erforderlich.
Wie handhabt IronOCR das Threading bei der Verarbeitung großer Datenmengen?
IronTesseract kann sicher pro Thread instanziiert werden. Sie können eine Instanz pro Thread in einem Parallel.ForEach- oder Task-Pool aufsetzen, OCR gleichzeitig ausführen und jede Instanz nach Abschluss entsorgen. Es ist kein globaler Zustand oder Sperren erforderlich.
Welche Ausgabeformate unterstützt IronOCR nach der Textextraktion?
IronOCR liefert strukturierte Ergebnisse mit Text, Wortkoordinaten, Konfidenzwerten und Seitenstruktur. Zu den Exportoptionen gehören reiner Text, durchsuchbare PDF-Dateien und strukturierte Ergebnisobjekte für die nachgeschaltete Verarbeitung.
Ist die Preisgestaltung von IronOCR vorhersehbarer als die von Azure Computer Vision OCR für die Skalierung von Workloads?
IronOCR verwendet eine pauschale, unbefristete Lizenzierung ohne Seiten- oder Volumengebühren. Egal, ob Sie 10.000 oder 10 Millionen Seiten verarbeiten, die Lizenzkosten bleiben konstant. Optionen für Volumen- und Teamlizenzen finden Sie auf der IronOCR-Preisseite.
Was passiert mit meinen bestehenden Tests nach der Migration von Azure Computer Vision OCR zu IronOCR?
Tests, die extrahierte Textinhalte überprüfen, sollten auch nach der Migration bestehen. Tests, die API-Aufrufmuster oder den Lebenszyklus von COM-Objekten validieren, müssen aktualisiert werden, um das einfachere Initialisierungs- und Ergebnismodell von IronOCR widerzuspiegeln.

