Umstellung von AWS Textract auf IronOCR
Dieser Leitfaden führt Sie durch eine vollständige Migration von AWS Textractzu IronOCR for .NET Entwickler, die eine lokale Dokumentenverarbeitung ohne Cloud-Abhängigkeiten benötigen. Es umfasst die Entfernung der Anmeldeinformationen, die Beseitigung der asynchronen Pipeline, die Eliminierung von S3 und die spezifischen Codeersetzungen, die erforderlich sind, um jede Textract-Operation in ihr IronOCR Äquivalent zu überführen.
Warum von AWS Textractmigrieren?
AWS Textract ist ein leistungsfähiger Managed Service, aber seine Architektur verursacht Kosten – finanzieller und betrieblicher Art –, die mit dem Wachstum Ihres Dokumenten-Workloads steigen. Teams, die professionelle Dokumentenverarbeitungssysteme entwickeln, stoßen früher oder später auf all diese Reibungspunkte.
Die AWS-Zugangsdateninfrastruktur erschwert jede Bereitstellung. Jede Umgebung, in der Textract-Code ausgeführt wird, benötigt AWS-Zugangsdaten: IAM-Benutzer oder -Rolle, Zugriffsschlüssel und -geheimnis, Regionskonfiguration und – für die PDF-Verarbeitung – zusätzlich S3-Bucket-Berechtigungen. Das sind fünf separate Konfigurationsschritte, bevor die erste Seite verarbeitet wird. Produktionsbereitstellungen erfordern Richtlinien zur Drehung von Anmeldeinformationen, sichere Injektion in Docker-Container oder Kubernetes-Pods und Überwachung für AccessDeniedException, wenn IAM-Richtlinienabweichungen stille Fehler verursachen.IronOCR benötigt eine einzige Zeichenkette: einen Lizenzschlüssel, der einmalig beim Start festgelegt wird.
Preise pro Seite haben keine Obergrenze. Der Basissatz von 0,0015 $ pro Seite für DetectDocumentText ist die Untergrenze, nicht die Kosten. Jedes Dokument mit Tabellen löst AnalyzeDocument bei 0,015 $ pro Seite aus - das Zehnfache der Grundrate. Formulare kosten zusätzlich 0,05 $ pro Seite. Ein gemischter Dokumentenworkflow mit 100.000 Seiten pro Monat und Tabellenextraktion kostet 18.000 US-Dollar pro Jahr, wobei diese Zahl jedes Jahr im Januar zurückgesetzt wird. Die IronOCR Professional -Lizenz kostet einmalig 2.999 US-Dollar. Danach betragen die Kosten pro Seite unabhängig vom Umfang null.
Die asynchrone PDF-Pipeline ist eine Wartungshaftung. Das Verarbeiten eines mehrseitigen PDFs durch Textract erfordert fünf verschiedene Phasen: S3-Upload, StartDocumentTextDetection, Polling-Schleife, Paginierte Ergebnisabfrage und S3-Bereinigung. Jede Phase ist ein unabhängiger Fehlermodus, der eine eigene Fehlerbehandlung, Rückversuchsstrategie und Timeout-Management erfordert. Teams, die diese Pipeline in die Produktion überführen, berichten übereinstimmend, dass sie mehr Zeit für deren Wartung aufwenden als für deren Entwicklung.IronOCR liest eine PDF-Datei mit nur zwei Codezeilen.
Kein Internetzugang bedeutet kein Textract. Docker-Container in isolierten Netzwerksegmenten, On-Premise-Server, abgeschottete Forschungsumgebungen und industrielle Systeme mit Beschränkungen des ausgehenden Datenverkehrs haben alle eine Gemeinsamkeit: Textract ist nicht verfügbar.IronOCR wird als NuGet Paket installiert und funktioniert nach dem ersten Herunterladen des Pakets ohne Netzwerkaufrufe.
Bei jedem Aufruf verlassen Daten Ihre Infrastruktur. Jeder OCR-Vorgang mit Textract überträgt Dokumentinhalte an die Server von Amazon. Für Organisationen im Gesundheitswesen, die PHI verarbeiten, für Rüstungsunternehmen, die CUI handhaben, für Rechtsteams, die privilegierte Kommunikation verarbeiten, und für Finanzinstitute, die Zahlungskartenbilder verarbeiten, handelt es sich hierbei nicht um eine Konfigurationsoption – es handelt sich um eine architektonische Einschränkung, die den Einsatz von Textract in regulierten Umgebungen gänzlich ausschließt.IronOCR verarbeitet Prozesse auf Ihrer Hardware. Es gibt keinen Cloud-Modus zum Deaktivieren; Die lokale Verarbeitung ist der einzige Modus.
Ratenbegrenzungen beschränken den Batch-Durchsatz. Das Standard-StartDocumentTextDetection TPS-Limit beträgt 5 Anfragen pro Sekunde. Batch-Jobs, die Hunderte von Dokumenten verarbeiten, erfordern SemaphoreSlim Drosselung, exponentielles Zurückgehen bei ProvisionedThroughputExceededException und Ratenauffüllungs-Timer. Für die Beantragung einer Erhöhung der Transaktionsanzahl pro Sekunde (TPS) ist ein formeller AWS-Supportfall erforderlich, dessen Ausgang nicht garantiert ist.IronOCR verarbeitet so schnell, wie es die lokale CPU erlaubt - ein 16-Kern-Server verarbeitet 16 Dokumente gleichzeitig mit einem einfachen Parallel.ForEach, keine Dienststufenaushandlung erforderlich.
Das grundsätzliche Problem
Jeder Textract-Einsatz beginnt mit dieser Zeremonie – bevor auch nur eine einzige Seite verarbeitet werden kann:
// Textract: five environment variables, an IAM role, and an S3 bucket
// — all required before the first OCR call
// Environment must have:
// AWS_ACCESS_KEY_ID=AKIA...
// AWS_SECRET_ACCESS_KEY=...
// AWS_DEFAULT_REGION=us-east-1
// IAM role: textract:DetectDocumentText, textract:AnalyzeDocument
// IAM role: s3:PutObject, s3:DeleteObject (for any PDF processing)
// S3 bucket: my-textract-staging-bucket (with lifecycle policy to prevent cost accumulation)
public class TextractOcrService
{
private readonly AmazonTextractClient _textractClient;
private readonly AmazonS3Client _s3Client; // required for PDFs
public TextractOcrService()
{
// Fails with AmazonClientException if credentials not found in chain
_textractClient = new AmazonTextractClient(Amazon.RegionEndpoint.USEast1);
_s3Client = new AmazonS3Client(Amazon.RegionEndpoint.USEast1);
}
}
// Textract: five environment variables, an IAM role, and an S3 bucket
// — all required before the first OCR call
// Environment must have:
// AWS_ACCESS_KEY_ID=AKIA...
// AWS_SECRET_ACCESS_KEY=...
// AWS_DEFAULT_REGION=us-east-1
// IAM role: textract:DetectDocumentText, textract:AnalyzeDocument
// IAM role: s3:PutObject, s3:DeleteObject (for any PDF processing)
// S3 bucket: my-textract-staging-bucket (with lifecycle policy to prevent cost accumulation)
public class TextractOcrService
{
private readonly AmazonTextractClient _textractClient;
private readonly AmazonS3Client _s3Client; // required for PDFs
public TextractOcrService()
{
// Fails with AmazonClientException if credentials not found in chain
_textractClient = new AmazonTextractClient(Amazon.RegionEndpoint.USEast1);
_s3Client = new AmazonS3Client(Amazon.RegionEndpoint.USEast1);
}
}
Imports Amazon
Imports Amazon.Textract
Imports Amazon.S3
' Textract: five environment variables, an IAM role, and an S3 bucket
' — all required before the first OCR call
' Environment must have:
' AWS_ACCESS_KEY_ID=AKIA...
' AWS_SECRET_ACCESS_KEY=...
' AWS_DEFAULT_REGION=us-east-1
' IAM role: textract:DetectDocumentText, textract:AnalyzeDocument
' IAM role: s3:PutObject, s3:DeleteObject (for any PDF processing)
' S3 bucket: my-textract-staging-bucket (with lifecycle policy to prevent cost accumulation)
Public Class TextractOcrService
Private ReadOnly _textractClient As AmazonTextractClient
Private ReadOnly _s3Client As AmazonS3Client ' required for PDFs
Public Sub New()
' Fails with AmazonClientException if credentials not found in chain
_textractClient = New AmazonTextractClient(Amazon.RegionEndpoint.USEast1)
_s3Client = New AmazonS3Client(Amazon.RegionEndpoint.USEast1)
End Sub
End Class
IronOCR ersetzt die gesamte Anmeldeinformationskette durch eine einzige Zuweisung:
// IronOCR: one line, one string, done
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
// Or from environment (no IAM, no rotation, no S3)
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
// IronOCR: one line, one string, done
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
// Or from environment (no IAM, no rotation, no S3)
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
Imports System
' IronOCR: one line, one string, done
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
' Or from environment (no IAM, no rotation, no S3)
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
IronOCR vs. AWS Textract: Funktionsvergleich
Die folgende Tabelle vergleicht die Fähigkeiten beider Bibliotheken anhand der für Migrationsentscheidungen relevantesten Dimensionen.
| Feature | AWS Textract | IronOCR |
|---|---|---|
| Bearbeitungsort | Amazon Cloud (obligatorisch) | Nur lokal / vor Ort |
| Internet erforderlich | Stets | Niemals |
| Einrichtung der Anmeldeinformationen | IAM-Benutzer/Rolle + optionaler S3-Bucket | Einzelner Lizenzschlüssel |
| Mehrseitiges PDF | Erfordert S3-Staging + asynchronen Job | Direkte synchrone input.LoadPdf() |
| Passwortgeschütztes PDF | Nicht unterstützt | input.LoadPdf(path, Password: "x") |
| Multi-Frame TIFF | Nicht unterstützt | input.LoadImageFrames("multi.tiff") |
| Asynchrone Abfrage erforderlich | Ja (für alle PDFs) | Nein – standardmäßig synchron. |
| Ratengrenzen | 5 TPS Standard (StartDocumentTextDetection) | Keine – nur CPU-gebunden |
| Kosten pro Seite | 0,0015–0,065 US-Dollar pro Seite | 0 € nach Lizenzkauf |
| Automatische Vorverarbeitung | Intern, nicht konfigurierbar | Entzerren, Rauschen entfernen, Kontrast erhöhen, Binärisierung, Schärfen, Skalieren |
| Durchsuchbare PDF-Ausgabe | Nicht verfügbar | result.SaveAsSearchablePdf() |
| hOCR-Export | Nicht verfügbar | result.SaveAsHocrFile() |
| Barcode-Lesung | Nicht verfügbar | ocr.Configuration.ReadBarCodes = true |
| Regionsbasierte OCR | Über AnalyzeDocument + Blockbeziehungen | CropRectangle(x, y, width, height) |
| Strukturierte Ergebnisse | Flache List<Block> gefiltert nach BlockType |
Typisierte Pages, Paragraphs, Lines, Words |
| Unterstützte Sprachen | Englischsprachige Vorliebe (weitere auswählen) | Über 125 Sprachpakete via NuGet |
| Mehrsprachige Simultanübertragung | Nicht unterstützt | OcrLanguage.French + OcrLanguage.German |
| Air-Gapped-Einsatz | Nicht möglich | Vollständig unterstützt |
| HIPAA ohne BAA | Nicht möglich | Kein externer Prozessor – nur lokal |
| Docker-Bereitstellung | Erfordert die Eingabe von AWS-Anmeldeinformationen | Keine Anmeldeinformationen erforderlich – einfaches NuGet Paket |
| Plattformübergreifend | AWS-verwaltet (nur Linux) | Windows, Linux, macOS, Docker, Azure, AWS EC2 |
| Lizenzierungsmodell | Abrechnung pro Seite (laufende Kosten) | Unbefristetes einmaliges ($999 / 1.499 $ / 2.999 $) |
Schnellstart: Migration von AWS Textractzu IronOCR
Schritt 1: Ersetzen des NuGet-Pakets
Entfernen Sie die AWS SDK-Pakete:
dotnet remove package AWSSDK.Textract
dotnet remove package AWSSDK.S3
dotnet remove package AWSSDK.Core
dotnet remove package AWSSDK.Textract
dotnet remove package AWSSDK.S3
dotnet remove package AWSSDK.Core
Installieren Sie IronOCR über NuGet :
dotnet add package IronOcr
Schritt 2: Namespaces aktualisieren
Ersetzen Sie alle AWS-Namespace-Importe durch den einzelnen IronOCR Namespace:
// Before (AWS Textract)
using Amazon.Textract;
using Amazon.Textract.Model;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.Runtime;
// After (IronOCR)
using IronOcr;
// Before (AWS Textract)
using Amazon.Textract;
using Amazon.Textract.Model;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.Runtime;
// After (IronOCR)
using IronOcr;
Imports Amazon.Textract
Imports Amazon.Textract.Model
Imports Amazon.S3
Imports Amazon.S3.Model
Imports Amazon.Runtime
Imports IronOcr
Schritt 3: Lizenz initialisieren
Fügen Sie die Lizenzinitialisierung einmalig beim Anwendungsstart hinzu. Entfernen Sie alle eingerichteten AWS-Anmeldeinformationsumgebungsvariablen.
// Remove from startup:
// AWS_ACCESS_KEY_ID environment variable
// AWS_SECRET_ACCESS_KEY environment variable
// AWS_DEFAULT_REGION environment variable
// ~/.aws/credentials file entries
// IAM role bindings on the execution context
// Add instead:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
// Remove from startup:
// AWS_ACCESS_KEY_ID environment variable
// AWS_SECRET_ACCESS_KEY environment variable
// AWS_DEFAULT_REGION environment variable
// ~/.aws/credentials file entries
// IAM role bindings on the execution context
// Add instead:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
' Remove from startup:
' AWS_ACCESS_KEY_ID environment variable
' AWS_SECRET_ACCESS_KEY environment variable
' AWS_DEFAULT_REGION environment variable
' ~/.aws/credentials file entries
' IAM role bindings on the execution context
' Add instead:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Beispiele für die Code-Migration
Ersetzen des AmazonTextractClient-Konstruktors und der IAM-Konfiguration
Die sichtbarste Änderung bei der Migration ist die Clientinitialisierung. Textract benötigt einen Client mit Dependency Injection und zugrundeliegender Credential Resolution – das heißt, sowohl der Client als auch alle seine Abhängigkeiten müssen in jeder Bereitstellungsumgebung vorkonfiguriert sein. Jede Fehlkonfiguration schlägt leise fehl, bis der erste OCR-Aufruf AmazonClientException auslöst.
AWS Textract-Ansatz:
// Requires: NuGet AWSSDK.Textract, AWSSDK.S3
// Requires: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION in environment
// Requires: IAM policy with textract:* and s3:PutObject, s3:DeleteObject
using Amazon.S3;
using Amazon.Textract;
public class DocumentOcrService
{
private readonly AmazonTextractClient _textractClient;
private readonly AmazonS3Client _s3Client;
private readonly string _stagingBucket;
public DocumentOcrService(IConfiguration config)
{
// Credential chain: environment → ~/.aws/credentials → EC2 instance profile
// Fails if none found — runtime exception, not compile-time
_textractClient = new AmazonTextractClient(Amazon.RegionEndpoint.USEast1);
_s3Client = new AmazonS3Client(Amazon.RegionEndpoint.USEast1);
_stagingBucket = config["Textract:StagingBucket"]; // bucket must exist and have permissions
}
public async Task<string> ReadImageAsync(string imagePath)
{
var bytes = File.ReadAllBytes(imagePath);
var response = await _textractClient.DetectDocumentTextAsync(
new DetectDocumentTextRequest
{
Document = new Document { Bytes = new MemoryStream(bytes) }
});
return string.Join("\n", response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
}
}
// Requires: NuGet AWSSDK.Textract, AWSSDK.S3
// Requires: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION in environment
// Requires: IAM policy with textract:* and s3:PutObject, s3:DeleteObject
using Amazon.S3;
using Amazon.Textract;
public class DocumentOcrService
{
private readonly AmazonTextractClient _textractClient;
private readonly AmazonS3Client _s3Client;
private readonly string _stagingBucket;
public DocumentOcrService(IConfiguration config)
{
// Credential chain: environment → ~/.aws/credentials → EC2 instance profile
// Fails if none found — runtime exception, not compile-time
_textractClient = new AmazonTextractClient(Amazon.RegionEndpoint.USEast1);
_s3Client = new AmazonS3Client(Amazon.RegionEndpoint.USEast1);
_stagingBucket = config["Textract:StagingBucket"]; // bucket must exist and have permissions
}
public async Task<string> ReadImageAsync(string imagePath)
{
var bytes = File.ReadAllBytes(imagePath);
var response = await _textractClient.DetectDocumentTextAsync(
new DetectDocumentTextRequest
{
Document = new Document { Bytes = new MemoryStream(bytes) }
});
return string.Join("\n", response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
}
}
Imports Amazon.S3
Imports Amazon.Textract
Imports System.IO
Imports System.Threading.Tasks
Imports Microsoft.Extensions.Configuration
Public Class DocumentOcrService
Private ReadOnly _textractClient As AmazonTextractClient
Private ReadOnly _s3Client As AmazonS3Client
Private ReadOnly _stagingBucket As String
Public Sub New(config As IConfiguration)
' Credential chain: environment → ~/.aws/credentials → EC2 instance profile
' Fails if none found — runtime exception, not compile-time
_textractClient = New AmazonTextractClient(Amazon.RegionEndpoint.USEast1)
_s3Client = New AmazonS3Client(Amazon.RegionEndpoint.USEast1)
_stagingBucket = config("Textract:StagingBucket") ' bucket must exist and have permissions
End Sub
Public Async Function ReadImageAsync(imagePath As String) As Task(Of String)
Dim bytes = File.ReadAllBytes(imagePath)
Dim response = Await _textractClient.DetectDocumentTextAsync(
New DetectDocumentTextRequest With {
.Document = New Document With {.Bytes = New MemoryStream(bytes)}
})
Return String.Join(vbCrLf, response.Blocks _
.Where(Function(b) b.BlockType = BlockType.LINE) _
.Select(Function(b) b.Text))
End Function
End Class
IronOCR Ansatz:
// Requires: NuGet IronOcr
// Requires: one license key string — nothing else
using IronOcr;
public class DocumentOcrService
{
private readonly IronTesseract _ocr;
public DocumentOcrService()
{
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; // or set at Program.cs startup
_ocr = new IronTesseract();
}
public string ReadImage(string imagePath)
{
// No credential chain, no region, no bucket — just read
return _ocr.Read(imagePath).Text;
}
}
// Requires: NuGet IronOcr
// Requires: one license key string — nothing else
using IronOcr;
public class DocumentOcrService
{
private readonly IronTesseract _ocr;
public DocumentOcrService()
{
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; // or set at Program.cs startup
_ocr = new IronTesseract();
}
public string ReadImage(string imagePath)
{
// No credential chain, no region, no bucket — just read
return _ocr.Read(imagePath).Text;
}
}
Imports IronOcr
Public Class DocumentOcrService
Private ReadOnly _ocr As IronTesseract
Public Sub New()
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY" ' or set at Program.vb startup
_ocr = New IronTesseract()
End Sub
Public Function ReadImage(imagePath As String) As String
' No credential chain, no region, no bucket — just read
Return _ocr.Read(imagePath).Text
End Function
End Class
Die gesamte AWS-Anmeldeinfrastruktur – IAM-Rollen, Umgebungsvariablen, ~/.aws/credentials-Dateien, S3-Bucket-Lebenszyklusrichtlinien und Regionskonfiguration – wird entfernt. Der DocumentOcrService-Konstruktor wechselt von einer 3-Abhängigkeiten-Injektionsstelle mit Laufzeit-Fehlermodi zu einer einzigen Lizenzzuweisung. Für Details zu den Bereitstellungsmustern siehe den IronTesseract-Einrichtungsleitfaden .
Ersetzen von StartDocumentTextDetection durch TIFF-Mehrbildverarbeitung
Die asynchrone Job-API von Textract (StartDocumentTextDetection) wird für jedes mehrseitige Dokument benötigt. Ein gängiges Muster bei der Digitalisierung von Dokumenten ist der Empfang gescannter Dokumente als Multi-Frame-TIFFs – ein Frame pro gescannter Seite. Textract kann TIFF-Eingaben überhaupt nicht direkt verarbeiten; Das Dokument muss in das PDF-Format konvertiert und in S3 hochgeladen werden, bevor ein Auftrag beginnen kann.IronOCR liest TIFF-Dateien mit mehreren Einzelbildern nativ.
AWS Textract-Ansatz:
// Textract cannot accept TIFF directly — requires PDF conversion + S3 + async job
// This shows the conversion overhead plus the minimum async pipeline
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.Textract;
using Amazon.Textract.Model;
public async Task<string> ProcessScannedTiffAsync(string tiffPath, string s3Bucket)
{
// Step 0: Convert TIFF to PDF first (Textract does not accept TIFF for async jobs)
// Requires a separate PDF conversion library — not shown here
var pdfPath = Path.ChangeExtension(tiffPath, ".pdf");
ConvertTiffToPdf(tiffPath, pdfPath); // external dependency required
// Step 1: Upload converted PDF to S3
var s3Key = $"staging/{Guid.NewGuid()}.pdf";
using (var stream = File.OpenRead(pdfPath))
{
await _s3Client.PutObjectAsync(new PutObjectRequest
{
BucketName = s3Bucket,
Key = s3Key,
InputStream = stream
});
}
try
{
// Step 2: Start async detection job
var startResp = await _textractClient.StartDocumentTextDetectionAsync(
new StartDocumentTextDetectionRequest
{
DocumentLocation = new DocumentLocation
{
S3Object = new S3Object { Bucket = s3Bucket, Name = s3Key }
}
});
// Step 3: Poll every 5 seconds — can block for 30–120 seconds on large files
GetDocumentTextDetectionResponse pollResp;
do
{
await Task.Delay(5000);
pollResp = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest { JobId = startResp.JobId });
} while (pollResp.JobStatus == JobStatus.IN_PROGRESS);
if (pollResp.JobStatus != JobStatus.SUCCEEDED)
throw new Exception($"Job failed: {pollResp.StatusMessage}");
// Step 4: Collect paginated results
var text = new StringBuilder();
string token = null;
do
{
var page = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest
{
JobId = startResp.JobId,
NextToken = token
});
foreach (var block in page.Blocks.Where(b => b.BlockType == BlockType.LINE))
text.AppendLine(block.Text);
token = page.NextToken;
} while (token != null);
return text.ToString();
}
finally
{
// Step 5: Clean up S3 — orphaned objects accumulate storage costs
await _s3Client.DeleteObjectAsync(s3Bucket, s3Key);
File.Delete(pdfPath); // clean up intermediate PDF
}
}
// Textract cannot accept TIFF directly — requires PDF conversion + S3 + async job
// This shows the conversion overhead plus the minimum async pipeline
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.Textract;
using Amazon.Textract.Model;
public async Task<string> ProcessScannedTiffAsync(string tiffPath, string s3Bucket)
{
// Step 0: Convert TIFF to PDF first (Textract does not accept TIFF for async jobs)
// Requires a separate PDF conversion library — not shown here
var pdfPath = Path.ChangeExtension(tiffPath, ".pdf");
ConvertTiffToPdf(tiffPath, pdfPath); // external dependency required
// Step 1: Upload converted PDF to S3
var s3Key = $"staging/{Guid.NewGuid()}.pdf";
using (var stream = File.OpenRead(pdfPath))
{
await _s3Client.PutObjectAsync(new PutObjectRequest
{
BucketName = s3Bucket,
Key = s3Key,
InputStream = stream
});
}
try
{
// Step 2: Start async detection job
var startResp = await _textractClient.StartDocumentTextDetectionAsync(
new StartDocumentTextDetectionRequest
{
DocumentLocation = new DocumentLocation
{
S3Object = new S3Object { Bucket = s3Bucket, Name = s3Key }
}
});
// Step 3: Poll every 5 seconds — can block for 30–120 seconds on large files
GetDocumentTextDetectionResponse pollResp;
do
{
await Task.Delay(5000);
pollResp = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest { JobId = startResp.JobId });
} while (pollResp.JobStatus == JobStatus.IN_PROGRESS);
if (pollResp.JobStatus != JobStatus.SUCCEEDED)
throw new Exception($"Job failed: {pollResp.StatusMessage}");
// Step 4: Collect paginated results
var text = new StringBuilder();
string token = null;
do
{
var page = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest
{
JobId = startResp.JobId,
NextToken = token
});
foreach (var block in page.Blocks.Where(b => b.BlockType == BlockType.LINE))
text.AppendLine(block.Text);
token = page.NextToken;
} while (token != null);
return text.ToString();
}
finally
{
// Step 5: Clean up S3 — orphaned objects accumulate storage costs
await _s3Client.DeleteObjectAsync(s3Bucket, s3Key);
File.Delete(pdfPath); // clean up intermediate PDF
}
}
Imports Amazon.S3
Imports Amazon.S3.Model
Imports Amazon.Textract
Imports Amazon.Textract.Model
Imports System.IO
Imports System.Text
Imports System.Threading.Tasks
Public Async Function ProcessScannedTiffAsync(tiffPath As String, s3Bucket As String) As Task(Of String)
' Step 0: Convert TIFF to PDF first (Textract does not accept TIFF for async jobs)
' Requires a separate PDF conversion library — not shown here
Dim pdfPath = Path.ChangeExtension(tiffPath, ".pdf")
ConvertTiffToPdf(tiffPath, pdfPath) ' external dependency required
' Step 1: Upload converted PDF to S3
Dim s3Key = $"staging/{Guid.NewGuid()}.pdf"
Using stream = File.OpenRead(pdfPath)
Await _s3Client.PutObjectAsync(New PutObjectRequest With {
.BucketName = s3Bucket,
.Key = s3Key,
.InputStream = stream
})
End Using
Try
' Step 2: Start async detection job
Dim startResp = Await _textractClient.StartDocumentTextDetectionAsync(
New StartDocumentTextDetectionRequest With {
.DocumentLocation = New DocumentLocation With {
.S3Object = New S3Object With {.Bucket = s3Bucket, .Name = s3Key}
}
})
' Step 3: Poll every 5 seconds — can block for 30–120 seconds on large files
Dim pollResp As GetDocumentTextDetectionResponse
Do
Await Task.Delay(5000)
pollResp = Await _textractClient.GetDocumentTextDetectionAsync(
New GetDocumentTextDetectionRequest With {.JobId = startResp.JobId})
Loop While pollResp.JobStatus = JobStatus.IN_PROGRESS
If pollResp.JobStatus <> JobStatus.SUCCEEDED Then
Throw New Exception($"Job failed: {pollResp.StatusMessage}")
End If
' Step 4: Collect paginated results
Dim text = New StringBuilder()
Dim token As String = Nothing
Do
Dim page = Await _textractClient.GetDocumentTextDetectionAsync(
New GetDocumentTextDetectionRequest With {
.JobId = startResp.JobId,
.NextToken = token
})
For Each block In page.Blocks.Where(Function(b) b.BlockType = BlockType.LINE)
text.AppendLine(block.Text)
Next
token = page.NextToken
Loop While token IsNot Nothing
Return text.ToString()
Finally
' Step 5: Clean up S3 — orphaned objects accumulate storage costs
Await _s3Client.DeleteObjectAsync(s3Bucket, s3Key)
File.Delete(pdfPath) ' clean up intermediate PDF
End Try
End Function
IronOCR Ansatz:
//IronOCR reads multi-frame TIFF directly — no conversion, no S3, no polling
using IronOcr;
public string ProcessScannedTiff(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // reads all frames natively
var ocr = new IronTesseract();
var result = ocr.Read(input);
// Access per-page results if needed
foreach (var page in result.Pages)
Console.WriteLine($"Page {page.PageNumber}: {page.Words.Length} words detected");
return result.Text;
}
//IronOCR reads multi-frame TIFF directly — no conversion, no S3, no polling
using IronOcr;
public string ProcessScannedTiff(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // reads all frames natively
var ocr = new IronTesseract();
var result = ocr.Read(input);
// Access per-page results if needed
foreach (var page in result.Pages)
Console.WriteLine($"Page {page.PageNumber}: {page.Words.Length} words detected");
return result.Text;
}
Imports IronOcr
Public Function ProcessScannedTiff(tiffPath As String) As String
Using input As New OcrInput()
input.LoadImageFrames(tiffPath) ' reads all frames natively
Dim ocr As New IronTesseract()
Dim result = ocr.Read(input)
' Access per-page results if needed
For Each page In result.Pages
Console.WriteLine($"Page {page.PageNumber}: {page.Words.Length} words detected")
Next
Return result.Text
End Using
End Function
Der TIFF-zu-PDF-Konvertierungsschritt, der S3-Upload, die asynchrone Abfrageschleife, die paginierte Ergebnisakkumulation und die S3-Bereinigung werden allesamt eliminiert. Die IronOCR-Implementierung liest Frames direkt und bietet seitenweisen Zugriff durch result.Pages ohne Zwischenformatkonvertierung. Der Leitfaden zur TIFF- und GIF-Eingabe behandelt die Auswahl von Einzelbildern für Fälle, in denen nur eine Teilmenge der Einzelbilder benötigt wird.
Ersetzen von S3-Staging durch durchsuchbare PDF-Ausgabe
Ein typischer Anwendungsfall für Textract ist die Umwandlung gescannter PDF-Archive in ein durchsuchbares Format. Der Textract-Ansatz erfordert das Hochladen jeder PDF-Datei auf S3, das Ausführen des asynchronen Jobs, das Sammeln des extrahierten Textes und anschließend die Verwendung einer separaten PDF-Bibliothek, um diesen Text als durchsuchbare Ebene einzubetten.IronOCR führt den Scan durch und erstellt in einem einzigen Aufruf eine durchsuchbare PDF-Datei.
AWS Textract-Ansatz:
// Textract extracts text but cannot produce searchable PDFs
// Requires: S3 upload + async job + separate PDF library to embed the text layer
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.Textract;
using Amazon.Textract.Model;
public async Task<string> ExtractTextForSearchableLayerAsync(
string scannedPdfPath,
string s3Bucket)
{
// Must upload to S3 — cannot pass PDF bytes directly for async jobs
var key = $"ocr-input/{Guid.NewGuid()}.pdf";
await using (var fs = File.OpenRead(scannedPdfPath))
{
await _s3Client.PutObjectAsync(new PutObjectRequest
{
BucketName = s3Bucket,
Key = key,
InputStream = fs,
ContentType = "application/pdf"
});
}
string jobId;
try
{
var startResp = await _textractClient.StartDocumentTextDetectionAsync(
new StartDocumentTextDetectionRequest
{
DocumentLocation = new DocumentLocation
{
S3Object = new S3Object { Bucket = s3Bucket, Name = key }
}
});
jobId = startResp.JobId;
}
catch
{
await _s3Client.DeleteObjectAsync(s3Bucket, key);
throw;
}
// Poll — minimum 5s wait; typical 15–60s for a 10-page PDF
GetDocumentTextDetectionResponse pollResp;
int pollAttempts = 0;
const int maxAttempts = 120; // 10 minute timeout
do
{
await Task.Delay(5000);
pollResp = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest { JobId = jobId });
if (++pollAttempts >= maxAttempts)
throw new TimeoutException("Textract job timed out after 10 minutes");
} while (pollResp.JobStatus == JobStatus.IN_PROGRESS);
await _s3Client.DeleteObjectAsync(s3Bucket, key); // cleanup regardless of outcome
if (pollResp.JobStatus != JobStatus.SUCCEEDED)
throw new Exception($"Textract job status: {pollResp.JobStatus} — {pollResp.StatusMessage}");
// Return extracted text — caller must use a separate library to produce searchable PDF
return string.Join("\n", pollResp.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
// Caller still needs: iTextSharp, PdfSharp, or similar to embed text layer
}
// Textract extracts text but cannot produce searchable PDFs
// Requires: S3 upload + async job + separate PDF library to embed the text layer
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.Textract;
using Amazon.Textract.Model;
public async Task<string> ExtractTextForSearchableLayerAsync(
string scannedPdfPath,
string s3Bucket)
{
// Must upload to S3 — cannot pass PDF bytes directly for async jobs
var key = $"ocr-input/{Guid.NewGuid()}.pdf";
await using (var fs = File.OpenRead(scannedPdfPath))
{
await _s3Client.PutObjectAsync(new PutObjectRequest
{
BucketName = s3Bucket,
Key = key,
InputStream = fs,
ContentType = "application/pdf"
});
}
string jobId;
try
{
var startResp = await _textractClient.StartDocumentTextDetectionAsync(
new StartDocumentTextDetectionRequest
{
DocumentLocation = new DocumentLocation
{
S3Object = new S3Object { Bucket = s3Bucket, Name = key }
}
});
jobId = startResp.JobId;
}
catch
{
await _s3Client.DeleteObjectAsync(s3Bucket, key);
throw;
}
// Poll — minimum 5s wait; typical 15–60s for a 10-page PDF
GetDocumentTextDetectionResponse pollResp;
int pollAttempts = 0;
const int maxAttempts = 120; // 10 minute timeout
do
{
await Task.Delay(5000);
pollResp = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest { JobId = jobId });
if (++pollAttempts >= maxAttempts)
throw new TimeoutException("Textract job timed out after 10 minutes");
} while (pollResp.JobStatus == JobStatus.IN_PROGRESS);
await _s3Client.DeleteObjectAsync(s3Bucket, key); // cleanup regardless of outcome
if (pollResp.JobStatus != JobStatus.SUCCEEDED)
throw new Exception($"Textract job status: {pollResp.JobStatus} — {pollResp.StatusMessage}");
// Return extracted text — caller must use a separate library to produce searchable PDF
return string.Join("\n", pollResp.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
// Caller still needs: iTextSharp, PdfSharp, or similar to embed text layer
}
Imports Amazon.S3
Imports Amazon.S3.Model
Imports Amazon.Textract
Imports Amazon.Textract.Model
Public Async Function ExtractTextForSearchableLayerAsync(
scannedPdfPath As String,
s3Bucket As String) As Task(Of String)
' Must upload to S3 — cannot pass PDF bytes directly for async jobs
Dim key = $"ocr-input/{Guid.NewGuid()}.pdf"
Await Using fs = File.OpenRead(scannedPdfPath)
Await _s3Client.PutObjectAsync(New PutObjectRequest With {
.BucketName = s3Bucket,
.Key = key,
.InputStream = fs,
.ContentType = "application/pdf"
})
End Using
Dim jobId As String
Try
Dim startResp = Await _textractClient.StartDocumentTextDetectionAsync(
New StartDocumentTextDetectionRequest With {
.DocumentLocation = New DocumentLocation With {
.S3Object = New S3Object With {.Bucket = s3Bucket, .Name = key}
}
})
jobId = startResp.JobId
Catch
Await _s3Client.DeleteObjectAsync(s3Bucket, key)
Throw
End Try
' Poll — minimum 5s wait; typical 15–60s for a 10-page PDF
Dim pollResp As GetDocumentTextDetectionResponse
Dim pollAttempts As Integer = 0
Const maxAttempts As Integer = 120 ' 10 minute timeout
Do
Await Task.Delay(5000)
pollResp = Await _textractClient.GetDocumentTextDetectionAsync(
New GetDocumentTextDetectionRequest With {.JobId = jobId})
If System.Threading.Interlocked.Increment(pollAttempts) >= maxAttempts Then
Throw New TimeoutException("Textract job timed out after 10 minutes")
End If
Loop While pollResp.JobStatus = JobStatus.IN_PROGRESS
Await _s3Client.DeleteObjectAsync(s3Bucket, key) ' cleanup regardless of outcome
If pollResp.JobStatus <> JobStatus.SUCCEEDED Then
Throw New Exception($"Textract job status: {pollResp.JobStatus} — {pollResp.StatusMessage}")
End If
' Return extracted text — caller must use a separate library to produce searchable PDF
Return String.Join(vbLf, pollResp.Blocks _
.Where(Function(b) b.BlockType = BlockType.LINE) _
.Select(Function(b) b.Text))
' Caller still needs: iTextSharp, PdfSharp, or similar to embed text layer
End Function
IronOCR Ansatz:
//IronOCR reads the PDF and produces a searchable PDF output directly
// No S3, no polling, no external PDF library needed
using IronOcr;
public void ConvertToSearchablePdf(string scannedPdfPath, string outputPath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf(scannedPdfPath);
var result = ocr.Read(input);
// Embed extracted text as searchable layer in one call
result.SaveAsSearchablePdf(outputPath);
Console.WriteLine($"Pages processed: {result.Pages.Length}");
Console.WriteLine($"Overall confidence: {result.Confidence:F1}%");
}
//IronOCR reads the PDF and produces a searchable PDF output directly
// No S3, no polling, no external PDF library needed
using IronOcr;
public void ConvertToSearchablePdf(string scannedPdfPath, string outputPath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf(scannedPdfPath);
var result = ocr.Read(input);
// Embed extracted text as searchable layer in one call
result.SaveAsSearchablePdf(outputPath);
Console.WriteLine($"Pages processed: {result.Pages.Length}");
Console.WriteLine($"Overall confidence: {result.Confidence:F1}%");
}
Imports IronOcr
Public Sub ConvertToSearchablePdf(scannedPdfPath As String, outputPath As String)
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadPdf(scannedPdfPath)
Dim result = ocr.Read(input)
' Embed extracted text as searchable layer in one call
result.SaveAsSearchablePdf(outputPath)
Console.WriteLine($"Pages processed: {result.Pages.Length}")
Console.WriteLine($"Overall confidence: {result.Confidence:F1}%")
End Using
End Sub
Die Logik für das Polling-Timeout, das S3-Upload- und Bereinigungsmuster sowie die Abhängigkeit von der externen PDF-Bibliothek sind entfallen. SaveAsSearchablePdf bettet den erkannten Text direkt in die Ausgabedatei ein und macht jede gescannte Seite volltextdurchsuchbar ohne eine zweite Bibliothek. Der durchsuchbare PDF-Leitfaden behandelt Seitenauswahl, Komprimierungsoptionen und Metadateneinbettung. Für einen umfassenderen Kontext von PDF-OCR-Pipelines siehe den PDF-Eingabeleitfaden .
Ersetzen der AnalyzeDocument-Blockgraphendurchlaufung durch strukturierte Seitenergebnisse
Textracts AnalyzeDocument mit TABLES und FORMS liefert ein flaches List<Block>, bei dem jedes Element – Zeilen, Wörter, Zellen, Tabellenüberschriften, Formularschlüssel, Formularwerte – vom gleichen Block-Typ ist, der nur durch BlockType unterschieden und mit RelationshipType.CHILD-ID-Arrays verknüpft wird. Die Rekonstruktion der logischen Struktur eines Dokuments erfordert das Durchlaufen dieses Beziehungsgraphen.IronOCR gibt eine typisierte Hierarchie zurück: Seiten, Absätze, Zeilen, Wörter, jeweils mit Koordinatenzugriff.
AWS Textract-Ansatz:
// AnalyzeDocument with TABLES returns blocks that must be graph-traversed
// to determine which words belong to which paragraphs
using Amazon.Textract;
using Amazon.Textract.Model;
public async Task<List<DocumentSection>> ExtractDocumentStructureAsync(string imagePath)
{
var bytes = File.ReadAllBytes(imagePath);
var response = await _textractClient.AnalyzeDocumentAsync(
new AnalyzeDocumentRequest
{
Document = new Document { Bytes = new MemoryStream(bytes) },
FeatureTypes = new List<string> { "TABLES", "FORMS" }
// Note: AnalyzeDocument (TABLES) costs $0.015/page — 10x DetectDocumentText
});
var sections = new List<DocumentSection>();
// Filter LINE blocks for paragraph reconstruction
// LINE blocks are not grouped into paragraphs — must infer from Y proximity
var lineBlocks = response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.OrderBy(b => b.Geometry?.BoundingBox?.Top ?? 0)
.ToList();
// Group lines into pseudo-paragraphs by vertical gap heuristic
var currentParagraph = new List<Block>();
float? lastBottom = null;
const float paragraphGapThreshold = 0.02f; // 2% of page height
foreach (var line in lineBlocks)
{
var top = line.Geometry?.BoundingBox?.Top ?? 0;
var height = line.Geometry?.BoundingBox?.Height ?? 0;
if (lastBottom.HasValue && (top - lastBottom.Value) > paragraphGapThreshold)
{
if (currentParagraph.Any())
{
sections.Add(new DocumentSection
{
Text = string.Join(" ", currentParagraph.Select(b => b.Text)),
LineCount = currentParagraph.Count,
// Confidence: average of all LINE block confidence values
Confidence = (float)currentParagraph.Average(b => b.Confidence ?? 0)
});
currentParagraph.Clear();
}
}
currentParagraph.Add(line);
lastBottom = top + height;
}
if (currentParagraph.Any())
sections.Add(new DocumentSection
{
Text = string.Join(" ", currentParagraph.Select(b => b.Text)),
LineCount = currentParagraph.Count,
Confidence = (float)currentParagraph.Average(b => b.Confidence ?? 0)
});
return sections;
}
public class DocumentSection
{
public string Text { get; set; }
public int LineCount { get; set; }
public float Confidence { get; set; }
}
// AnalyzeDocument with TABLES returns blocks that must be graph-traversed
// to determine which words belong to which paragraphs
using Amazon.Textract;
using Amazon.Textract.Model;
public async Task<List<DocumentSection>> ExtractDocumentStructureAsync(string imagePath)
{
var bytes = File.ReadAllBytes(imagePath);
var response = await _textractClient.AnalyzeDocumentAsync(
new AnalyzeDocumentRequest
{
Document = new Document { Bytes = new MemoryStream(bytes) },
FeatureTypes = new List<string> { "TABLES", "FORMS" }
// Note: AnalyzeDocument (TABLES) costs $0.015/page — 10x DetectDocumentText
});
var sections = new List<DocumentSection>();
// Filter LINE blocks for paragraph reconstruction
// LINE blocks are not grouped into paragraphs — must infer from Y proximity
var lineBlocks = response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.OrderBy(b => b.Geometry?.BoundingBox?.Top ?? 0)
.ToList();
// Group lines into pseudo-paragraphs by vertical gap heuristic
var currentParagraph = new List<Block>();
float? lastBottom = null;
const float paragraphGapThreshold = 0.02f; // 2% of page height
foreach (var line in lineBlocks)
{
var top = line.Geometry?.BoundingBox?.Top ?? 0;
var height = line.Geometry?.BoundingBox?.Height ?? 0;
if (lastBottom.HasValue && (top - lastBottom.Value) > paragraphGapThreshold)
{
if (currentParagraph.Any())
{
sections.Add(new DocumentSection
{
Text = string.Join(" ", currentParagraph.Select(b => b.Text)),
LineCount = currentParagraph.Count,
// Confidence: average of all LINE block confidence values
Confidence = (float)currentParagraph.Average(b => b.Confidence ?? 0)
});
currentParagraph.Clear();
}
}
currentParagraph.Add(line);
lastBottom = top + height;
}
if (currentParagraph.Any())
sections.Add(new DocumentSection
{
Text = string.Join(" ", currentParagraph.Select(b => b.Text)),
LineCount = currentParagraph.Count,
Confidence = (float)currentParagraph.Average(b => b.Confidence ?? 0)
});
return sections;
}
public class DocumentSection
{
public string Text { get; set; }
public int LineCount { get; set; }
public float Confidence { get; set; }
}
Imports Amazon.Textract
Imports Amazon.Textract.Model
Imports System.IO
Imports System.Linq
Imports System.Threading.Tasks
Public Class DocumentProcessor
Private _textractClient As IAmazonTextract
Public Sub New(textractClient As IAmazonTextract)
_textractClient = textractClient
End Sub
Public Async Function ExtractDocumentStructureAsync(imagePath As String) As Task(Of List(Of DocumentSection))
Dim bytes = File.ReadAllBytes(imagePath)
Dim response = Await _textractClient.AnalyzeDocumentAsync(
New AnalyzeDocumentRequest With {
.Document = New Document With {.Bytes = New MemoryStream(bytes)},
.FeatureTypes = New List(Of String) From {"TABLES", "FORMS"}
})
Dim sections = New List(Of DocumentSection)()
Dim lineBlocks = response.Blocks _
.Where(Function(b) b.BlockType = BlockType.LINE) _
.OrderBy(Function(b) If(b.Geometry?.BoundingBox?.Top, 0)) _
.ToList()
Dim currentParagraph = New List(Of Block)()
Dim lastBottom As Single? = Nothing
Const paragraphGapThreshold As Single = 0.02F
For Each line In lineBlocks
Dim top = If(line.Geometry?.BoundingBox?.Top, 0)
Dim height = If(line.Geometry?.BoundingBox?.Height, 0)
If lastBottom.HasValue AndAlso (top - lastBottom.Value) > paragraphGapThreshold Then
If currentParagraph.Any() Then
sections.Add(New DocumentSection With {
.Text = String.Join(" ", currentParagraph.Select(Function(b) b.Text)),
.LineCount = currentParagraph.Count,
.Confidence = CSng(currentParagraph.Average(Function(b) If(b.Confidence, 0)))
})
currentParagraph.Clear()
End If
End If
currentParagraph.Add(line)
lastBottom = top + height
Next
If currentParagraph.Any() Then
sections.Add(New DocumentSection With {
.Text = String.Join(" ", currentParagraph.Select(Function(b) b.Text)),
.LineCount = currentParagraph.Count,
.Confidence = CSng(currentParagraph.Average(Function(b) If(b.Confidence, 0)))
})
End If
Return sections
End Function
End Class
Public Class DocumentSection
Public Property Text As String
Public Property LineCount As Integer
Public Property Confidence As Single
End Class
IronOCR Ansatz:
//IronOCR returns a typed hierarchy — paragraphs are first-class objects
// No block graph, no heuristic gap detection, no confidence averaging
using IronOcr;
public List<DocumentSection> ExtractDocumentStructure(string imagePath)
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
var sections = new List<DocumentSection>();
foreach (var page in result.Pages)
{
foreach (var paragraph in page.Paragraphs)
{
sections.Add(new DocumentSection
{
Text = paragraph.Text,
LineCount = paragraph.Lines.Length,
// Coordinate access: exact pixel position on the page
LocationX = paragraph.X,
LocationY = paragraph.Y,
Width = paragraph.Width,
Height = paragraph.Height,
Confidence = paragraph.Confidence
});
}
}
return sections;
}
public class DocumentSection
{
public string Text { get; set; }
public int LineCount { get; set; }
public int LocationX { get; set; }
public int LocationY { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public double Confidence { get; set; }
}
//IronOCR returns a typed hierarchy — paragraphs are first-class objects
// No block graph, no heuristic gap detection, no confidence averaging
using IronOcr;
public List<DocumentSection> ExtractDocumentStructure(string imagePath)
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
var sections = new List<DocumentSection>();
foreach (var page in result.Pages)
{
foreach (var paragraph in page.Paragraphs)
{
sections.Add(new DocumentSection
{
Text = paragraph.Text,
LineCount = paragraph.Lines.Length,
// Coordinate access: exact pixel position on the page
LocationX = paragraph.X,
LocationY = paragraph.Y,
Width = paragraph.Width,
Height = paragraph.Height,
Confidence = paragraph.Confidence
});
}
}
return sections;
}
public class DocumentSection
{
public string Text { get; set; }
public int LineCount { get; set; }
public int LocationX { get; set; }
public int LocationY { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public double Confidence { get; set; }
}
Imports IronOcr
Public Function ExtractDocumentStructure(imagePath As String) As List(Of DocumentSection)
Dim ocr As New IronTesseract()
Dim result = ocr.Read(imagePath)
Dim sections As New List(Of DocumentSection)()
For Each page In result.Pages
For Each paragraph In page.Paragraphs
sections.Add(New DocumentSection With {
.Text = paragraph.Text,
.LineCount = paragraph.Lines.Length,
.LocationX = paragraph.X,
.LocationY = paragraph.Y,
.Width = paragraph.Width,
.Height = paragraph.Height,
.Confidence = paragraph.Confidence
})
Next
Next
Return sections
End Function
Public Class DocumentSection
Public Property Text As String
Public Property LineCount As Integer
Public Property LocationX As Integer
Public Property LocationY As Integer
Public Property Width As Integer
Public Property Height As Integer
Public Property Confidence As Double
End Class
Absätze, Zeilen und Wörter sind direkt als typisierte Sammlungen zugänglich mit .X, .Y, .Width, .Height und .Confidence-Eigenschaften. Keine BoundingBox-Normalisierung, keine Heuristiken zur Lückenschwelle, keine Blocktypfilterung. Der Read-Results-Leitfaden dokumentiert die vollständige OcrResult-Hierarchie einschließlich des Zugriffs auf die Koordinatenebene. Für Rechnungs- und Beleg-Workflows, die auf dieser Struktur basieren, siehe das Tutorial zur Rechnungs-OCR .
Ersetzen der ratenbegrenzten Stapelverarbeitung durch uneingeschränkte parallele Ausführung
Das Batch-Bildverarbeitung ist der Bereich, in dem Textracts TPS-Limits die sichtbarsten Betriebskosten verursachen. Die DetectDocumentText-synchrone API ist ratengesteuert; Das Senden von mehr als 5 Anfragen pro Sekunde erzeugt ProvisionedThroughputExceededException. Korrekte Stapelverarbeitung erfordert SemaphoreSlim-Drosselung, Rückgabelogik mit Rückgang und vorsichtige Betrachtung der in Bearbeitung befindlichen Anfragen.IronOCR hat keine Ratenbegrenzung – der Durchsatz ist nur durch die verfügbaren CPU-Kerne begrenzt.
AWS Textract-Ansatz:
// Batch processing requires SemaphoreSlim throttle + retry on rate limit exceeded
using Amazon.Textract;
using Amazon.Textract.Model;
using System.Collections.Concurrent;
public async Task<Dictionary<string, string>> ProcessImageBatchAsync(
IReadOnlyList<string> imagePaths,
IProgress<(int Completed, int Total)> progress = null)
{
var results = new ConcurrentDictionary<string, string>();
var semaphore = new SemaphoreSlim(5); // 5 concurrent — Textract default TPS limit
var tasks = new List<Task>();
int completed = 0;
foreach (var path in imagePaths)
{
await semaphore.WaitAsync();
tasks.Add(Task.Run(async () =>
{
try
{
string text = null;
int retryCount = 0;
while (text == null && retryCount < 3)
{
try
{
var bytes = await File.ReadAllBytesAsync(path);
var response = await _textractClient.DetectDocumentTextAsync(
new DetectDocumentTextRequest
{
Document = new Document { Bytes = new MemoryStream(bytes) }
});
text = string.Join("\n", response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
}
catch (AmazonTextractException ex)
when (ex.ErrorCode == "ProvisionedThroughputExceededException")
{
// Exponential backoff on rate limit — blocks the entire thread
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, retryCount)));
retryCount++;
}
}
results[path] = text ?? string.Empty;
var done = Interlocked.Increment(ref completed);
progress?.Report((done, imagePaths.Count));
}
finally
{
semaphore.Release();
}
}));
}
await Task.WhenAll(tasks);
return results.ToDictionary(x => x.Key, x => x.Value);
}
// Batch processing requires SemaphoreSlim throttle + retry on rate limit exceeded
using Amazon.Textract;
using Amazon.Textract.Model;
using System.Collections.Concurrent;
public async Task<Dictionary<string, string>> ProcessImageBatchAsync(
IReadOnlyList<string> imagePaths,
IProgress<(int Completed, int Total)> progress = null)
{
var results = new ConcurrentDictionary<string, string>();
var semaphore = new SemaphoreSlim(5); // 5 concurrent — Textract default TPS limit
var tasks = new List<Task>();
int completed = 0;
foreach (var path in imagePaths)
{
await semaphore.WaitAsync();
tasks.Add(Task.Run(async () =>
{
try
{
string text = null;
int retryCount = 0;
while (text == null && retryCount < 3)
{
try
{
var bytes = await File.ReadAllBytesAsync(path);
var response = await _textractClient.DetectDocumentTextAsync(
new DetectDocumentTextRequest
{
Document = new Document { Bytes = new MemoryStream(bytes) }
});
text = string.Join("\n", response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
}
catch (AmazonTextractException ex)
when (ex.ErrorCode == "ProvisionedThroughputExceededException")
{
// Exponential backoff on rate limit — blocks the entire thread
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, retryCount)));
retryCount++;
}
}
results[path] = text ?? string.Empty;
var done = Interlocked.Increment(ref completed);
progress?.Report((done, imagePaths.Count));
}
finally
{
semaphore.Release();
}
}));
}
await Task.WhenAll(tasks);
return results.ToDictionary(x => x.Key, x => x.Value);
}
Imports Amazon.Textract
Imports Amazon.Textract.Model
Imports System.Collections.Concurrent
Imports System.IO
Imports System.Threading
Public Async Function ProcessImageBatchAsync(
imagePaths As IReadOnlyList(Of String),
Optional progress As IProgress(Of (Completed As Integer, Total As Integer)) = Nothing) As Task(Of Dictionary(Of String, String))
Dim results As New ConcurrentDictionary(Of String, String)()
Dim semaphore As New SemaphoreSlim(5) ' 5 concurrent — Textract default TPS limit
Dim tasks As New List(Of Task)()
Dim completed As Integer = 0
For Each path In imagePaths
Await semaphore.WaitAsync()
tasks.Add(Task.Run(Async Function()
Try
Dim text As String = Nothing
Dim retryCount As Integer = 0
While text Is Nothing AndAlso retryCount < 3
Try
Dim bytes = Await File.ReadAllBytesAsync(path)
Dim response = Await _textractClient.DetectDocumentTextAsync(
New DetectDocumentTextRequest With {
.Document = New Document With {.Bytes = New MemoryStream(bytes)}
})
text = String.Join(vbLf, response.Blocks _
.Where(Function(b) b.BlockType = BlockType.LINE) _
.Select(Function(b) b.Text))
Catch ex As AmazonTextractException When ex.ErrorCode = "ProvisionedThroughputExceededException"
' Exponential backoff on rate limit — blocks the entire thread
Await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, retryCount)))
retryCount += 1
End Try
End While
results(path) = If(text, String.Empty)
Dim done = Interlocked.Increment(completed)
If progress IsNot Nothing Then
progress.Report((done, imagePaths.Count))
End If
Finally
semaphore.Release()
End Try
End Function))
Next
Await Task.WhenAll(tasks)
Return results.ToDictionary(Function(x) x.Key, Function(x) x.Value)
End Function
IronOCR Ansatz:
// No rate limits, no semaphore, no retry logic — Parallel.ForEach saturates all cores
using IronOcr;
using System.Collections.Concurrent;
public Dictionary<string, string> ProcessImageBatch(
IReadOnlyList<string> imagePaths,
IProgress<(int Completed, int Total)> progress = null)
{
var ocr = new IronTesseract();
var results = new ConcurrentDictionary<string, string>();
int completed = 0;
Parallel.ForEach(imagePaths, path =>
{
var result = ocr.Read(path);
results[path] = result.Text;
var done = Interlocked.Increment(ref completed);
progress?.Report((done, imagePaths.Count));
});
return results.ToDictionary(x => x.Key, x => x.Value);
}
// No rate limits, no semaphore, no retry logic — Parallel.ForEach saturates all cores
using IronOcr;
using System.Collections.Concurrent;
public Dictionary<string, string> ProcessImageBatch(
IReadOnlyList<string> imagePaths,
IProgress<(int Completed, int Total)> progress = null)
{
var ocr = new IronTesseract();
var results = new ConcurrentDictionary<string, string>();
int completed = 0;
Parallel.ForEach(imagePaths, path =>
{
var result = ocr.Read(path);
results[path] = result.Text;
var done = Interlocked.Increment(ref completed);
progress?.Report((done, imagePaths.Count));
});
return results.ToDictionary(x => x.Key, x => x.Value);
}
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Threading
Public Function ProcessImageBatch(
imagePaths As IReadOnlyList(Of String),
Optional progress As IProgress(Of (Completed As Integer, Total As Integer)) = Nothing) As Dictionary(Of String, String)
Dim ocr As New IronTesseract()
Dim results As New ConcurrentDictionary(Of String, String)()
Dim completed As Integer = 0
Parallel.ForEach(imagePaths, Sub(path)
Dim result = ocr.Read(path)
results(path) = result.Text
Dim done = Interlocked.Increment(completed)
If progress IsNot Nothing Then
progress.Report((done, imagePaths.Count))
End If
End Sub)
Return results.ToDictionary(Function(x) x.Key, Function(x) x.Value)
End Function
Das SemaphoreSlim, die Wiederholungsschleife, das exponentielle Zurückgehen und die ProvisionedThroughputExceededException-Catch-Blöcke werden alle entfernt. IronTesseract ist threadsicher und eine einzelne Instanz, die über Threads geteilt wird, verarbeitet die volle parallele Last ohne Sperren. Auf einer Maschine mit 8 Kernen verarbeitet dies 8 Dokumente gleichzeitig ohne Konfiguration; auf 32 Kernen, 32 gleichzeitig. Das Beispiel zur Multithreading-Architektur zeigt das vollständige Muster, und der Leitfaden zur Geschwindigkeitsoptimierung behandelt die Konfigurationsoptimierung der Engine für durchsatzorientierte Bereitstellungen.
Ersetzen der AnalyzeDocument-Formularextraktion durch regionenbasierte CropRectangle-OCR
Textracts AnalyzeDocument mit FORMS liefert KEY_VALUE_SET-Blöcke, die durch RelationshipType.VALUE-Beziehungen verbunden sind. Die Rekonstruktion von Formularfeld-Schlüsselwertpaaren erfordert das Filtern nach EntityTypes.Contains("KEY"), das Folgen von VALUE-Beziehungs-IDs und dann die Abrufung von untergeordneten WORD-Blöcken zur Zusammensetzung des Textes. Für fest formatierte Formulare, bei denen die Feldpositionen bekannt sind, extrahiert IronOCRs CropRectangle jedes Feld direkt ohne Durchlauf durch das Beziehungsdiagramm - und kostet null pro Seite.
AWS Textract-Ansatz:
// FORMS mode costs $0.05/page — the most expensive Textract tier
// Relationship graph traversal required to reconstruct key-value pairs
using Amazon.Textract;
using Amazon.Textract.Model;
public async Task<Dictionary<string, string>> ExtractInvoiceFieldsAsync(string imagePath)
{
var bytes = File.ReadAllBytes(imagePath);
// $0.05/page for FORMS analysis
var response = await _textractClient.AnalyzeDocumentAsync(
new AnalyzeDocumentRequest
{
Document = new Document { Bytes = new MemoryStream(bytes) },
FeatureTypes = new List<string> { "FORMS" }
});
var allBlocks = response.Blocks;
var fields = new Dictionary<string, string>();
// Find KEY blocks only
var keyBlocks = allBlocks.Where(b =>
b.BlockType == BlockType.KEY_VALUE_SET &&
b.EntityTypes?.Contains("KEY") == true);
foreach (var keyBlock in keyBlocks)
{
// Assemble key text from CHILD WORD blocks
var keyText = GetTextFromBlock(allBlocks, keyBlock);
// Find associated VALUE block via VALUE relationship
var valueBlockId = keyBlock.Relationships?
.FirstOrDefault(r => r.Type == RelationshipType.VALUE)?
.Ids.FirstOrDefault();
if (valueBlockId == null) continue;
var valueBlock = allBlocks.FirstOrDefault(b => b.Id == valueBlockId);
if (valueBlock == null) continue;
var valueText = GetTextFromBlock(allBlocks, valueBlock);
fields[keyText.TrimEnd(':')] = valueText;
}
return fields;
}
private string GetTextFromBlock(IList<Block> allBlocks, Block block)
{
if (block.Relationships == null) return string.Empty;
var childWordIds = block.Relationships
.Where(r => r.Type == RelationshipType.CHILD)
.SelectMany(r => r.Ids);
return string.Join(" ", allBlocks
.Where(b => b.BlockType == BlockType.WORD && childWordIds.Contains(b.Id))
.Select(b => b.Text));
}
// FORMS mode costs $0.05/page — the most expensive Textract tier
// Relationship graph traversal required to reconstruct key-value pairs
using Amazon.Textract;
using Amazon.Textract.Model;
public async Task<Dictionary<string, string>> ExtractInvoiceFieldsAsync(string imagePath)
{
var bytes = File.ReadAllBytes(imagePath);
// $0.05/page for FORMS analysis
var response = await _textractClient.AnalyzeDocumentAsync(
new AnalyzeDocumentRequest
{
Document = new Document { Bytes = new MemoryStream(bytes) },
FeatureTypes = new List<string> { "FORMS" }
});
var allBlocks = response.Blocks;
var fields = new Dictionary<string, string>();
// Find KEY blocks only
var keyBlocks = allBlocks.Where(b =>
b.BlockType == BlockType.KEY_VALUE_SET &&
b.EntityTypes?.Contains("KEY") == true);
foreach (var keyBlock in keyBlocks)
{
// Assemble key text from CHILD WORD blocks
var keyText = GetTextFromBlock(allBlocks, keyBlock);
// Find associated VALUE block via VALUE relationship
var valueBlockId = keyBlock.Relationships?
.FirstOrDefault(r => r.Type == RelationshipType.VALUE)?
.Ids.FirstOrDefault();
if (valueBlockId == null) continue;
var valueBlock = allBlocks.FirstOrDefault(b => b.Id == valueBlockId);
if (valueBlock == null) continue;
var valueText = GetTextFromBlock(allBlocks, valueBlock);
fields[keyText.TrimEnd(':')] = valueText;
}
return fields;
}
private string GetTextFromBlock(IList<Block> allBlocks, Block block)
{
if (block.Relationships == null) return string.Empty;
var childWordIds = block.Relationships
.Where(r => r.Type == RelationshipType.CHILD)
.SelectMany(r => r.Ids);
return string.Join(" ", allBlocks
.Where(b => b.BlockType == BlockType.WORD && childWordIds.Contains(b.Id))
.Select(b => b.Text));
}
Imports Amazon.Textract
Imports Amazon.Textract.Model
Imports System.IO
Imports System.Threading.Tasks
Public Class InvoiceFieldExtractor
Private _textractClient As AmazonTextractClient
Public Async Function ExtractInvoiceFieldsAsync(imagePath As String) As Task(Of Dictionary(Of String, String))
Dim bytes = File.ReadAllBytes(imagePath)
' $0.05/page for FORMS analysis
Dim response = Await _textractClient.AnalyzeDocumentAsync(
New AnalyzeDocumentRequest With {
.Document = New Document With {.Bytes = New MemoryStream(bytes)},
.FeatureTypes = New List(Of String) From {"FORMS"}
})
Dim allBlocks = response.Blocks
Dim fields As New Dictionary(Of String, String)()
' Find KEY blocks only
Dim keyBlocks = allBlocks.Where(Function(b)
Return b.BlockType = BlockType.KEY_VALUE_SET AndAlso
b.EntityTypes?.Contains("KEY") = True
End Function)
For Each keyBlock In keyBlocks
' Assemble key text from CHILD WORD blocks
Dim keyText = GetTextFromBlock(allBlocks, keyBlock)
' Find associated VALUE block via VALUE relationship
Dim valueBlockId = keyBlock.Relationships?.
FirstOrDefault(Function(r) r.Type = RelationshipType.VALUE)?.
Ids.FirstOrDefault()
If valueBlockId Is Nothing Then Continue For
Dim valueBlock = allBlocks.FirstOrDefault(Function(b) b.Id = valueBlockId)
If valueBlock Is Nothing Then Continue For
Dim valueText = GetTextFromBlock(allBlocks, valueBlock)
fields(keyText.TrimEnd(":"c)) = valueText
Next
Return fields
End Function
Private Function GetTextFromBlock(allBlocks As IList(Of Block), block As Block) As String
If block.Relationships Is Nothing Then Return String.Empty
Dim childWordIds = block.Relationships.
Where(Function(r) r.Type = RelationshipType.CHILD).
SelectMany(Function(r) r.Ids)
Return String.Join(" ", allBlocks.
Where(Function(b) b.BlockType = BlockType.WORD AndAlso childWordIds.Contains(b.Id)).
Select(Function(b) b.Text))
End Function
End Class
IronOCR Ansatz:
// CropRectangle extracts each field zone directly — no relationship graph
// Works for any fixed-format form where field positions are known
using IronOcr;
// Define the field zones for your form template once
private static readonly Dictionary<string, CropRectangle> InvoiceFieldZones =
new Dictionary<string, CropRectangle>
{
{ "InvoiceNumber", new CropRectangle(450, 80, 300, 35) },
{ "InvoiceDate", new CropRectangle(450, 120, 300, 35) },
{ "VendorName", new CropRectangle(50, 160, 400, 35) },
{ "TotalAmount", new CropRectangle(450, 680, 200, 35) },
{ "DueDate", new CropRectangle(450, 720, 200, 35) }
};
public Dictionary<string, string> ExtractInvoiceFields(string imagePath)
{
var ocr = new IronTesseract();
var fields = new Dictionary<string, string>();
foreach (var zone in InvoiceFieldZones)
{
using var input = new OcrInput();
input.LoadImage(imagePath, zone.Value); // load only the defined region
var result = ocr.Read(input);
fields[zone.Key] = result.Text.Trim();
}
return fields;
}
// CropRectangle extracts each field zone directly — no relationship graph
// Works for any fixed-format form where field positions are known
using IronOcr;
// Define the field zones for your form template once
private static readonly Dictionary<string, CropRectangle> InvoiceFieldZones =
new Dictionary<string, CropRectangle>
{
{ "InvoiceNumber", new CropRectangle(450, 80, 300, 35) },
{ "InvoiceDate", new CropRectangle(450, 120, 300, 35) },
{ "VendorName", new CropRectangle(50, 160, 400, 35) },
{ "TotalAmount", new CropRectangle(450, 680, 200, 35) },
{ "DueDate", new CropRectangle(450, 720, 200, 35) }
};
public Dictionary<string, string> ExtractInvoiceFields(string imagePath)
{
var ocr = new IronTesseract();
var fields = new Dictionary<string, string>();
foreach (var zone in InvoiceFieldZones)
{
using var input = new OcrInput();
input.LoadImage(imagePath, zone.Value); // load only the defined region
var result = ocr.Read(input);
fields[zone.Key] = result.Text.Trim();
}
return fields;
}
Imports IronOcr
' CropRectangle extracts each field zone directly — no relationship graph
' Works for any fixed-format form where field positions are known
' Define the field zones for your form template once
Private Shared ReadOnly InvoiceFieldZones As New Dictionary(Of String, CropRectangle) From {
{"InvoiceNumber", New CropRectangle(450, 80, 300, 35)},
{"InvoiceDate", New CropRectangle(450, 120, 300, 35)},
{"VendorName", New CropRectangle(50, 160, 400, 35)},
{"TotalAmount", New CropRectangle(450, 680, 200, 35)},
{"DueDate", New CropRectangle(450, 720, 200, 35)}
}
Public Function ExtractInvoiceFields(imagePath As String) As Dictionary(Of String, String)
Dim ocr As New IronTesseract()
Dim fields As New Dictionary(Of String, String)()
For Each zone In InvoiceFieldZones
Using input As New OcrInput()
input.LoadImage(imagePath, zone.Value) ' load only the defined region
Dim result = ocr.Read(input)
fields(zone.Key) = result.Text.Trim()
End Using
Next
Return fields
End Function
Die KEY_VALUE_SET-Blockfilterung, die RelationshipType.VALUE-Durchquerung und der GetTextFromBlock-Helfer werden eliminiert. Jedes Feld liest exakt den Pixelbereich, der es enthält – nicht mehr und nicht weniger. Der Leitfaden zur regionsbasierten OCR behandelt CropRectangle-Koordinaten und wie man Zonen aus Vorlagenmessungen definiert. Für rechnungsspezifische Arbeitsabläufe demonstrieren der Blogbeitrag zur OCR-Texterkennung von Rechnungen und das Tutorial zum Scannen von Belegen vollständige Pipelines zur Feldextraktion.
AWS TextractAPI zu IronOCR Mapping-Referenz
| AWS Textract | IronOCR-Äquivalent |
|---|---|
AmazonTextractClient |
IronTesseract |
AmazonS3Client |
Nicht erforderlich |
DetectDocumentTextRequest |
OcrInput mit input.LoadImage() |
DetectDocumentTextResponse |
OcrResult |
AnalyzeDocumentRequest (TABELLEN/FORMULARE) |
OcrInput mit optionalem CropRectangle |
StartDocumentTextDetectionRequest |
OcrInput mit input.LoadPdf() — synchron, kein Jobstart |
GetDocumentTextDetectionRequest |
Nicht zutreffend – Ergebnisse liegen sofort vor. |
Document.Bytes |
input.LoadImage(byteArray) oder input.LoadImage(stream) |
S3Object (Dokumentenstaging) |
Dateipfad oder Stream – kein Staging erforderlich |
Block (BlockType.LINE) |
result.Lines (Typisierte Sammlung) |
Block (BlockType.WORD) |
result.Words (Typisierte Sammlung) |
Block (BlockType.TABLE) |
result.Words gruppiert nach Koordinatennähe |
Block (BlockType.KEY_VALUE_SET) |
CropRectangle Regionsextraktion pro Feld |
Block.Confidence |
word.Confidence / result.Confidence |
Block.Geometry.BoundingBox |
word.X, word.Y, word.Width, word.Height |
RelationshipType.CHILD-Durchquerung |
Direkter Zugriff auf Eigenschaften von typisierten Ergebnisobjekten |
JobStatus.IN_PROGRESS |
Nicht anwendbar – kein asynchroner Zustand |
JobStatus.SUCCEEDED |
Nicht zutreffend – synchrone Rückgabe |
response.NextToken (Paginierung) |
Nicht zutreffend – Ergebnisse nicht paginiert |
ProvisionedThroughputExceededException |
Nicht anwendbar – keine Drosselklappenpotentiometer-Grenzwerte |
AccessDeniedException |
Nicht zutreffend – keine Anmeldeinformationskette |
input.LoadImageFrames() |
Direkte Unterstützung für TIFF-Mehrbildformate (kein Textract-Äquivalent) |
result.SaveAsSearchablePdf() |
Durchsuchbare PDF-Ausgabe (kein Textract-Äquivalent) |
Gängige Migrationsprobleme und Lösungen
Problem 1: Falsch konfigurierte Anmeldeinformationen in neuen Bereitstellungsumgebungen
AWS Textract: Der AmazonTextractClient-Konstruktor löst Anmeldeinformationen aus einer Kette: Umgebungsvariablen, ~/.aws/credentials, EC2-Instanzprofil, ECS-Aufgabenrolle. In einem neuen Docker-Container oder in einer CI-Umgebung, wenn keiner dieser konfiguriert ist, wird der Client erstellt, ohne Fehler zu verursachen, aber jeder API-Aufruf wirft AmazonClientException: No credentials specified. Der Fehler ist erst zur Laufzeit sichtbar.
Lösung: Die Anmeldeinformationskette vollständig entfernen. Legen Sie den IronOCR-Lizenzschlüssel über eine Umgebungsvariable fest, die einfacher zu konfigurieren ist und keine IAM-Berechtigungen zur Verwaltung erfordert:
// Single environment variable — no IAM, no rotation, no chain resolution
var key = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
if (string.IsNullOrEmpty(key))
throw new InvalidOperationException("IRONOCR_LICENSE environment variable is not set");
IronOcr.License.LicenseKey = key;
// Single environment variable — no IAM, no rotation, no chain resolution
var key = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
if (string.IsNullOrEmpty(key))
throw new InvalidOperationException("IRONOCR_LICENSE environment variable is not set");
IronOcr.License.LicenseKey = key;
Imports System
' Single environment variable — no IAM, no rotation, no chain resolution
Dim key As String = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
If String.IsNullOrEmpty(key) Then
Throw New InvalidOperationException("IRONOCR_LICENSE environment variable is not set")
End If
IronOcr.License.LicenseKey = key
Problem 2: Asynchrone Aufrufstellen im gesamten Quellcode
AWS Textract: Da das gesamte SDK nur asynchron ist (DetectDocumentTextAsync, StartDocumentTextDetectionAsync, GetDocumentTextDetectionAsync), verbreiten alle Textract-Aufrufstellen async Task<t> durch den Aufrufstapel. Die Migration zur synchronen API von IronOCR erfordert eine Entscheidung an jedem Standort.
Lösung: Bei Hintergrundverarbeitungsdiensten und Controller-Aktionen sind die synchronen Aufrufe von IronOCR auf Hintergrundthreads sicher. Umschließen Sie in Task.Run nur, wenn die bestehende Aufrufkette asynchron bleiben muss:
// If the call site must remain async (e.g., an ASP.NET controller action):
public async Task<IActionResult> ProcessUpload(IFormFile file)
{
using var ms = new MemoryStream();
await file.CopyToAsync(ms);
// Offload synchronous OCR to thread pool — keeps controller non-blocking
var text = await Task.Run(() =>
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage(ms.ToArray());
return ocr.Read(input).Text;
});
return Ok(text);
}
// If the call site must remain async (e.g., an ASP.NET controller action):
public async Task<IActionResult> ProcessUpload(IFormFile file)
{
using var ms = new MemoryStream();
await file.CopyToAsync(ms);
// Offload synchronous OCR to thread pool — keeps controller non-blocking
var text = await Task.Run(() =>
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage(ms.ToArray());
return ocr.Read(input).Text;
});
return Ok(text);
}
Imports System.IO
Imports System.Threading.Tasks
Imports Microsoft.AspNetCore.Mvc
Public Class YourController
Inherits Controller
' If the call site must remain async (e.g., an ASP.NET controller action):
Public Async Function ProcessUpload(file As IFormFile) As Task(Of IActionResult)
Using ms As New MemoryStream()
Await file.CopyToAsync(ms)
' Offload synchronous OCR to thread pool — keeps controller non-blocking
Dim text = Await Task.Run(Function()
Dim ocr = New IronTesseract()
Using input As New OcrInput()
input.LoadImage(ms.ToArray())
Return ocr.Read(input).Text
End Using
End Function)
Return Ok(text)
End Using
End Function
End Class
Für Batch-Prozessoren und Hintergrunddienste, die bereits auf Nicht-UI-Threads laufen, synchron auf ocr.Read() aufrufen — Task.Run fügt in diesen Kontexten Überkopf ohne Vorteil hinzu. Der Leitfaden zur asynchronen OCR beschreibt die empfohlenen Muster.
Problem 3: Logik zur Bereinigung des S3-Buckets im gesamten Code verstreut
AWS Textract: Jeder Code, der zur Verarbeitung durch Textract in S3 hochgeladen wird, muss nach Abschluss des Auftrags auch wieder aus S3 gelöscht werden. Diese Bereinigung lebt häufig in finally-Blöcken und wird manchmal auf Fehlerpfaden ausgelassen, was verwaiste Objekte verursacht, die Speicherkosten ansammeln. Bei der Migration wird der S3-Bereinigungscode leicht übersehen, da er konzeptionell nicht mit OCR zusammenhängt.
Lösung: Für das gesamte S3-Upload- und Bereinigungsmuster gibt es kein IronOCR Äquivalent. Löschen Sie alle PutObjectAsync, DeleteObjectAsync und S3-bezogenen try/finally-Blöcke vollständig.IronOCR liest direkt von lokalen Pfaden oder Datenströmen:
// Before: upload → job → poll → extract → cleanup (50+ lines)
// After: two lines, no cleanup required
using var input = new OcrInput();
input.LoadPdf(localPdfPath);
var text = new IronTesseract().Read(input).Text;
// Before: upload → job → poll → extract → cleanup (50+ lines)
// After: two lines, no cleanup required
using var input = new OcrInput();
input.LoadPdf(localPdfPath);
var text = new IronTesseract().Read(input).Text;
Imports IronOcr
Using input As New OcrInput()
input.LoadPdf(localPdfPath)
Dim text As String = New IronTesseract().Read(input).Text
End Using
Nach der Migration den S3-Staging-Bucket außer Betrieb nehmen. Der PDF-Eingabeleitfaden und der Stream-Eingabeleitfaden decken jedes Eingabemuster ab, das den gestaffelten Dokumentenzugriff über S3 ersetzt.
Problem 4: Blockgraph-Traversierungscode hat kein direktes Äquivalent
AWS Textract: Der Code, der Block.Relationships durchquert, um Tabellenzellen, Formularfelder oder Absatzgruppen aus RelationshipType.CHILD-ID-Arrays zu rekonstruieren, ist der zeitaufwendigste Code zur Migration. Es gibt keinen direkten Ersatz, da IronOCR typisierte Sammlungen anstelle eines Beziehungsgraphen zurückgibt.
Lösung: Ersetzen Sie jedes Traversierungsmuster durch die entsprechende typisierte Eigenschaft. Beziehungs-ID-Abfragen werden zu einem direkten Zugriff auf Eigenschaften:
// Before: filter by BlockType + traverse relationship IDs
var paragraphText = string.Join(" ", allBlocks
.Where(b => b.BlockType == BlockType.WORD &&
childIds.Contains(b.Id))
.Select(b => b.Text));
// After: direct paragraph text (no filtering, no relationship lookup)
foreach (var page in result.Pages)
foreach (var para in page.Paragraphs)
Console.WriteLine(para.Text); // already assembled
// Before: filter by BlockType + traverse relationship IDs
var paragraphText = string.Join(" ", allBlocks
.Where(b => b.BlockType == BlockType.WORD &&
childIds.Contains(b.Id))
.Select(b => b.Text));
// After: direct paragraph text (no filtering, no relationship lookup)
foreach (var page in result.Pages)
foreach (var para in page.Paragraphs)
Console.WriteLine(para.Text); // already assembled
' Before: filter by BlockType + traverse relationship IDs
Dim paragraphText As String = String.Join(" ", allBlocks _
.Where(Function(b) b.BlockType = BlockType.WORD AndAlso _
childIds.Contains(b.Id)) _
.Select(Function(b) b.Text))
' After: direct paragraph text (no filtering, no relationship lookup)
For Each page In result.Pages
For Each para In page.Paragraphs
Console.WriteLine(para.Text) ' already assembled
Next
Next
Für die Tabellrekonstruktion, die von BlockType.CELL und ColumnIndex-Eigenschaften abhing, verwenden Sie die Wortkoordinatengruppierung auf result.Words. Die Tabellenleseanleitung behandelt den koordinatenbasierten Ansatz.
Problem 5: Catch-Blöcke für ProvisionedThroughputExceededException
AWS Textract: Robuster Batch-Verarbeitungscode fängt AmazonTextractException mit ErrorCode == "ProvisionedThroughputExceededException" ein und implementiert eine Rückversuchsstrategie mit Rückgang. Dieser Fehlercode ist ein Textract-spezifisches Konzept, für das es kein Äquivalent in IronOCR gibt.
Lösung: Löschen Sie alle ProvisionedThroughputExceededException-Catch-Blöcke.IronOCR hat keine TPS-Beschränkungen. Ersetzen Sie das gesamte gedrosselte Batch-Muster durch Parallel.ForEach:
// Delete: SemaphoreSlim, retry loops, ProvisionedThroughputExceededException handlers
// Replace with:
Parallel.ForEach(imagePaths, path =>
{
var result = new IronTesseract().Read(path);
results[path] = result.Text;
});
// Delete: SemaphoreSlim, retry loops, ProvisionedThroughputExceededException handlers
// Replace with:
Parallel.ForEach(imagePaths, path =>
{
var result = new IronTesseract().Read(path);
results[path] = result.Text;
});
Imports System.Threading.Tasks
Parallel.ForEach(imagePaths, Sub(path)
Dim result = New IronTesseract().Read(path)
results(path) = result.Text
End Sub)
Problem 6: Regionskonfiguration in Client-Konstruktoren integriert
AWS Textract: new AmazonTextractClient(Amazon.RegionEndpoint.USEast1) codiert eine Region fest. Für Multi-Region-Bereitstellungen sind mehrere Clientinstanzen oder eine dynamische Regionsauswahl erforderlich. Wenn Textract die Unterstützung in neuen Regionen hinzufügt, muss der Code aktualisiert werden.
Lösung:IronOCR kennt kein Regionskonzept. Entfernen Sie alle Amazon.RegionEndpoint-Referenzen. Der IronTesseract Konstruktor benötigt keine Netzwerkkonfiguration - die Verarbeitung erfolgt lokal. Bei Multi-Region-Bereitstellungen auf AWS-Infrastruktur, bei denen jede Region ihre eigene Rechenkapazität betreibt, verarbeitet jede IronOCR Instanz Dokumente lokal innerhalb dieser Rechenkapazitätsgrenze ohne regionsübergreifende Aufrufe.
Checkliste für die AWS Textract-Migration
Vor der Migration
Überprüfen Sie die gesamte Verwendung von Textract und dem S3 SDK im Quellcode:
grep -rn "Amazon.Textract\|AmazonTextractClient\|DetectDocumentText" --include="*.cs" .
grep -rn "StartDocumentTextDetection\|GetDocumentTextDetection\|JobStatus" --include="*.cs" .
grep -rn "AmazonS3Client\|PutObjectRequest\|DeleteObjectAsync" --include="*.cs" .
grep -rn "BlockType\|RelationshipType\|KEY_VALUE_SET" --include="*.cs" .
grep -rn "ProvisionedThroughputExceededException\|AmazonTextractException" --include="*.cs" .
grep -rn "AWSSDK\|Amazon.RegionEndpoint" --include="*.csproj" .
grep -rn "Amazon.Textract\|AmazonTextractClient\|DetectDocumentText" --include="*.cs" .
grep -rn "StartDocumentTextDetection\|GetDocumentTextDetection\|JobStatus" --include="*.cs" .
grep -rn "AmazonS3Client\|PutObjectRequest\|DeleteObjectAsync" --include="*.cs" .
grep -rn "BlockType\|RelationshipType\|KEY_VALUE_SET" --include="*.cs" .
grep -rn "ProvisionedThroughputExceededException\|AmazonTextractException" --include="*.cs" .
grep -rn "AWSSDK\|Amazon.RegionEndpoint" --include="*.csproj" .
Prüfen Sie vor dem Schreiben des Ersatzcodes Folgendes:
- Zählen Sie alle Dateien, die
AmazonTextractCliententhalten – jede ist ein Ziel zum Ersetzen - Listen Sie alle
AnalyzeDocument-Aufrufstellen auf – notieren Sie, obTABLES,FORMSoder beides verwendet wird - Identifizieren Sie alle asynchronen Polling-Schleifen (
do { await Task.Delay } while JobStatus == IN_PROGRESS) - Finden Sie alle
finallyS3-Bereinigungsblöcke – diese werden vollständig gelöscht, nicht ersetzt - Zählen Sie alle
ProvisionedThroughputExceededException-Catch-Blöcke – gelöscht, nicht ersetzt - Notieren Sie alle
BlockType.TABLE,BlockType.CELL,BlockType.KEY_VALUE_SET-Durchquerungslogik – jeder benötigt eine strukturierte Ausgabenersetzung
Code-Migration
- Entfernen Sie
AWSSDK.Textract,AWSSDK.S3undAWSSDK.Coreaus allen.csproj-Dateien - Führen Sie
dotnet add package IronOcrin jedem Projekt aus, das OCR durchführt - Fügen Sie
IronOcr.License.LicenseKey = ...einmal beim Anwendungsstart hinzu (Program.csoder Äquivalent) - Entfernen Sie alle
using Amazon.Textract;,using Amazon.Textract.Model;,using Amazon.S3;,using Amazon.Runtime;-Anweisungen - Fügen Sie
using IronOcr;zu jeder Datei hinzu, die zuvor Textract-Namensräume importierte - Ersetzen Sie alle
AmazonTextractClient-Konstruktoraufrufe mitnew IronTesseract() - Ersetzen Sie alle
AmazonS3Client-Instanzierungen und allePutObjectAsync/DeleteObjectAsync-Aufrufe durch direkte Dateipfad- oder Streamlesungen - Ersetzen Sie alle
DetectDocumentTextAsync-Aufrufe:var text = ocr.Read(path).Text - Ersetzen Sie alle
StartDocumentTextDetectionAsync+ Polling-Schleifen mitinput.LoadPdf(path)+ocr.Read(input) - Ersetzen Sie alle mehrrahmigen TIFF-Pipelines (TIFF → PDF → S3 → async) mit
input.LoadImageFrames(tiffPath) - Ersetzen Sie
BlockType.LINE/BlockType.WORD-Filterketten mitresult.Lines/result.Words - Ersetzen Sie die
BlockType.KEY_VALUE_SET-Beziehungsdurchquerung mitCropRectangle-Zonenextraktion - Löschen Sie alle
ProvisionedThroughputExceededException-Catch-Blöcke undSemaphoreSlim-Drosselungen - Ersetzen Sie gedrosselte Batch-Muster mit
Parallel.ForEachunter Verwendung einer gemeinsam genutztenIronTesseract-Instanz - Entfernen Sie sämtliche Konfigurationen von AWS-Anmeldeinformationen in den Umgebungsvariablen aus den Bereitstellungsmanifesten und CI-Pipelines.
- S3-Staging-Buckets außer Betrieb nehmen, nachdem der gesamte Verarbeitungscode migriert und verifiziert wurde.
Nach der Migration
- Überprüfen Sie, ob die Anwendung sauber startet, wenn nur
IRONOCR_LICENSEgesetzt ist und alle AWS-Umgebungsvariablen entfernt wurden - Führen Sie die OCR-Analyse auf denselben Beispielbildern durch, die zuvor von Textract verarbeitet wurden, und vergleichen Sie den extrahierten Text auf Genauigkeit.
- Mehrseitige PDFs direkt verarbeiten und überprüfen, ob alle Seiten ohne S3 oder asynchrones Polling extrahiert wurden
- Verarbeiten Sie eine TIFF-Datei mit mehreren Einzelbildern und überprüfen Sie, ob die Seitenzahl mit der Ausgabe von Textract für dasselbe Dokument übereinstimmt.
- Testen Sie die Batch-Verarbeitung mit einem Satz von 50+ Bildern und bestätigen Sie, dass kein
ProvisionedThroughputExceededException-Äquivalent auftritt - Führen Sie die Anwendung in einem Docker-Container ohne ausgehenden Internetzugang aus und überprüfen Sie, ob die OCR-Operation erfolgreich abgeschlossen wurde.
- Überprüfen Sie, ob die durchsuchbare PDF-Ausgabe in Adobe Reader korrekt geöffnet wird und die Volltextsuche möglich ist.
- Bestätigen Sie, dass das Entfernen von
AWS_ACCESS_KEY_IDundAWS_SECRET_ACCESS_KEYaus der Bereitstellungsumgebung nichts beschädigt - Testen Sie beliebige Workflows zur Formular- oder Rechnungsextraktion anhand bekannter Ausgaben von Textract, um die Feldgenauigkeit zu validieren.
- Messung der Verarbeitungslatenz für einen repräsentativen Dokumentensatz und Bestätigung der Beseitigung der minimalen Abfragewartezeit von 5 Sekunden.
Wichtigste Vorteile der Migration zu IronOCR
Infrastrukturkonsolidierung zu einem einzigen NuGet Paket. Drei AWS SDK-Pakete, ein S3-Bucket mit Lebenszyklusrichtlinien, IAM-Rollen in jeder Bereitstellungsumgebung und AWS-Anmeldeinformationsrotationsverfahren werden durch ein einziges NuGet Paket ersetzt. Die .csproj-Änderung ist dotnet remove package AWSSDK.Textract gefolgt von dotnet add package IronOcr. Alle Folgeerscheinungen der AWS-Anmeldeinformationsverwaltung – Sicherheitsaudits, Rotationspläne, Umgebungsvariablenhygiene, Docker-Secrets-Konfiguration – verschwinden damit.
Vorhersehbare Gesamtbetriebskosten. Das Abrechnungsmodell pro Seite erzeugt variable Kosten, die mit dem Dokumentenvolumen unbegrenzt skalieren. Nach der Migration zu IronOCR betragen die Kosten pro zusätzlich verarbeiteter Seite unabhängig vom Volumen null. Ein Team, das 200.000 Seiten pro Monat zum Textract AnalyzeDocument-Satz für Tabellen verarbeitet, gibt 36.000 $ pro Jahr allein für OCR aus. Mit der IronOCR Enterprise Lizenz für 2.999 US-Dollar amortisieren sich diese Kosten in weniger als 31 Tagen durch vermiedene Rechnungsstellung. Auf der IronOCR -Lizenzseite finden Sie alle Details zu den Lizenzstufen und den SaaS-Weitervertriebsbedingungen.
Synchrone Verarbeitung eliminiert die fünfphasige asynchrone Komplexität. Der S3-Upload, der Jobstart, die Abfrageschleife, die paginierte Ergebniserfassung und der abschließende Bereinigungsblock stellen fünf unabhängige Fehlermodi in der Textract-PDF-Pipeline dar. Nach der Migration wird eine PDF-Datei in zwei Zeilen eingelesen. Die Fehlerbehandlung reduziert sich auf ein einzelnes try/catch um den ocr.Read()-Aufruf. Die Polling-Timeout-Logik, die do/while JobStatus == IN_PROGRESS Schleife, der nextToken Paginierungsakkumulator – keines dieser Konzepte existiert im IronOCR-Codebas. Der Wartungsaufwand sinkt proportional zum entfernten Code.
Vollständige Flexibilität bei der Bereitstellung. Textract benötigt bei jedem OCR-Aufruf einen Internetzugang.IronOCR benötigt nur zum Herunterladen des NuGet Pakets während der Installation einen Internetzugang. Danach funktioniert es in abgeschotteten Netzwerken, Docker-Containern ohne ausgehende Regeln, auf lokalen Servern und AWS EC2-Instanzen ohne Textract-Zugriff. Dieselbe Binärdatei, die im Produktivbetrieb auf Windows Server läuft, läuft auch in einem Linux-Container. Der Docker-Bereitstellungsleitfaden und der Linux-Bereitstellungsleitfaden beschreiben die spezifische Konfiguration für containerisierte Umgebungen, die bisher von der Nutzung von Textract ausgeschlossen waren.
Datensouveränität wird durch die Architektur, nicht durch die Konfiguration erreicht.IronOCR verfügt über keinen deaktivierbaren Netzwerkübertragungsmodus – die lokale Verarbeitung ist der einzige Modus. Mit IronOCR verarbeitete Dokumente verlassen niemals den Host-Rechner. Für Anwendungen im Gesundheitswesen, die PHI (geschützte Gesundheitsdaten) verarbeiten, für Anwendungen im Verteidigungsbereich, die CUI (kontrollierte unleserliche Informationen) verarbeiten, und für Finanzanwendungen, die Zahlungskartenbilder verarbeiten, bietet dies eine Compliance-Lösung, die keine BAA-Verhandlung (Business Associate Agreement), keine Konfiguration des regionalen Datenspeicherorts und keine Prüfung der Datenschutzrichtlinien von Amazon erfordert. Der Geltungsbereich der Compliance entspricht den Grenzen Ihrer eigenen Infrastruktur, die Sie bereits kontrollieren. Auf der IronOCR -Produktseite finden Teams, die eine Compliance-Prüfung durchführen, eine vollständige Funktionsübersicht und die Bereitstellungsdokumentation.
Konfigurierbare Vorverarbeitung für die Dokumentenqualitätskontrolle. Die interne Vorverarbeitung von Textract ist weder zugänglich noch konfigurierbar. Wenn ein gescanntes Dokument ein schlechtes Ergebnis liefert, bleibt nur die Möglichkeit, das Ergebnis zu akzeptieren oder erneut zu scannen.IronOCR deckt die gesamte Vorverarbeitungspipeline ab: Deskew(), DeNoise(), Contrast(), Binarize(), Sharpen(), Scale(), Dilate(), Erode() und DeepCleanBackgroundNoise(). Teams, die Dokumente aufgrund von unzuverlässigen Ergebnissen bei schwierigen Scans aus Textract verschoben haben, können die Ursache direkt mit Filtern beheben, die auf den jeweiligen Defekt abgestimmt sind – Rotation, Rauschen, geringer Kontrast oder unzureichende Auflösung. Die Seite zu den Vorverarbeitungsfunktionen und der Leitfaden zur Bildqualitätskorrektur dokumentieren die verfügbaren Filter und ihre jeweiligen Anwendungsfälle.
Häufig gestellte Fragen
Warum sollte ich von Amazon Textract 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 Amazon Textract zu IronOCR?
Ersetzen Sie die Initialisierungssequenzen von Amazon Textract 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 Amazon Textract 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 Amazon Textract 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 Amazon Textract zu IronOCR Änderungen an der Bereitstellungsinfrastruktur?
IronOCR erfordert weniger Änderungen an der Infrastruktur als Amazon Textract. 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 Amazon Textract?
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 für die Skalierung von Arbeitslasten berechenbarer als die von Amazon Textract?
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 Amazon Textract 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.

