Wie man Multi FramePage GIFs und TIFFs in C# liest
Diese Anleitung führt .NET-Entwickler durch eine vollständige Migration von PaddleSharp OCR(Sdcb.PaddleOCR) zu IronOCR. Es umfasst den Ersatz des Inferenzsitzungsmanagements, die Beseitigung der OpenCV-Vorverarbeitungsabhängigkeit, die Entfernung der Backend-Auswahllogik für CPU, GPU und OpenVINO sowie die Migration von Tabellenerkennungs-Workflows. Jeder Abschnitt enthält Vorher-Nachher-Codebeispiele, die aus PaddleSharp-spezifischen Mustern stammen, welche bei generischen OCR-Vergleichen nicht vorkommen.
Warum von PaddleSharp OCRmigrieren?
PaddleSharp stellt eine Deep-Learning-Inferenzpipeline auf Anwendungsebene bereit. Diese Architektur ermöglicht Ihnen den Zugriff auf die Leistungsfähigkeit des PaddlePaddle-Modells, erfordert aber, dass Ihre Anwendung die sonst üblichen Infrastrukturaufgaben selbst übernimmt. Die folgenden Probleme veranlassen die meisten .NET Teams dazu, nach Alternativen zu suchen.
Konfiguration des Inferenz-Backend ist Anwendungscode. Die Wahl zwischen CPU, GPU und OpenVINO Backends in PaddleSharp erfordert das Erstellen und Konfigurieren von PaddleConfig-Objekten, die Auswahl des richtigen nativen Runtime-NuGet-Pakets für das Bereitstellungsziel und bedingtes Verzweigen Ihres Initialisierungscodes basierend auf der zur Laufzeit verfügbaren Hardware. Diese Logik befindet sich in Ihrer Anwendung, nicht in der Bibliothek, und sie bricht zusammen, wenn sich die Zielumgebung ändert.
OpenCV ist eine notwendige Abhängigkeit für die Bildeingabe. PaddleSharp kann weder einen Dateipfad noch einen Datenstrom direkt verarbeiten. Jedes Bild durchläuft OpenCV's Cv2.ImRead(), bevor es die OCR-Engine erreicht. Das erzwingt OpenCvSharp4 und ein plattformspezifisches OpenCvSharp4.runtime.*-Paket in Ihrem Abhängigkeitsgraphen. Wird die Laufzeitumgebung einer Plattform aktualisiert, ohne die andere zu aktualisieren, führt dies zu Laufzeitfehlern, die in verschiedenen Umgebungen schwer zu reproduzieren sind.
Die Lebensdauer von Inferenz-Sitzungen erfordert ein explizites Design. PaddleOcrAll lädt beim Konstruktion drei Modell-Binärdateien von der Festplatte. Diese Kosten — messbar in Hunderten von Millisekunden — bedeuten, dass das Objekt nicht pro Anfrage instanziiert werden kann. Teams müssen eine Lebenszyklusstrategie entwerfen: Singleton, gepoolt oder gescoped. In ASP.NET Core bedeutet dies typischerweise einen registrierten Service mit sorgfältiger Thread-Sicherheitsanalyse, da PaddleOcrAll den zugrunde liegenden nativen Zustand teilt.
Tabellenerkennung erfordert separate Modell-Downloads. Die Extraktion strukturierter Dokumente in PaddleSharp benötigt neben der standardmäßigen dreistufigen Erkennungs-/Klassifizierungs-/Identifizierungspipeline ein dediziertes Tabellenerkennungsmodell. Dieses Modell muss als vierte Datei heruntergeladen, versioniert und konfiguriert werden. Es gibt keine einheitliche API-Oberfläche – die Tabellenerkennung verwendet einen separaten Codepfad mit eigenem Ergebnistyp.
Es wird keine durchsuchbare PDF-Ausgabe erstellt. PaddleSharp erzeugt Textzeichenfolgen. Es kann keine durchsuchbaren PDF-Dateien erstellen. Teams, die gescannte Dokumente als durchsuchbare PDFs archivieren müssen, müssen eine separate PDF-Bibliothek integrieren, diese zusätzliche Abhängigkeit verwalten und eine Konvertierungsschicht schreiben. Die Lücke im Ausgabeformat ist vollständig: kein hOCR, kein strukturiertes durchsuchbares PDF, keine Textebenenüberlagerung.
Die Upstream-Abhängigkeitskette gehört nicht der .NET Community. PaddleSharp nutzt das PaddlePaddle-Inferenzframework von Baidu. Änderungen des Modellformats zwischen verschiedenen PaddleOCR-Versionen haben in der Vergangenheit die .NET Bindungsschicht beeinträchtigt. Die meisten Problemverfolgungs-, Dokumentations- und Release-Diskussionen finden auf Chinesisch statt. Für ein .NET Team ohne Mandarin-Sprecher, die vorgelagerte Projekte überwachen, kommen Breaking Changes ohne Vorwarnung.
Das grundsätzliche Problem
Die Auswahl und Initialisierung eines Backends in PaddleSharp erfordert Konfigurationscode, der zur Infrastruktur und nicht zur OCR-Logik gehört:
// PaddleSharp: Backend selection sprawls into application startup
// Simplified — see Sdcb.PaddleInference documentation for full API
using Sdcb.PaddleInference;
using Sdcb.PaddleOCR;
// CPU-only deployment
var config = PaddleConfig.FromModelDir("models/det");
config.SetCpuMathLibraryNumThreads(4);
// GPU deployment — different package, different init path
// var config = PaddleConfig.FromModelDir("models/det");
// config.EnableGpu(500, 0); // memoryMB, deviceId
// OpenVINO deployment — third conditional branch
// config.EnableMkldnn();
// Application code now owns the hardware topology decision
// PaddleSharp: Backend selection sprawls into application startup
// Simplified — see Sdcb.PaddleInference documentation for full API
using Sdcb.PaddleInference;
using Sdcb.PaddleOCR;
// CPU-only deployment
var config = PaddleConfig.FromModelDir("models/det");
config.SetCpuMathLibraryNumThreads(4);
// GPU deployment — different package, different init path
// var config = PaddleConfig.FromModelDir("models/det");
// config.EnableGpu(500, 0); // memoryMB, deviceId
// OpenVINO deployment — third conditional branch
// config.EnableMkldnn();
// Application code now owns the hardware topology decision
Imports Sdcb.PaddleInference
Imports Sdcb.PaddleOCR
' PaddleSharp: Backend selection sprawls into application startup
' Simplified — see Sdcb.PaddleInference documentation for full API
' CPU-only deployment
Dim config = PaddleConfig.FromModelDir("models/det")
config.SetCpuMathLibraryNumThreads(4)
' GPU deployment — different package, different init path
' Dim config = PaddleConfig.FromModelDir("models/det")
' config.EnableGpu(500, 0) ' memoryMB, deviceId
' OpenVINO deployment — third conditional branch
' config.EnableMkldnn()
' Application code now owns the hardware topology decision
// IronOCR:Neinbackend selection.Neinconfig objects. Zero hardware decisions.
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("document.jpg");
Console.WriteLine(result.Text);
// Runs on CPU, Linux, Docker, or ARM without a code change
// IronOCR:Neinbackend selection.Neinconfig objects. Zero hardware decisions.
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("document.jpg");
Console.WriteLine(result.Text);
// Runs on CPU, Linux, Docker, or ARM without a code change
Imports IronOcr
' IronOCR:Neinbackend selection.Neinconfig objects. Zero hardware decisions.
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim result = (New IronTesseract()).Read("document.jpg")
Console.WriteLine(result.Text)
' Runs on CPU, Linux, Docker, or ARM without a code change
IronOCR vs. PaddleSharp OCR: Funktionsvergleich
Hier folgt ein direkter Leistungsvergleich hinsichtlich der Dimensionen, die bei der Migration am wichtigsten sind:
| Feature | PaddleSharp OCR | IronOCR |
|---|---|---|
| Erforderliche NuGet Pakete | Mindestens 3–4 | 1 |
| Bildeingabemethode | OpenCV Cv2.ImRead() |
Direkter Pfad, Stream oder Byte-Array |
| PDF-Eingabe (nativ) | Nein | Ja |
| Passwortgeschütztes PDF | Nein | Ja |
| Mehrseitiges TIFF | Über OpenCV | Native |
| Durchsuchbare PDF-Ausgabe | Nein | Ja (result.SaveAsSearchablePdf()) |
| hOCR-Export | Nein | Ja |
| Backend-Auswahl (CPU/GPU/OpenVINO) | Manuell PaddleConfig |
Automatisch |
| Vorverarbeitungspipeline | Manuelle OpenCV-Operationen | Eingebaut (Deskew, DeNoise, Contrast, etc.) |
| Lebenszyklusmanagement von Inferenzsitzungen | Manuell (teure Konstruktion) | Leichtgewicht IronTesseract |
| Tabellenerkennungsmodell | Separater Download- und Codepfad | input.LoadImage() + strukturiertes Ergebnis |
| Unterstützte Sprachen | ~10-20 | 125+ |
| Sprachinstallation | Modelldatei herunterladen | NuGet-Paket |
| Mehrsprachige Simultanübertragung | Beschränkt | Ja (OcrLanguage.French + OcrLanguage.German) |
| Regionsbasierte OCR | Keine eingebauten | CropRectangle |
| Barcode-Lesung während der OCR | Nein | Ja (ocr.Configuration.ReadBarCodes = true) |
| Konfidenzwerte | Pro Region | Pro Wort, pro Zeile, pro Seite |
| Strukturierte Ausgabehierarchie | Liste der flachen Regionen | Seiten → Absätze → Zeilen → Wörter → Zeichen |
| Plattformübergreifende Bereitstellung | Komplexe Plattform-Laufzeitpakete | Einzelnes NuGet, alle Plattformen |
| Docker-Einsatz | Mehrere Schichten, Laufzeitpakete | Einschichtig |
| Kommerzielle Unterstützung | GitHub Probleme (hauptsächlich chinesische) | E-Mail-Support |
| Lizenzmodell | Apache 2.0 | Perpetual ($999 Lite, $1,499 Pro, $2,999 Enterprise) |
Schnellstart: Migration von PaddleSharp OCRzu IronOCR
Schritt 1: Ersetzen des NuGet-Pakets
PaddleSharp und seine OpenCV-Abhängigkeit entfernen:
dotnet remove package Sdcb.PaddleOCR
dotnet remove package Sdcb.PaddleInference
dotnet remove package OpenCvSharp4
dotnet remove package OpenCvSharp4.runtime.win
dotnet remove package Sdcb.PaddleOCR
dotnet remove package Sdcb.PaddleInference
dotnet remove package OpenCvSharp4
dotnet remove package OpenCvSharp4.runtime.win
Installieren Sie IronOCR über NuGet :
dotnet add package IronOcr
Schritt 2: Namespaces aktualisieren
Ersetzen Sie die PaddleSharp-Namensräume durch den einzelnen IronOCR Namensraum:
// Before (PaddleSharp)
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using Sdcb.PaddleInference;
using OpenCvSharp;
// After (IronOCR)
using IronOcr;
// Before (PaddleSharp)
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using Sdcb.PaddleInference;
using OpenCvSharp;
// After (IronOCR)
using IronOcr;
Imports IronOcr
Schritt 3: Lizenz initialisieren
Fügen Sie die Lizenzinitialisierung einmal beim Anwendungsstart hinzu — in Program.cs, Startup.cs oder Ihrem zusammengesetzten Root:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Beispiele für die Code-Migration
Lebenszyklusersetzung der Inferenzsitzung
PaddleSharp's PaddleOcrAll ist teuer zu konstruieren, da es beim Instanziieren drei Modell-Binärdateien synchron lädt. In Produktionsanwendungen muss es als langlebiges Objekt behandelt werden, was ein bestimmtes Abhängigkeitsinjektionsmuster erfordert. Die Entsorgungskette erfordert ebenfalls Aufmerksamkeit, da die zugrunde liegenden nativen Ressourcen in der richtigen Reihenfolge freigegeben werden müssen.
PaddleSharp OCR-Ansatz:
// Simplified — see Sdcb.PaddleOCR documentation for full API
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using OpenCvSharp;
using Microsoft.Extensions.DependencyInjection;
// Expensive: loads 3 model files from disk on construction (~300–800ms)
public class PaddleOcrEngine : IDisposable
{
private readonly PaddleOcrAll _ocr;
private bool _disposed;
public PaddleOcrEngine()
{
var detModel = LocalFullModels.ChineseV3.DetectionModel;
var clsModel = LocalFullModels.ChineseV3.ClassifierModel;
var recModel = LocalFullModels.ChineseV3.RecognitionModel;
// Must be singleton — cannot afford per-request construction
_ocr = new PaddleOcrAll(detModel, clsModel, recModel);
}
public string Read(string imagePath)
{
using var mat = Cv2.ImRead(imagePath); // OpenCV required even for a file path
var result = _ocr.Run(mat);
return string.Join(" ", result.Regions.Select(r => r.Text));
}
public void Dispose()
{
if (!_disposed)
{
_ocr?.Dispose();
_disposed = true;
}
}
}
// Startup.cs — forced singleton because of construction cost
services.AddSingleton<PaddleOcrEngine>();
// Simplified — see Sdcb.PaddleOCR documentation for full API
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using OpenCvSharp;
using Microsoft.Extensions.DependencyInjection;
// Expensive: loads 3 model files from disk on construction (~300–800ms)
public class PaddleOcrEngine : IDisposable
{
private readonly PaddleOcrAll _ocr;
private bool _disposed;
public PaddleOcrEngine()
{
var detModel = LocalFullModels.ChineseV3.DetectionModel;
var clsModel = LocalFullModels.ChineseV3.ClassifierModel;
var recModel = LocalFullModels.ChineseV3.RecognitionModel;
// Must be singleton — cannot afford per-request construction
_ocr = new PaddleOcrAll(detModel, clsModel, recModel);
}
public string Read(string imagePath)
{
using var mat = Cv2.ImRead(imagePath); // OpenCV required even for a file path
var result = _ocr.Run(mat);
return string.Join(" ", result.Regions.Select(r => r.Text));
}
public void Dispose()
{
if (!_disposed)
{
_ocr?.Dispose();
_disposed = true;
}
}
}
// Startup.cs — forced singleton because of construction cost
services.AddSingleton<PaddleOcrEngine>();
Imports Sdcb.PaddleOCR
Imports Sdcb.PaddleOCR.Models
Imports OpenCvSharp
Imports Microsoft.Extensions.DependencyInjection
' Expensive: loads 3 model files from disk on construction (~300–800ms)
Public Class PaddleOcrEngine
Implements IDisposable
Private ReadOnly _ocr As PaddleOcrAll
Private _disposed As Boolean
Public Sub New()
Dim detModel = LocalFullModels.ChineseV3.DetectionModel
Dim clsModel = LocalFullModels.ChineseV3.ClassifierModel
Dim recModel = LocalFullModels.ChineseV3.RecognitionModel
' Must be singleton — cannot afford per-request construction
_ocr = New PaddleOcrAll(detModel, clsModel, recModel)
End Sub
Public Function Read(imagePath As String) As String
Using mat = Cv2.ImRead(imagePath) ' OpenCV required even for a file path
Dim result = _ocr.Run(mat)
Return String.Join(" ", result.Regions.Select(Function(r) r.Text))
End Using
End Function
Public Sub Dispose() Implements IDisposable.Dispose
If Not _disposed Then
_ocr?.Dispose()
_disposed = True
End If
End Sub
End Class
' Startup.vb — forced singleton because of construction cost
services.AddSingleton(Of PaddleOcrEngine)()
IronOCR Ansatz:
using IronOcr;
using Microsoft.Extensions.DependencyInjection;
// IronTesseract has lightweight initialization — no model loading on construction
public class OcrEngine
{
public string Read(string imagePath)
{
return new IronTesseract().Read(imagePath).Text;
}
}
// Flexible registration — singleton, scoped, or transient all work
services.AddTransient<OcrEngine>();
// Or skip the wrapper entirely and inject IronTesseract directly
services.AddTransient<IronTesseract>();
using IronOcr;
using Microsoft.Extensions.DependencyInjection;
// IronTesseract has lightweight initialization — no model loading on construction
public class OcrEngine
{
public string Read(string imagePath)
{
return new IronTesseract().Read(imagePath).Text;
}
}
// Flexible registration — singleton, scoped, or transient all work
services.AddTransient<OcrEngine>();
// Or skip the wrapper entirely and inject IronTesseract directly
services.AddTransient<IronTesseract>();
Imports IronOcr
Imports Microsoft.Extensions.DependencyInjection
' IronTesseract has lightweight initialization — no model loading on construction
Public Class OcrEngine
Public Function Read(imagePath As String) As String
Return New IronTesseract().Read(imagePath).Text
End Function
End Class
' Flexible registration — singleton, scoped, or transient all work
services.AddTransient(Of OcrEngine)()
' Or skip the wrapper entirely and inject IronTesseract directly
services.AddTransient(Of IronTesseract)()
Der Shift von erzwungener Singleton-Lebensdauer zu flexibler Lebensdauer ist signifikant. Die Baukosten von PaddleSharp sichern Ihnen die Entscheidung für die Nutzungsdauer; Mit IronOCR können Sie die passende Lösung basierend auf den Anforderungen Ihrer Anwendung hinsichtlich Threading und Anfrageisolation auswählen. Die IronTesseract-Einrichtungsanleitung behandelt Konfigurationsoptionen, die auf Instanzebene gelten.
OpenCV-Vorverarbeitungspipeline-Migration
PaddleSharp-Teams, die mit Scans geringer Qualität arbeiten, erstellen typischerweise eine OpenCV-Vorverarbeitungspipeline, bevor sie die OCR-Engine aufrufen. Diese Pipeline erfordert Kenntnisse der OpenCV-API, die wesentlich umfangreicher ist als der tatsächliche Umfang der OCR-Vorverarbeitung. Gemeinsame Operationen — Entzerrung, Rauschreduktion, Kontraststretch — erfordern mehrere Mat-Operationen und sorgfältiges Speicher-Management mit using-Blöcken, um native Speicherlecks zu verhindern.
PaddleSharp OCR-Ansatz:
// Simplified — see OpenCvSharp documentation for full API
using OpenCvSharp;
using Sdcb.PaddleOCR;
public string ReadWithPreprocessing(string imagePath, PaddleOcrAll ocr)
{
using var original = Cv2.ImRead(imagePath);
// Step 1: Grayscale conversion
using var gray = new Mat();
Cv2.CvtColor(original, gray, ColorConversionCodes.BGR2GRAY);
// Step 2: Denoise (Gaussian blur to reduce noise)
using var denoised = new Mat();
Cv2.GaussianBlur(gray, denoised, new Size(3, 3), 0);
// Step 3: Adaptive threshold for binarization
using var binary = new Mat();
Cv2.AdaptiveThreshold(denoised, binary, 255,
AdaptiveThresholdTypes.GaussianC, ThresholdTypes.Binary, 11, 2);
// Step 4: Deskew — requires custom rotation detection logic (not shown)
// Several dozen lines of custom Mat operations
var result = ocr.Run(binary);
return string.Join(" ", result.Regions.Select(r => r.Text));
// Each Mat must be disposed; missing a using block leaks native memory
}
// Simplified — see OpenCvSharp documentation for full API
using OpenCvSharp;
using Sdcb.PaddleOCR;
public string ReadWithPreprocessing(string imagePath, PaddleOcrAll ocr)
{
using var original = Cv2.ImRead(imagePath);
// Step 1: Grayscale conversion
using var gray = new Mat();
Cv2.CvtColor(original, gray, ColorConversionCodes.BGR2GRAY);
// Step 2: Denoise (Gaussian blur to reduce noise)
using var denoised = new Mat();
Cv2.GaussianBlur(gray, denoised, new Size(3, 3), 0);
// Step 3: Adaptive threshold for binarization
using var binary = new Mat();
Cv2.AdaptiveThreshold(denoised, binary, 255,
AdaptiveThresholdTypes.GaussianC, ThresholdTypes.Binary, 11, 2);
// Step 4: Deskew — requires custom rotation detection logic (not shown)
// Several dozen lines of custom Mat operations
var result = ocr.Run(binary);
return string.Join(" ", result.Regions.Select(r => r.Text));
// Each Mat must be disposed; missing a using block leaks native memory
}
Imports OpenCvSharp
Imports Sdcb.PaddleOCR
Public Function ReadWithPreprocessing(imagePath As String, ocr As PaddleOcrAll) As String
Using original As Mat = Cv2.ImRead(imagePath)
' Step 1: Grayscale conversion
Using gray As New Mat()
Cv2.CvtColor(original, gray, ColorConversionCodes.BGR2GRAY)
' Step 2: Denoise (Gaussian blur to reduce noise)
Using denoised As New Mat()
Cv2.GaussianBlur(gray, denoised, New Size(3, 3), 0)
' Step 3: Adaptive threshold for binarization
Using binary As New Mat()
Cv2.AdaptiveThreshold(denoised, binary, 255, AdaptiveThresholdTypes.GaussianC, ThresholdTypes.Binary, 11, 2)
' Step 4: Deskew — requires custom rotation detection logic (not shown)
' Several dozen lines of custom Mat operations
Dim result = ocr.Run(binary)
Return String.Join(" ", result.Regions.Select(Function(r) r.Text))
End Using
End Using
End Using
End Using
End Function
IronOCR Ansatz:
using IronOcr;
public string ReadWithPreprocessing(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
// Named operations replace OpenCV knowledge requirements
input.Deskew();
input.DeNoise();
input.Contrast();
input.Binarize();
var result = new IronTesseract().Read(input);
return result.Text;
// OcrInput implements IDisposable; using block handles cleanup
}
using IronOcr;
public string ReadWithPreprocessing(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
// Named operations replace OpenCV knowledge requirements
input.Deskew();
input.DeNoise();
input.Contrast();
input.Binarize();
var result = new IronTesseract().Read(input);
return result.Text;
// OcrInput implements IDisposable; using block handles cleanup
}
Imports IronOcr
Public Function ReadWithPreprocessing(imagePath As String) As String
Using input As New OcrInput()
input.LoadImage(imagePath)
' Named operations replace OpenCV knowledge requirements
input.Deskew()
input.DeNoise()
input.Contrast()
input.Binarize()
Dim result = New IronTesseract().Read(input)
Return result.Text
' OcrInput implements IDisposable; using block handles cleanup
End Using
End Function
Keine Mat-Zuweisungen. Keine Kenntnisse über adaptive Schwellenwertparameter. Keine benutzerdefinierten Berechnungen zur Entzerrung und Rotation. Die gleiche Vorverarbeitungspipeline, die 30–50 Zeilen OpenCV-Code erforderte, reduziert sich auf vier Methodenaufrufe. Der Leitfaden zur Bildqualitätskorrektur dokumentiert jeden verfügbaren Filter mit Vorher-Nachher-Beispielen. Für Dokumente mit starkem Hintergrundrauschen geht input.DeepCleanBackgroundNoise() weiter als DeNoise() ohne zusätzliche Parameter.
Für Teams mit nicht standardisierten Anforderungen an die Vorverarbeitung bietet der Filterassistent ein interaktives Werkzeug zur Auswertung von Filterkombinationen für Ihre spezifischen Dokumenttypen, bevor Sie den Code festlegen.
Eliminierung der Backend-Auswahl
PaddleSharp macht das Inferenz-Backend zu einer Angelegenheit auf Anwendungsebene. Eine Bereitstellung, die auf einer reinen CPU-Cloud-VM laufen soll, verwendet einen anderen Initialisierungscode als eine Bereitstellung für eine GPU-Workstation oder ein Intel OpenVINO-fähiges Edge-Gerät. Diese bedingte Logik landet typischerweise im Startcode der Anwendung, in Prüfungen von Umgebungsvariablen oder in Feature-Flags – also in der Infrastrukturarbeit, die nichts mit dem Lesen von Text aus Bildern zu tun hat.
PaddleSharp OCR-Ansatz:
// Simplified — see Sdcb.PaddleInference documentation for full API
using Sdcb.PaddleInference;
using Sdcb.PaddleOCR;
public PaddleOcrAll CreateOcrEngine(string backendMode)
{
// Each backend requires a different NuGet runtime package installed
switch (backendMode)
{
case "gpu":
// Requires: Sdcb.PaddleInference.runtime.win64.cuda
// Requires: CUDA toolkit + cuDNN installed on host
var gpuConfig = PaddleConfig.FromModelDir("models/");
gpuConfig.EnableGpu(500, deviceId: 0); // Simplified
break;
case "openvino":
// Requires: Sdcb.PaddleInference.runtime.win64.mkl
var oviConfig = PaddleConfig.FromModelDir("models/");
oviConfig.EnableMkldnn(); // Simplified
break;
default:
// CPU-only — still requires platform-specific runtime package
var cpuConfig = PaddleConfig.FromModelDir("models/");
cpuConfig.SetCpuMathLibraryNumThreads(Environment.ProcessorCount);
break;
}
// Backend-specific config passed to model constructors — Simplified
var detModel = LocalFullModels.ChineseV3.DetectionModel;
var clsModel = LocalFullModels.ChineseV3.ClassifierModel;
var recModel = LocalFullModels.ChineseV3.RecognitionModel;
return new PaddleOcrAll(detModel, clsModel, recModel);
}
// Simplified — see Sdcb.PaddleInference documentation for full API
using Sdcb.PaddleInference;
using Sdcb.PaddleOCR;
public PaddleOcrAll CreateOcrEngine(string backendMode)
{
// Each backend requires a different NuGet runtime package installed
switch (backendMode)
{
case "gpu":
// Requires: Sdcb.PaddleInference.runtime.win64.cuda
// Requires: CUDA toolkit + cuDNN installed on host
var gpuConfig = PaddleConfig.FromModelDir("models/");
gpuConfig.EnableGpu(500, deviceId: 0); // Simplified
break;
case "openvino":
// Requires: Sdcb.PaddleInference.runtime.win64.mkl
var oviConfig = PaddleConfig.FromModelDir("models/");
oviConfig.EnableMkldnn(); // Simplified
break;
default:
// CPU-only — still requires platform-specific runtime package
var cpuConfig = PaddleConfig.FromModelDir("models/");
cpuConfig.SetCpuMathLibraryNumThreads(Environment.ProcessorCount);
break;
}
// Backend-specific config passed to model constructors — Simplified
var detModel = LocalFullModels.ChineseV3.DetectionModel;
var clsModel = LocalFullModels.ChineseV3.ClassifierModel;
var recModel = LocalFullModels.ChineseV3.RecognitionModel;
return new PaddleOcrAll(detModel, clsModel, recModel);
}
Imports Sdcb.PaddleInference
Imports Sdcb.PaddleOCR
Public Function CreateOcrEngine(ByVal backendMode As String) As PaddleOcrAll
' Each backend requires a different NuGet runtime package installed
Select Case backendMode
Case "gpu"
' Requires: Sdcb.PaddleInference.runtime.win64.cuda
' Requires: CUDA toolkit + cuDNN installed on host
Dim gpuConfig As PaddleConfig = PaddleConfig.FromModelDir("models/")
gpuConfig.EnableGpu(500, deviceId:=0) ' Simplified
Case "openvino"
' Requires: Sdcb.PaddleInference.runtime.win64.mkl
Dim oviConfig As PaddleConfig = PaddleConfig.FromModelDir("models/")
oviConfig.EnableMkldnn() ' Simplified
Case Else
' CPU-only — still requires platform-specific runtime package
Dim cpuConfig As PaddleConfig = PaddleConfig.FromModelDir("models/")
cpuConfig.SetCpuMathLibraryNumThreads(Environment.ProcessorCount)
End Select
' Backend-specific config passed to model constructors — Simplified
Dim detModel = LocalFullModels.ChineseV3.DetectionModel
Dim clsModel = LocalFullModels.ChineseV3.ClassifierModel
Dim recModel = LocalFullModels.ChineseV3.RecognitionModel
Return New PaddleOcrAll(detModel, clsModel, recModel)
End Function
IronOCR Ansatz:
using IronOcr;
//Neinbackend selection.Neinswitch statement.Neinenvironment variable check.
// The same code runs on CPU-only VMs, GPU workstations, and ARM devices.
public IronTesseract CreateOcrEngine()
{
return new IronTesseract();
}
// Parallel processing across CPU cores — no GPU configuration required
public IEnumerable<string> ReadBatch(IEnumerable<string> imagePaths)
{
var results = new System.Collections.Concurrent.ConcurrentBag<string>();
Parallel.ForEach(imagePaths, path =>
{
var result = new IronTesseract().Read(path);
results.Add(result.Text);
});
return results;
}
using IronOcr;
//Neinbackend selection.Neinswitch statement.Neinenvironment variable check.
// The same code runs on CPU-only VMs, GPU workstations, and ARM devices.
public IronTesseract CreateOcrEngine()
{
return new IronTesseract();
}
// Parallel processing across CPU cores — no GPU configuration required
public IEnumerable<string> ReadBatch(IEnumerable<string> imagePaths)
{
var results = new System.Collections.Concurrent.ConcurrentBag<string>();
Parallel.ForEach(imagePaths, path =>
{
var result = new IronTesseract().Read(path);
results.Add(result.Text);
});
return results;
}
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Threading.Tasks
Public Class OcrProcessor
' Neinbackend selection.Neinswitch statement.Neinenvironment variable check.
' The same code runs on CPU-only VMs, GPU workstations, and ARM devices.
Public Function CreateOcrEngine() As IronTesseract
Return New IronTesseract()
End Function
' Parallel processing across CPU cores — no GPU configuration required
Public Function ReadBatch(imagePaths As IEnumerable(Of String)) As IEnumerable(Of String)
Dim results As New ConcurrentBag(Of String)()
Parallel.ForEach(imagePaths, Sub(path)
Dim result = New IronTesseract().Read(path)
results.Add(result.Text)
End Sub)
Return results
End Function
End Class
Das Parallel.ForEach-Muster ist hier von Haus aus thread-sicher. Jede IronTesseract-Instanz ist unabhängig und hat keinen gemeinsamen nativen Zustand. Für Teams, deren PaddleSharp-Bereitstellung Zeit mit der Verwaltung von Backend-Bedingungen verbringt, bedeutet diese Vereinfachung auch eine Verbesserung der Bereitstellungszuverlässigkeit – das gleiche Build-Artefakt wird überall ohne Hardware-Erkennungscode ausgeführt. Der Leitfaden zur Geschwindigkeitsoptimierung behandelt Konfigurationsoptionen für durchsatzkritische Szenarien.
Tabellenerkennungsmigration
Die Tabellenextraktion in PaddleSharp erfordert ein spezielles Tabellenerkennungsmodell – eine vierte Modelldatei zusätzlich zum Standard-Erkennungs-, Klassifizierungs- und Zuordnungsset. Das Tabellenmodell verwendet einen separaten API-Aufruf und gibt eine eigene Ergebnisstruktur zurück. Teams, die Verarbeitungspipelines für Rechnungen, Formulare oder Tabellenkalkulationen erstellen, pflegen zwei parallele Initialisierungspfade und zwei Strategien zur Ergebnisanalyse.
PaddleSharp OCR-Ansatz:
// Simplified — see Sdcb.PaddleOCR documentation for full API
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using OpenCvSharp;
public class TableRecognitionService
{
// Standard OCR engine — 3 models
private readonly PaddleOcrAll _textOcr;
// Table engine — 4th model, separate initialization
// private readonly PaddleOcrTable _tableOcr; // Simplified
public TableRecognitionService()
{
var detModel = LocalFullModels.ChineseV3.DetectionModel;
var clsModel = LocalFullModels.ChineseV3.ClassifierModel;
var recModel = LocalFullModels.ChineseV3.RecognitionModel;
_textOcr = new PaddleOcrAll(detModel, clsModel, recModel);
// Table model: separate download, separate version tracking
// var tableModel = LocalFullModels.TableEnV2.Model; // Simplified
// _tableOcr = new PaddleOcrTable(tableModel); // Simplified
}
public void ProcessDocument(string imagePath)
{
using var image = Cv2.ImRead(imagePath);
// Text extraction path
var textResult = _textOcr.Run(image);
var text = string.Join(" ", textResult.Regions.Select(r => r.Text));
// Table extraction path — different API, different result structure
// var tableResult = _tableOcr.Run(image); // Simplified
// foreach (var cell in tableResult.Cells) { ... } // Simplified
}
}
// Simplified — see Sdcb.PaddleOCR documentation for full API
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using OpenCvSharp;
public class TableRecognitionService
{
// Standard OCR engine — 3 models
private readonly PaddleOcrAll _textOcr;
// Table engine — 4th model, separate initialization
// private readonly PaddleOcrTable _tableOcr; // Simplified
public TableRecognitionService()
{
var detModel = LocalFullModels.ChineseV3.DetectionModel;
var clsModel = LocalFullModels.ChineseV3.ClassifierModel;
var recModel = LocalFullModels.ChineseV3.RecognitionModel;
_textOcr = new PaddleOcrAll(detModel, clsModel, recModel);
// Table model: separate download, separate version tracking
// var tableModel = LocalFullModels.TableEnV2.Model; // Simplified
// _tableOcr = new PaddleOcrTable(tableModel); // Simplified
}
public void ProcessDocument(string imagePath)
{
using var image = Cv2.ImRead(imagePath);
// Text extraction path
var textResult = _textOcr.Run(image);
var text = string.Join(" ", textResult.Regions.Select(r => r.Text));
// Table extraction path — different API, different result structure
// var tableResult = _tableOcr.Run(image); // Simplified
// foreach (var cell in tableResult.Cells) { ... } // Simplified
}
}
Imports Sdcb.PaddleOCR
Imports Sdcb.PaddleOCR.Models
Imports OpenCvSharp
Public Class TableRecognitionService
' Standard OCR engine — 3 models
Private ReadOnly _textOcr As PaddleOcrAll
' Table engine — 4th model, separate initialization
' Private ReadOnly _tableOcr As PaddleOcrTable ' Simplified
Public Sub New()
Dim detModel = LocalFullModels.ChineseV3.DetectionModel
Dim clsModel = LocalFullModels.ChineseV3.ClassifierModel
Dim recModel = LocalFullModels.ChineseV3.RecognitionModel
_textOcr = New PaddleOcrAll(detModel, clsModel, recModel)
' Table model: separate download, separate version tracking
' Dim tableModel = LocalFullModels.TableEnV2.Model ' Simplified
' _tableOcr = New PaddleOcrTable(tableModel) ' Simplified
End Sub
Public Sub ProcessDocument(imagePath As String)
Using image = Cv2.ImRead(imagePath)
' Text extraction path
Dim textResult = _textOcr.Run(image)
Dim text = String.Join(" ", textResult.Regions.Select(Function(r) r.Text))
' Table extraction path — different API, different result structure
' Dim tableResult = _tableOcr.Run(image) ' Simplified
' For Each cell In tableResult.Cells ' Simplified
' ...
' Next
End Using
End Sub
End Class
IronOCR Ansatz:
using IronOcr;
public class TableRecognitionService
{
// One engine handles both text and table regions
public void ProcessDocument(string imagePath)
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
// Structured hierarchy: pages → paragraphs → lines → words
foreach (var page in result.Pages)
{
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($"Block at ({paragraph.X},{paragraph.Y}): {paragraph.Text}");
}
}
Console.WriteLine($"Full document text: {result.Text}");
}
}
using IronOcr;
public class TableRecognitionService
{
// One engine handles both text and table regions
public void ProcessDocument(string imagePath)
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
// Structured hierarchy: pages → paragraphs → lines → words
foreach (var page in result.Pages)
{
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($"Block at ({paragraph.X},{paragraph.Y}): {paragraph.Text}");
}
}
Console.WriteLine($"Full document text: {result.Text}");
}
}
Imports IronOcr
Public Class TableRecognitionService
' One engine handles both text and table regions
Public Sub ProcessDocument(imagePath As String)
Dim ocr As New IronTesseract()
Dim result = ocr.Read(imagePath)
' Structured hierarchy: pages → paragraphs → lines → words
For Each page In result.Pages
For Each paragraph In page.Paragraphs
Console.WriteLine($"Block at ({paragraph.X},{paragraph.Y}): {paragraph.Text}")
Next
Next
Console.WriteLine($"Full document text: {result.Text}")
End Sub
End Class
Für Dokumente, bei denen die Tabellenstruktur selbst als Zeilen und Spalten extrahiert werden muss, bietet IronOCR eine spezielle Tabellenextraktionsfunktion:
using IronOcr;
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("invoice-with-table.jpg");
var result = ocr.Read(input);
// Access structured page layout for table region extraction
foreach (var page in result.Pages)
{
foreach (var line in page.Lines)
{
// Lines within a table region preserve spatial ordering
Console.WriteLine($"Row text: {line.Text} | Y position: {line.Y}");
foreach (var word in line.Words)
{
Console.WriteLine($" Cell: '{word.Text}' at X={word.X}");
}
}
}
using IronOcr;
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("invoice-with-table.jpg");
var result = ocr.Read(input);
// Access structured page layout for table region extraction
foreach (var page in result.Pages)
{
foreach (var line in page.Lines)
{
// Lines within a table region preserve spatial ordering
Console.WriteLine($"Row text: {line.Text} | Y position: {line.Y}");
foreach (var word in line.Words)
{
Console.WriteLine($" Cell: '{word.Text}' at X={word.X}");
}
}
}
Imports IronOcr
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadImage("invoice-with-table.jpg")
Dim result = ocr.Read(input)
' Access structured page layout for table region extraction
For Each page In result.Pages
For Each line In page.Lines
' Lines within a table region preserve spatial ordering
Console.WriteLine($"Row text: {line.Text} | Y position: {line.Y}")
For Each word In line.Words
Console.WriteLine($" Cell: '{word.Text}' at X={word.X}")
Next
Next
Next
End Using
Ein Modell-Download wurde entfernt. Ein Initialisierungspfad wurde eliminiert. Die strukturierte Ergebnishierarchie in IronOCR– mit X/Y-Koordinaten auf Wortebene – liefert die Positionsdaten, die zur Rekonstruktion von Tabellenzeilen und -spalten ohne ein separates Erkennungsmodell benötigt werden. Die Tabellenleseanleitung und die Ergebnisleseanleitung beschreiben die vollständige strukturierte Ausgabe-API.
Durchsuchbare PDF-Ausgabe aus gescannten Dokumenten
PaddleSharp erzeugt Textzeichenketten und sonst nichts. Um ein Dokumentenarchiv zu erstellen, das gescannte PDFs textdurchsuchbar macht, ist die Integration einer separaten PDF-Bibliothek, das Schreiben einer Textüberlagerungsebene und die gleichzeitige Verwaltung beider Bibliotheken erforderlich. Teams, die diese Einschränkung akzeptiert haben, stellen oft fest, dass sie der Auslöser für die Migration ist – der Aufwand für die Integration der beiden Bibliotheken übersteigt den Aufwand für den Wechsel des OCR-Anbieters.
PaddleSharp OCR-Ansatz:
// Simplified — PaddleSharp has no PDF output. Requires a separate PDF library.
// Example of what teams typically build:
// using Sdcb.PaddleOCR;
// using SomePdfLibrary; // Third dependency to produce searchable PDF
public void ArchiveScannedDocument(string imagePath, string outputPdfPath)
{
// Step 1: OCR via PaddleSharp — produces text only
// var text = _ocr.Run(Cv2.ImRead(imagePath));
// Step 2: Build a PDF with text overlay using a separate PDF library
// Requires: text positions mapped to PDF coordinate space
// Requires: image embedded as background
// Requires: invisible text layer positioned over image
// ~50–100 lines of PDF construction code
throw new NotImplementedException("Requires a separate PDF library");
}
// Simplified — PaddleSharp has no PDF output. Requires a separate PDF library.
// Example of what teams typically build:
// using Sdcb.PaddleOCR;
// using SomePdfLibrary; // Third dependency to produce searchable PDF
public void ArchiveScannedDocument(string imagePath, string outputPdfPath)
{
// Step 1: OCR via PaddleSharp — produces text only
// var text = _ocr.Run(Cv2.ImRead(imagePath));
// Step 2: Build a PDF with text overlay using a separate PDF library
// Requires: text positions mapped to PDF coordinate space
// Requires: image embedded as background
// Requires: invisible text layer positioned over image
// ~50–100 lines of PDF construction code
throw new NotImplementedException("Requires a separate PDF library");
}
Public Sub ArchiveScannedDocument(imagePath As String, outputPdfPath As String)
' Step 1: OCR via PaddleSharp — produces text only
' Dim text = _ocr.Run(Cv2.ImRead(imagePath))
' Step 2: Build a PDF with text overlay using a separate PDF library
' Requires: text positions mapped to PDF coordinate space
' Requires: image embedded as background
' Requires: invisible text layer positioned over image
' ~50–100 lines of PDF construction code
Throw New NotImplementedException("Requires a separate PDF library")
End Sub
IronOCR Ansatz:
using IronOcr;
public void ArchiveScannedDocument(string imagePath, string outputPdfPath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
input.Deskew(); // Straighten scan before archiving
input.DeNoise(); // Clean up scan artifacts
var ocr = new IronTesseract();
var result = ocr.Read(input);
// One call: OCR + searchable PDF with text layer + image background
result.SaveAsSearchablePdf(outputPdfPath);
}
// Multi-page document — same pattern
public void ArchiveMultiPageDocument(string[] imageFiles, string outputPdfPath)
{
using var input = new OcrInput();
foreach (var file in imageFiles)
input.LoadImage(file);
var result = new IronTesseract().Read(input);
result.SaveAsSearchablePdf(outputPdfPath);
}
using IronOcr;
public void ArchiveScannedDocument(string imagePath, string outputPdfPath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
input.Deskew(); // Straighten scan before archiving
input.DeNoise(); // Clean up scan artifacts
var ocr = new IronTesseract();
var result = ocr.Read(input);
// One call: OCR + searchable PDF with text layer + image background
result.SaveAsSearchablePdf(outputPdfPath);
}
// Multi-page document — same pattern
public void ArchiveMultiPageDocument(string[] imageFiles, string outputPdfPath)
{
using var input = new OcrInput();
foreach (var file in imageFiles)
input.LoadImage(file);
var result = new IronTesseract().Read(input);
result.SaveAsSearchablePdf(outputPdfPath);
}
Imports IronOcr
Public Sub ArchiveScannedDocument(imagePath As String, outputPdfPath As String)
Using input As New OcrInput()
input.LoadImage(imagePath)
input.Deskew() ' Straighten scan before archiving
input.DeNoise() ' Clean up scan artifacts
Dim ocr As New IronTesseract()
Dim result = ocr.Read(input)
' One call: OCR + searchable PDF with text layer + image background
result.SaveAsSearchablePdf(outputPdfPath)
End Using
End Sub
' Multi-page document — same pattern
Public Sub ArchiveMultiPageDocument(imageFiles As String(), outputPdfPath As String)
Using input As New OcrInput()
For Each file In imageFiles
input.LoadImage(file)
Next
Dim result = New IronTesseract().Read(input)
result.SaveAsSearchablePdf(outputPdfPath)
End Using
End Sub
Keine PDF-Bibliothek. Keine Koordinatenzuordnung. Keine Positionierung der Textebene. Das durchsuchbare PDF-Ausgabeformat in IronOCR bettet eine unsichtbare Textebene über das Originalbild ein und erzeugt so eine Datei, die sowohl visuell dem gescannten Dokument entspricht als auch vollständig textdurchsuchbar ist. Die durchsuchbare PDF-Anleitung behandelt Seitenauswahl, Qualitätsoptionen und Metadatenkontrolle.
PaddleSharp OCRAPI zu IronOCR Mapping-Referenz
| PaddleSharp OCR | IronOCR |
|---|---|
Sdcb.PaddleOCR (Namensraum) |
IronOcr (Namensraum) |
Sdcb.PaddleInference (Namensraum) |
Nicht erforderlich – automatisch konfiguriert |
PaddleOcrAll |
IronTesseract |
new PaddleOcrAll(det, cls, rec) |
new IronTesseract() |
LocalFullModels.ChineseV3.DetectionModel |
Kein Äquivalent – keine Modellauswahl |
LocalFullModels.ChineseV3.ClassifierModel |
Kein Äquivalent – keine Modellauswahl |
LocalFullModels.ChineseV3.RecognitionModel |
Kein Äquivalent – keine Modellauswahl |
PaddleConfig.FromModelDir() |
Kein Äquivalent – kein Konfigurationsobjekt |
config.EnableGpu(memMB, deviceId) |
Kein Äquivalent – das Backend ist automatisch. |
config.EnableMkldnn() |
Kein Äquivalent – das Backend ist automatisch. |
config.SetCpuMathLibraryNumThreads(n) |
Kein Äquivalent – wird intern verwaltet |
Cv2.ImRead(path) (OpenCV Laden) |
input.LoadImage(path) |
ocr.Run(mat) |
ocr.Read(input) oder ocr.Read("file.jpg") |
result.Regions |
result.Pages[0].Words oder result.Pages[0].Lines |
region.Text |
word.Text, line.Text, paragraph.Text |
region.Rect.Center.X/.Y |
word.X, word.Y |
region.Score (Vertrauen) |
word.Confidence, result.Confidence |
| Sprachtausch auf Modellebene | ocr.Language = OcrLanguage.French |
| Tabellenmodell (separater Download) | Eingebaute strukturierte Ergebnishierarchie |
Cv2.CvtColor(..., GRAY) |
input.Binarize() oder input.Contrast() |
Cv2.GaussianBlur(...) |
input.DeNoise() |
| Keine durchsuchbare PDF-Ausgabe | result.SaveAsSearchablePdf("output.pdf") |
Gängige Migrationsprobleme und Lösungen
Problem 1: OpenCV-Abhängigkeit kann nicht entladen werden
PaddleSharp OCR: OpenCvSharp4.runtime.win und ähnliche plattformspezifische Laufzeitpakete installieren nicht verwaltete native DLLs. Diese DLLs können in manchen Hosting-Szenarien – insbesondere beim Recycling von IIS-Anwendungspools – eine ordnungsgemäße Bereinigung verhindern und zu Fehlern beim Laden von Assemblys führen, wenn beim Build-Prozess auf das falsche Plattform-Runtime-Paket verwiesen wird. Um sie zu entfernen, müssen sowohl das NuGet Paket deinstalliert als auch alle zwischengespeicherten nativen Binärdateien im Ausgabeverzeichnis gelöscht werden.
Lösung: Entfernen Sie nach dem Entfernen der OpenCvSharp4- und OpenCvSharp4.runtime.*-Pakete das Build-Ausgabeverzeichnis, bevor Sie es neu aufbauen:
dotnet remove package OpenCvSharp4
dotnet remove package OpenCvSharp4.runtime.win
dotnet clean
dotnet build
dotnet remove package OpenCvSharp4
dotnet remove package OpenCvSharp4.runtime.win
dotnet clean
dotnet build
IronOCR bündelt seine nativen Abhängigkeiten intern und kümmert sich um den nicht verwalteten Lebenszyklus. Es ist keine plattformspezifische Auswahl eines Laufzeitpakets erforderlich. Die IronTesseract-Einrichtungsanleitung dokumentiert die Plattformvoraussetzungen, die IronOCR automatisch erfüllt.
Problem 2: Modelldateien verblieben nach der Migration auf der Festplatte
PaddleSharp OCR: Von PaddleSharp heruntergeladene Modelldateien (Erkennung, Klassifizierung, Erkennung und Tabellenmodelle) werden typischerweise in einem models/-Verzeichnis relativ zur Anwendung oder in einem konfigurierten Pfad gespeichert. Diese Dateien werden bei der Deinstallation des NuGet Pakets nicht entfernt. In einem Docker-Image erhöhen sie die Layergröße unnötig. In einer Deployment-Pipeline können veraltete Modelldateien in alten Pfaden zu Startfehlern führen, falls noch Initialisierungscode darauf verweist.
Lösung: Modellverzeichnisse im Rahmen der Migration explizit entfernen. Überprüfen Sie die Startkonfiguration auf Pfadverweise:
# Locate model directory references in application code
grep -r "LocalFullModels\|ModelPath\|models/" --include="*.cs" .
grep -r "DetectionModel\|RecognitionModel\|ClassifierModel" --include="*.cs" .
# Locate model directory references in application code
grep -r "LocalFullModels\|ModelPath\|models/" --include="*.cs" .
grep -r "DetectionModel\|RecognitionModel\|ClassifierModel" --include="*.cs" .
Sobald die Modellreferenzen entfernt und IronOCR initialisiert sind, löschen Sie das Modellverzeichnis aus dem Repository und dem Docker-Build-Kontext.
Problem 3: Die Annahme der Singleton-Lebensdauer wird nach der Migration verletzt
PaddleSharp OCR: PaddleOcrAll wurde als Singleton registriert, da die Konstruktionskosten eine pro-Anfrage-Instanziierung unpraktisch machten. Migrationscode, der IronOCR in dieselbe Singleton-Registrierung überführt, führt zu einer unnötigen gemeinsamen Nutzung von Zuständen über Anfragen hinweg. Auch wenn IronTesseract threadsicher ist, wenn es gleichzeitig verwendet wird, ist es nicht notwendig, eine einzelne Instanz zu teilen — jede Instanz ist unabhängig.
Lösung: Prüfen Sie, ob die Singleton-Registrierung über die reine Performance hinaus einen Zweck erfüllt. Für die meisten ASP.NET Core Anwendungen ist die temporäre Registrierung mit IronOCR die sauberere Wahl:
// PaddleSharp — forced singleton due to construction cost
services.AddSingleton<PaddleOcrAll>(sp =>
{
var det = LocalFullModels.ChineseV3.DetectionModel; // Simplified
var cls = LocalFullModels.ChineseV3.ClassifierModel; // Simplified
var rec = LocalFullModels.ChineseV3.RecognitionModel; // Simplified
return new PaddleOcrAll(det, cls, rec);
});
//IronOCR— transient works; no expensive construction
services.AddTransient<IronTesseract>();
// PaddleSharp — forced singleton due to construction cost
services.AddSingleton<PaddleOcrAll>(sp =>
{
var det = LocalFullModels.ChineseV3.DetectionModel; // Simplified
var cls = LocalFullModels.ChineseV3.ClassifierModel; // Simplified
var rec = LocalFullModels.ChineseV3.RecognitionModel; // Simplified
return new PaddleOcrAll(det, cls, rec);
});
//IronOCR— transient works; no expensive construction
services.AddTransient<IronTesseract>();
Imports Microsoft.Extensions.DependencyInjection
' PaddleSharp — forced singleton due to construction cost
services.AddSingleton(Of PaddleOcrAll)(Function(sp)
Dim det = LocalFullModels.ChineseV3.DetectionModel ' Simplified
Dim cls = LocalFullModels.ChineseV3.ClassifierModel ' Simplified
Dim rec = LocalFullModels.ChineseV3.RecognitionModel ' Simplified
Return New PaddleOcrAll(det, cls, rec)
End Function)
' IronOCR— transient works; no expensive construction
services.AddTransient(Of IronTesseract)()
Für Batch-Szenarien mit hohem Durchsatz, bei denen eine explizite Wiederverwendung von Instanzen gewünscht ist, funktioniert ein Singleton- oder Pool-Muster weiterhin – dies ist jedoch eine Frage der Leistungsfähigkeit und keine Anforderung an die Korrektheit.
Ausgabe 4: Reihenfolge der Ergebnisse nach Region nicht mehr erforderlich
PaddleSharp OCR: result.Regions gibt erkannte Textbereiche in der Erkennungsreihenfolge zurück, die nicht notwendigerweise der Lesereihenfolge entspricht (links-nach-rechts, oben-nach-unten). Teams sortieren typischerweise nach .Rect.Center.Y, dann .Rect.Center.X, bevor sie Textregionen zusammenfügen — ein Muster, das in fast jeder PaddleSharp-Textextraktionsimplementierung erscheint. Eine wörtliche Übertragung dieses Musters auf IronOCR erzeugt redundanten Code.
Lösung:IronOCR liefert die Ergebnisse standardmäßig in Lesereihenfolge. Sortierung entfernen:
// PaddleSharp — manual reading-order sort required
var text = string.Join("\n", result.Regions
.OrderBy(r => r.Rect.Center.Y)
.ThenBy(r => r.Rect.Center.X)
.Select(r => r.Text));
//IronOCR— result.Text is already in reading order; no sort needed
var text = result.Text;
// For word-level access with position, use the structured hierarchy directly
foreach (var word in result.Pages[0].Words)
{
Console.WriteLine($"{word.Text} at ({word.X},{word.Y})");
}
// PaddleSharp — manual reading-order sort required
var text = string.Join("\n", result.Regions
.OrderBy(r => r.Rect.Center.Y)
.ThenBy(r => r.Rect.Center.X)
.Select(r => r.Text));
//IronOCR— result.Text is already in reading order; no sort needed
var text = result.Text;
// For word-level access with position, use the structured hierarchy directly
foreach (var word in result.Pages[0].Words)
{
Console.WriteLine($"{word.Text} at ({word.X},{word.Y})");
}
Imports System
Imports System.Linq
' PaddleSharp — manual reading-order sort required
Dim text = String.Join(vbLf, result.Regions _
.OrderBy(Function(r) r.Rect.Center.Y) _
.ThenBy(Function(r) r.Rect.Center.X) _
.Select(Function(r) r.Text))
' IronOCR— result.Text is already in reading order; no sort needed
text = result.Text
' For word-level access with position, use the structured hierarchy directly
For Each word In result.Pages(0).Words
Console.WriteLine($"{word.Text} at ({word.X},{word.Y})")
Next
Problem 5: Backend-bedingte Pakete verursachen Probleme bei der Wiederherstellung
PaddleSharp OCR: Einige PaddleSharp-Setups verweisen bedingt auf verschiedene Sdcb.PaddleInference.runtime.*-Pakete basierend auf der Zielumgebung (CUDA für GPU, MKL für OpenVINO, nur CPU). Dies erscheint manchmal als .csproj-Bedingungen oder als separate Projektdateien pro Bereitstellungsziel. Die resultierende Build-Matrix führt zu Fehlern in den CI-Pipelines, wenn der falsche Paketsatz wiederhergestellt wird.
Lösung: Entfernen Sie nach der Entfernung der PaddleSharp-Pakete die .csproj-Datei auf Bedingungen überprüft PackageReference-Blöcke, die auf irgendwelche Sdcb.*- oder OpenCvSharp*-Pakete verweisen, und entfernen Sie sie vollständig:
grep -n "Sdcb\|OpenCvSharp\|PaddleInference" *.csproj
grep -n "Sdcb\|OpenCvSharp\|PaddleInference" *.csproj
IronOCR verwendet eine einzelne IronOcr-Paketreferenz ohne Plattformbedingungen. Das gleiche Paket lässt sich unter Windows, Linux und macOS korrekt wiederherstellen.
Problem 6: Die Struktur der Tabellenergebnisse hat keine direkte Entsprechung
PaddleSharp OCR: PaddleOcrTable gibt eine zellbasierte Struktur mit Zeilen- und Spaltenindizes pro erkannter Zelle zurück. Code, der diese Struktur verwendet, erstellt typischerweise ein zweidimensionales Array, das nach (row, column) indiziert ist.IronOCR bietet keine identische Zellenindexstruktur – es liefert Wort- und Zeilenkoordinaten, die eine räumliche Gruppierung erfordern, um ein Zellraster zu rekonstruieren.
Lösung: Rekonstruieren Sie die Tabellenstruktur anhand der Wortkoordinaten von IronOCR, indem Sie die Zeilen nach Y-Position gruppieren und die Spalten nach X-Position sortieren. Für gängige Tabellenformate bietet die Anleitung zum Lesen von Tabellen einen Ansatz zur räumlichen Gruppierung. Für strukturierte Rechnungen mit bekannten Feldpositionen ist regionsbasierte OCR mit CropRectangle ein saubereres Muster als die Extraktion einer vollständigen Seitentabelle:
using IronOcr;
// Target specific table cells by region instead of full-page table detection
var totalAmountRegion = new CropRectangle(400, 600, 200, 30); // x, y, width, height
using var input = new OcrInput();
input.LoadImage("invoice.jpg", totalAmountRegion);
var result = new IronTesseract().Read(input);
Console.WriteLine($"Total: {result.Text}");
using IronOcr;
// Target specific table cells by region instead of full-page table detection
var totalAmountRegion = new CropRectangle(400, 600, 200, 30); // x, y, width, height
using var input = new OcrInput();
input.LoadImage("invoice.jpg", totalAmountRegion);
var result = new IronTesseract().Read(input);
Console.WriteLine($"Total: {result.Text}");
Imports IronOcr
' Target specific table cells by region instead of full-page table detection
Dim totalAmountRegion As New CropRectangle(400, 600, 200, 30) ' x, y, width, height
Using input As New OcrInput()
input.LoadImage("invoice.jpg", totalAmountRegion)
Dim result = New IronTesseract().Read(input)
Console.WriteLine($"Total: {result.Text}")
End Using
PaddleSharp OCR-Migrations-Checkliste
Vor der Migration
Überprüfen Sie alle PaddleSharp-Verweise im Code, bevor Sie Pakete entfernen:
# Find all PaddleSharp and PaddleInference usages
grep -rn "Sdcb\.PaddleOCR\|Sdcb\.PaddleInference" --include="*.cs" .
# Find OpenCV usages that will need replacement
grep -rn "OpenCvSharp\|Cv2\.\|using var.*Mat\b" --include="*.cs" .
# Find model path references and configuration
grep -rn "LocalFullModels\|ModelDir\|DetectionModel\|RecognitionModel\|ClassifierModel" --include="*.cs" .
# Find backend selection logic
grep -rn "EnableGpu\|EnableMkldnn\|PaddleConfig\|SetCpuMath" --include="*.cs" .
# Find table recognition usages
grep -rn "PaddleOcrTable\|TableModel\|table.*ocr\|ocr.*table" --include="*.cs" .
# Find result region access patterns that need updating
grep -rn "\.Regions\b\|Rect\.Center\|region\.Text" --include="*.cs" .
# Find all PaddleSharp and PaddleInference usages
grep -rn "Sdcb\.PaddleOCR\|Sdcb\.PaddleInference" --include="*.cs" .
# Find OpenCV usages that will need replacement
grep -rn "OpenCvSharp\|Cv2\.\|using var.*Mat\b" --include="*.cs" .
# Find model path references and configuration
grep -rn "LocalFullModels\|ModelDir\|DetectionModel\|RecognitionModel\|ClassifierModel" --include="*.cs" .
# Find backend selection logic
grep -rn "EnableGpu\|EnableMkldnn\|PaddleConfig\|SetCpuMath" --include="*.cs" .
# Find table recognition usages
grep -rn "PaddleOcrTable\|TableModel\|table.*ocr\|ocr.*table" --include="*.cs" .
# Find result region access patterns that need updating
grep -rn "\.Regions\b\|Rect\.Center\|region\.Text" --include="*.cs" .
Erstellen Sie eine Bestandsliste der Modelldateien auf der Festplatte und notieren Sie deren Pfade. Erfassen Sie alle Bereitstellungsziele und ob in .csproj GPU- oder OpenVINO-spezifische NuGet-Bedingungen vorhanden sind. Beachten Sie alle Dienste, die aufgrund der PaddleSharp-Konstruktionskosten als Singleton registriert sind.
Code-Migration
- Entfernen Sie alle
Sdcb.PaddleOCR,Sdcb.PaddleInference,OpenCvSharp4undOpenCvSharp4.runtime.*NuGet-Pakete aus jeder Projektdatei. - Installieren Sie das
IronOcrNuGet-Paket. - Installieren Sie Sprach-NuGet-Pakete für benötigte Sprachen (z.B.
IronOcr.Languages.ChineseSimplified). - Fügen Sie
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";zum Anwendungsstart hinzu. - Ersetzen Sie alle
using Sdcb.PaddleOCR,using Sdcb.PaddleInferenceundusing OpenCvSharpAnweisungen durchusing IronOcr. - Ersetzen Sie die
PaddleOcrAllInstanziierung und Modellladung durchnew IronTesseract(). - Löschen Sie alle
PaddleConfigBackend-Auswahlblöcke (CPU, GPU, OpenVINO Bedingungen). - Ersetzen Sie
Cv2.ImRead(path)Aufrufe durchinput.LoadImage(path)mitOcrInput. - Ersetzen Sie OpenCV-Vorverarbeitungsoperationen (
CvtColor,GaussianBlur,Threshold, etc.) durchOcrInputFiltermethoden (Deskew(),DeNoise(),Contrast(),Binarize()). - Ersetzen Sie
ocr.Run(mat)Aufrufe durchocr.Read(input). - Ersetzen Sie die
result.Regions-Aufzählung durchresult.Pages,result.Pages[n].Linesoderresult.Pages[n].Words. - Entfernen Sie die
.OrderBy(r => r.Rect.Center.Y).ThenBy(r => r.Rect.Center.X)-Sortierketten — die Lesereihenfolge ist automatisch. - Ersetzen Sie die
PaddleOcrTableInitialisierung und Ergebnisanalyse durchOcrInputregionsbasiertes Zielanpeilen oder koordinatenbasiertes Wortgruppieren. - Fügen Sie
result.SaveAsSearchablePdf(path)hinzu, überall dort, wo ein durchsuchbares PDF-Archiv erforderlich ist. - Überprüfung der Lebensdauer von Registrierungen: Singleton-Registrierungen, die durch die Konstruktionskosten von PaddleSharp gesteuert werden, können in der Regel als transient oder scoped definiert werden.
- Löschen Sie Modelldateien von der Festplatte und entfernen Sie Modellverzeichnisse aus den Docker-Build-Kontexten.
- Entfernen Sie alle
.csprojbedingtenPackageReferenceBlöcke für plattformspezifische Paddle- oder OpenCV-Laufzeitpakete.
Nach der Migration
- Überprüfen Sie anhand einer repräsentativen Stichprobe von 20–30 Dokumenten aus jedem Dokumenttyp in der Pipeline, ob die Ergebnisse der Textextraktion denen von PaddleSharp entsprechen oder diese übertreffen.
- Bestätigen Sie keine
OpenCvSharp-bezogenen Assembly-Load-Ausnahmen in den Anwendungsstartprotokollen. - Testen Sie die Bereitstellung auf jeder Zielplattform (Windows, Linux, Docker) unter Verwendung desselben Build-Artefakts – eine plattformspezifische Paketauswahl sollte nicht erforderlich sein.
- Verifizieren Sie, dass Dokumente, die zuvor manuelle Ergebnissortierung erforderten, korrekt geordneten Text über
result.Textproduzieren. - Stellen Sie sicher, dass die erstellten durchsuchbaren PDF-Dateien in Adobe Acrobat Reader oder einem PDF-Viewer Ihrer Wahl textdurchsuchbar sind.
- Führen Sie die Anwendung unter Last aus, um zu bestätigen, dass
IronTesseract-Instanzen, die pro Anfrage erstellt werden, keinen Speicherbelastungsdruck vergleichbar zur pro AnfragePaddleOcrAll-Erstellung erzeugen. - Stellen Sie sicher, dass als NuGet-Pakete installierte Sprachpakete in der CI korrekt wiederhergestellt werden, ohne dass zusätzliche Schritte zur Dateibereitstellung erforderlich sind.
- Testen Sie alle Szenarien zur Tabellenextraktion anhand der erwarteten Zeilen-/Spaltenstruktur unter Verwendung des regionbasierten oder koordinatenbasierten Ansatzes.
- Bestätigen Sie, dass die Anwendungsstartzeit nach der Eliminierung der
PaddleOcrAll-Singleton-Erstellung aus dem Startpfad abnimmt.
Wichtigste Vorteile der Migration zu IronOCR
Ein Paket ersetzt einen Vier-Paket-Stack. Nach der Migration ist der OCR-Abhängigkeitsumfang eine einzelne IronOcr NuGet-Referenz. Der Vier-Paket-Stack — Sdcb.PaddleOCR, Sdcb.PaddleInference, OpenCvSharp4 und eine plattformspezifische Laufzeit — wird zu einem Eintrag in der Projektdatei. Abhängigkeitsprüfungen, Lizenzscans und Sicherheitsüberwachung decken nun eine Fläche statt vier ab.
Bereitstellungsartefakte sind über alle Umgebungen hinweg einheitlich. Die Bedingungen für die Backend-Auswahl – CPU versus GPU versus OpenVINO – entfallen. Dasselbe Build-Artefakt wird auf einem Entwickler-Laptop, einem CI-Runner, einem Linux-Container und einer Cloud-VM bereitgestellt, ohne dass eine umgebungsspezifische Paketauswahl oder Initialisierungsverzweigung erforderlich ist. Docker-Images schrumpfen, da es keine Modelldateien zum COPY gibt und keine plattformabhängigen Laufzeitpakete installiert werden müssen.
Dokumentenarchivpipelines erfordern keine zweite Bibliothek mehr. result.SaveAsSearchablePdf() eliminiert die PDF-Bibliotheksabhängigkeit, die die meisten PaddleSharp-Teams hinzugefügt hatten, um durchsuchbare Archive zu erstellen. Der OCR-Durchlauf und das Schreiben des durchsuchbaren PDFs erfolgen über einen einzigen API-Aufruf. Für Teams, die täglich Tausende von gescannten Dokumenten verarbeiten, beseitigt diese Vereinfachung eine ganze Klasse von Versionskonflikten zwischen Bibliotheken. Der Blogbeitrag zu durchsuchbaren PDFs behandelt Überlegungen zur Produktionseinsatzfähigkeit.
Entscheidungen über die Lebensdauer von Diensten spiegeln Anwendungsanforderungen wider, nicht Bibliothekseinschränkungen. IronTesseract hat eine leichte Konstruktion. Das durch das aufwendige Laden von Modellen in PaddleSharp bedingte Forced-Singleton-Muster ist nicht mehr erforderlich. Dienste können in .NET Core auf Anfrage begrenzt werden, wodurch eine klarere Trennung zwischen gleichzeitigen Benutzern geschaffen und Bedenken hinsichtlich Threading mit gemeinsamem Status beseitigt werden. Weitere Informationen zu Bereitstellungsoptionen finden Sie auf der Seite mit Anwendungsbeispielen für ASP.NET OCR.
Die Spracherweiterung ist eine Paketinstallation, kein Forschungsprojekt. Der Katalog mit über 125 Sprachen umfasst europäische, asiatische, nahöstliche und spezielle Schriftsysteme als NuGet-Pakete. Französisch, Deutsch, Arabisch oder Japanisch zu einer Pipeline hinzufügen, die als Chinesisch-Only begann, ist dotnet add package IronOcr.Languages.French und eine Konfigurationszeile. Kein Modelldatei-Quellen, keine Verfügbarkeitsrecherche upstream, keine manuelle Datei-Bereitstellung.
Vorverarbeitung ist Teil der OCR-API. Das OpenCV-Wissen, das PaddleSharp für die Vorverarbeitung erforderte — Filterkernel verstehen, Mat Entsorgung verwalten, adaptive Threshold-Parameter auswählen — ist nicht mehr Voraussetzung für OCR-Arbeiten. OcrInput bietet benannte Operationen mit sinnvollen Standardwerten. Teams, die keine OpenCV-Spezialisten waren, aber OpenCV-Vorverarbeitungscode warteten, können diesen Code löschen, ohne ihn zu ersetzen. Auf der Seite mit den Vorverarbeitungsfunktionen sind alle verfügbaren Filter aufgeführt, zusammen mit einer Dokumentation dazu, wann die einzelnen Filter anzuwenden sind.
Häufig gestellte Fragen
Warum sollte ich von PaddleSharp 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 PaddleSharp OCR zu IronOCR?
Ersetzen Sie PaddleSharp-Initialisierungssequenzen durch IronTesseract-Instanziierung, 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 PaddleSharp 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 PaddleSharp OCR separat installiert?
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 PaddleSharp OCR zu IronOCR Änderungen an der Bereitstellungsinfrastruktur?
IronOCR erfordert weniger Änderungen an der Infrastruktur als PaddleSharp 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 ein String, der im Anwendungscode festgelegt wird.
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 PaddleSharp?
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 PaddleSharp OCR bei der Skalierung von Arbeitslasten?
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 PaddleSharp 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.

