Passer au contenu du pied de page
VIDéOS

Comment lire les GIF et TIFF multi-trames en C#

Ce guide guide les développeurs .NET à travers une migration complète de PaddleSharp OCR(Sdcb.PaddleOCR) à IronOCR. Il s'agit de remplacer la gestion des sessions d'inférence, d'éliminer la dépendance au prétraitement OpenCV, de supprimer la logique de sélection du backend pour CPU, GPU et OpenVINO et de migrer les flux de travail de reconnaissance de tableaux. Chaque section fournit un code avant et après, tiré de modèles spécifiques à PaddleSharp qui n'apparaissent pas dans les comparaisons OCR génériques.

Pourquoi migrer depuis PaddleSharp OCR?

PaddleSharp expose un pipeline d'inférence d'apprentissage profond au niveau de la couche application. Cette architecture vous donne accès aux performances du modèle PaddlePaddle, mais exige que votre application gère ce qui relèverait autrement de l'infrastructure. Les difficultés suivantes incitent la plupart des équipes .NET à rechercher des alternatives.

La configuration du backend d'inférence est un code d'application. Choisir entre les backends CPU, GPU et OpenVINO dans PaddleSharp nécessite de construire et configurer des objets PaddleConfig, de sélectionner le bon package NuGet d'exécution native pour la cible de déploiement, et de détourner conditionnellement votre code d'initialisation en fonction du matériel disponible à l'exécution. Cette logique réside dans votre application, pas dans la bibliothèque, et elle se brise lorsque l'environnement cible change.

OpenCV est une dépendance requise pour l'entrée d'images. PaddleSharp ne peut pas accepter directement un chemin de fichier ou un flux. Chaque image passe par l'Cv2.ImRead() d'OpenCV avant de parvenir au moteur OCR. Ceci force OpenCvSharp4 et un package OpenCvSharp4.runtime.* spécifique à la plateforme dans votre graphique de dépendances. La mise à jour d'un environnement d'exécution sans l'autre provoque des défaillances d'exécution difficiles à reproduire dans différents environnements.

La durée de vie de la session d'inférence nécessite une conception explicite. PaddleOcrAll charge trois binaires de modèle depuis le disque lors de la construction. Ce coût — mesurable en centaines de millisecondes — signifie que l'objet ne peut pas être instancié par demande. Les équipes doivent concevoir une stratégie de cycle de vie : singleton, mise en commun ou mise à l'échelle. Dans ASP.NET Core, cela signifie typiquement un service enregistré avec une analyse minutieuse de la sécurité des threads, car PaddleOcrAll partage l'état natif sous-jacent.

La reconnaissance de tableaux nécessite le téléchargement d'un modèle supplémentaire. L'extraction de documents structurés dans PaddleSharp requiert un modèle de reconnaissance de tableaux dédié, en plus du pipeline standard en trois étapes (détection, classification, reconnaissance). Ce modèle constitue un quatrième fichier à télécharger, à versionner et à configurer. Il n'existe pas d'interface API unifiée ; la reconnaissance des tables utilise un chemin de code distinct avec son propre type de résultat.

Aucun fichier PDF consultable en sortie. PaddleSharp génère des chaînes de caractères. Il ne peut pas écrire de fichiers PDF consultables. Les équipes qui ont besoin d'archiver des documents numérisés sous forme de PDF interrogeables doivent intégrer une bibliothèque PDF distincte, gérer cette dépendance supplémentaire et écrire une couche de conversion. Le fossé en matière de format de sortie est total : pas de hOCR, pas de PDF structuré et consultable, pas de superposition de couche de texte.

La chaîne de dépendances en amont n'appartient pas à la communauté .NET . PaddleSharp encapsule le framework d'inférence PaddlePaddle de Baidu. Par le passé, des modifications du format des modèles entre les versions de PaddleOCR ont provoqué des dysfonctionnements au niveau de la couche de liaison .NET . La plupart des suivis de problèmes, de la documentation et des discussions relatives aux versions sont en chinois. Pour une équipe .NET sans locuteurs mandarin assurant le suivi des projets en amont, les changements majeurs surviennent sans prévenir.

Le problème fondamental

La sélection et l'initialisation d'un backend dans PaddleSharp nécessitent un code de configuration qui relève de l'infrastructure, et non de la logique OCR :

// 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
$vbLabelText   $csharpLabel
// IronOCR: Non backend selection. Non config 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: Non backend selection. Non config 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: Non backend selection. Non config 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
$vbLabelText   $csharpLabel

IronOCR vs PaddleSharp OCR: comparaison des fonctionnalités

Voici une comparaison directe des capacités sur les aspects les plus importants lors de la migration :

Fonction PaddleSharp OCR IronOCR
Packages NuGet requis 3 à 4 minimum 1
Méthode de saisie d'images OpenCV Cv2.ImRead() Chemin direct, flux ou tableau d'octets
Entrée PDF (native) Non Oui
PDF protégé par mot de passe Non Oui
TIFF multipage Via OpenCV Natif
Sortie PDF consultable Non Oui (result.SaveAsSearchablePdf())
Exportation hOCR Non Oui
Sélection du backend (CPU/GPU/OpenVINO) Manuel PaddleConfig Automatique
Pipeline de prétraitement Opérations manuelles OpenCV Intégré (Deskew, DeNoise, Contrast, etc.)
Gestion du cycle de vie des sessions d'inférence Manuel (construction coûteuse) Léger IronTesseract
Modèle de reconnaissance de tableaux Téléchargement et chemin d'accès au code séparés input.LoadImage() + résultat structuré
Langues prises en charge ~10-20 125+
Installation linguistique Téléchargement du fichier modèle Paquet NuGet
Multilingue simultané Limité Oui (OcrLanguage.French + OcrLanguage.German)
OCR basé sur la région Pas de fonctionnalités intégrées CropRectangle
Lecture de codes-barres lors de la reconnaissance optique de caractères (OCR) Non Oui (ocr.Configuration.ReadBarCodes = true)
scores de confiance Par région Au mot, à la ligne, à la page
Hiérarchie de sortie structurée Liste des régions plates Pages → Paragraphes → Lignes → WORDs → Caractères
Déploiement multiplateforme Complexe (packages d'exécution de la plateforme) NuGet unique, toutes plateformes
Déploiement de Docker Plusieurs couches, packages d'exécution Une seule couche
Soutien commercial Tickets GitHub (principalement en chinois) Assistance par e-mail
Modèle de licence Apache 2.0 Perpetual ($999 Lite, $1,499 Pro, $2,999 Enterprise)

Guide de démarrage rapide : migration de PaddleSharp OCRvers IronOCR

Étape 1 : Remplacer le package NuGet

Supprimer PaddleSharp et sa dépendance OpenCV :

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
SHELL

Installez IronOCR depuis NuGet :

dotnet add package IronOcr

Étape 2 : Mise à jour des espaces de noms

Remplacer les espaces de noms PaddleSharp par l'espace de noms unique IronOCR :

// 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
$vbLabelText   $csharpLabel

Étape 3 : initialisation de la licence

Ajoutez l'initialisation de la licence une fois au démarrage de l'application — dans Program.cs, Startup.cs ou votre racine de composition :

IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
$vbLabelText   $csharpLabel

Exemples de migration de code

Remplacement du cycle de vie des sessions d'inférence

L'PaddleOcrAll de PaddleSharp est coûteux à construire car il charge trois binaires de modèle de manière synchrone à l'instanciation. Les applications de production doivent le traiter comme un objet à longue durée de vie, ce qui implique un modèle d'injection de dépendances spécifique. La chaîne de libération nécessite également une attention particulière, car les ressources natives sous-jacentes doivent être libérées dans le bon ordre.

Approche OCR de PaddleSharp :

// 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)()
$vbLabelText   $csharpLabel

Approche IronOCR :

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)()
$vbLabelText   $csharpLabel

Le shift d'un singleton forcé à une durée de vie flexible est significatif. Le coût de construction de PaddleSharp détermine votre choix de service pour toute la durée de vie du produit ; IronOCR vous permet de faire votre choix en fonction des besoins de votre application en matière de threading et d'isolation des requêtes. Le guide d'installation d'IronTesseract couvre les options de configuration applicables au niveau de l'instance.

Migration du pipeline de prétraitement OpenCV

Les équipes PaddleSharp traitant des numérisations de mauvaise qualité mettent généralement en place un pipeline de prétraitement OpenCV avant d'invoquer le moteur OCR. Ce pipeline nécessite une connaissance de l'interface API d'OpenCV, qui est nettement plus étendue que ce dont toute tâche de prétraitement OCR a réellement besoin. Les opérations courantes — redressement, réduction du bruit, étirement du contraste — nécessitent plusieurs opérations Mat et une gestion soigneuse de la mémoire avec des blocs using pour éviter les fuites de mémoire natives.

Approche OCR de PaddleSharp :

// 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
$vbLabelText   $csharpLabel

Approche IronOCR :

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
$vbLabelText   $csharpLabel

Aucune allocation Mat. Aucune connaissance des paramètres de seuil adaptatif. Pas de calculs mathématiques personnalisés pour la rotation de redressement. Le même pipeline de prétraitement qui nécessitait 30 à 50 lignes de code OpenCV se résume désormais à quatre appels de méthode. Le guide de correction de la qualité d'image répertorie tous les filtres disponibles avec des exemples avant/après. Pour les documents avec beaucoup de bruit de fond, input.DeepCleanBackgroundNoise() va plus loin que DeNoise() sans aucun paramètre supplémentaire.

Pour les équipes dont les exigences de prétraitement ne sont pas standard, l'assistant de filtrage fournit un outil interactif permettant d'évaluer des combinaisons de filtres sur vos types de documents spécifiques avant de valider le code.

Élimination de la sélection du backend

PaddleSharp expose le backend d'inférence comme une préoccupation au niveau de l'application. Un déploiement devant s'exécuter sur une machine virtuelle cloud équipée uniquement d'un processeur utilise un code d'initialisation différent de celui destiné à une station de travail GPU ou à un périphérique de bord compatible avec Intel OpenVINO. Cette logique conditionnelle se retrouve généralement dans le code de démarrage de l'application, les vérifications des variables d'environnement ou les indicateurs de fonctionnalités — un travail d'infrastructure qui n'a rien à voir avec la lecture de texte à partir d'images.

Approche OCR de PaddleSharp :

// 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
$vbLabelText   $csharpLabel

Approche IronOCR :

using IronOcr;

// Non backend selection. Non switch statement. Non environment 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;

// Non backend selection. Non switch statement. Non environment 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
    ' Non backend selection. Non switch statement. Non environment 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
$vbLabelText   $csharpLabel

Le modèle Parallel.ForEach ici est sûr en termes de threads dès le départ. Chaque instance d'IronTesseract est indépendante sans état natif partagé. Pour les équipes dont le déploiement de PaddleSharp nécessite du temps pour gérer les conditions du backend, cette simplification constitue également une amélioration de la fiabilité du déploiement : le même artefact de build s'exécute partout sans code de détection du matériel. Le guide d'optimisation de la vitesse couvre les options de configuration pour les scénarios sensibles au débit.

Migration de la reconnaissance de tableaux

L'extraction de tableaux dans PaddleSharp nécessite un modèle de reconnaissance de tableaux dédié — un quatrième fichier de modèle en plus de l'ensemble standard de détection, de classification et de reconnaissance. Le modèle de table utilise un appel API distinct et renvoie sa propre structure de résultats. Les équipes chargées de la mise en place de pipelines de traitement des factures, des formulaires ou des feuilles de calcul gèrent deux chemins d'initialisation parallèles et deux stratégies d'analyse des résultats.

Approche OCR de PaddleSharp :

// 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
$vbLabelText   $csharpLabel

Approche IronOCR :

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
$vbLabelText   $csharpLabel

Pour les documents dont la structure du tableau doit être extraite sous forme de lignes et de colonnes, IronOCR offre une fonctionnalité dédiée à l'extraction de tableaux :

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
$vbLabelText   $csharpLabel

Un téléchargement de modèle supprimé. Un chemin d'initialisation supprimé. La hiérarchie structurée des résultats dans IronOCR — avec des coordonnées X/Y au niveau des mots — fournit les données de position nécessaires pour reconstruire les lignes et les colonnes d'un tableau sans modèle de reconnaissance distinct. Le guide de lecture du tableau et le guide des résultats de lecture couvrent l'intégralité de l'API de sortie structurée.

Fichiers PDF consultables à partir de documents numérisés

PaddleSharp produit des chaînes de texte et rien d'autre. La création d'une archive de documents permettant la recherche de texte dans des PDF numérisés nécessite l'intégration d'une bibliothèque PDF distincte, l'écriture d'une couche de superposition de texte et la gestion conjointe de ces deux bibliothèques. Les équipes qui ont accepté cette contrainte constatent souvent que c'est ce qui déclenche la migration : l'effort d'intégration des deux bibliothèques dépasse celui nécessaire pour changer de fournisseur d'OCR.

Approche OCR de PaddleSharp :

// 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
$vbLabelText   $csharpLabel

Approche IronOCR :

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
$vbLabelText   $csharpLabel

Pas de bibliothèque PDF. Pas de mappage de coordonnées. Pas de positionnement des calques de texte. Le format de sortie PDF consultable d'IronOCR intègre une couche de texte invisible par-dessus l'image d'origine, produisant ainsi un fichier à la fois fidèle visuellement au document numérisé et entièrement consultable par recherche textuelle. Le guide pratique au format PDF consultable couvre la sélection des pages, les options de qualité et le contrôle des métadonnées.

Référence de mappage de l'API OCR PaddleSharp vers IronOCR

PaddleSharp OCR IronOCR
Sdcb.PaddleOCR (namespace) IronOcr (namespace)
Sdcb.PaddleInference (namespace) Non nécessaire — configuration automatique
PaddleOcrAll IronTesseract
new PaddleOcrAll(det, cls, rec) new IronTesseract()
LocalFullModels.ChineseV3.DetectionModel Pas d'équivalent — pas de sélection de modèle
LocalFullModels.ChineseV3.ClassifierModel Pas d'équivalent — pas de sélection de modèle
LocalFullModels.ChineseV3.RecognitionModel Pas d'équivalent — pas de sélection de modèle
PaddleConfig.FromModelDir() Pas d'équivalent — pas d'objet de configuration
config.EnableGpu(memMB, deviceId) Pas d'équivalent — le backend est automatique
config.EnableMkldnn() Pas d'équivalent — le backend est automatique
config.SetCpuMathLibraryNumThreads(n) Pas d'équivalent — géré en interne
Cv2.ImRead(path) (chargement OpenCV) input.LoadImage(path)
ocr.Run(mat) ocr.Read(input) ou ocr.Read("file.jpg")
result.Regions result.Pages[0].Words ou result.Pages[0].Lines
region.Text word.Text, line.Text, paragraph.Text
region.Rect.Center.X/.Y word.X, word.Y
region.Score (confiance) word.Confidence, result.Confidence
Changement de langue au niveau du modèle ocr.Language = OcrLanguage.French
Modèle de tableau (téléchargement séparé) Hiérarchie structurée des résultats intégrée
Cv2.CvtColor(..., GRAY) input.Binarize() ou input.Contrast()
Cv2.GaussianBlur(...) input.DeNoise()
Aucun fichier PDF consultable result.SaveAsSearchablePdf("output.pdf")

Problèmes de migration courants et solutions

Problème n° 1 : la dépendance OpenCV ne se décharge pas

PaddleSharp OCR : OpenCvSharp4.runtime.win et des packages similaires d'exécution spécifiques aux plateformes installent des DLLs natives non gérées. Ces DLL peuvent empêcher un nettoyage correct dans certains scénarios d'hébergement — en particulier le recyclage du pool d'applications IIS — et provoquer des échecs de chargement d'assembly lorsque le mauvais package d'exécution de la plateforme est référencé au moment de la compilation. Leur suppression nécessite à la fois la suppression du package NuGet et l'effacement de tous les binaires natifs mis en cache dans le répertoire de sortie.

Solution : Après avoir supprimé les packages OpenCvSharp4 et OpenCvSharp4.runtime.*, nettoyez le répertoire de sortie de construction avant de reconstruire :

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
SHELL

IronOCR regroupe ses dépendances natives en interne et gère le cycle de vie non géré. Aucune sélection de package d'exécution spécifique à une plateforme n'est requise. Le guide d'installation d'IronTesseract documente les exigences de la plateforme qu'IronOCR gère automatiquement.

Problème n° 2 : fichiers de modèle restés sur le disque après la migration

PaddleSharp OCR : Les fichiers de modèle téléchargés par PaddleSharp (détection, classification, reconnaissance et modèles de table) sont généralement stockés dans un répertoire models/ relatif à l'application ou dans un chemin configuré. Ces fichiers ne sont pas supprimés lors de la désinstallation du package NuGet. Dans une image Docker, ils ajoutent une taille de couche inutile. Dans un pipeline de déploiement, des fichiers de modèle obsolètes situés dans d'anciens chemins d'accès peuvent provoquer des échecs au démarrage si un code d'initialisation résiduel y fait référence.

Solution : Supprimer explicitement les répertoires de modèles dans le cadre de la migration. Vérifiez la configuration de démarrage pour détecter toute référence de chemin d'accès :

# 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" .
SHELL

Une fois les références au modèle supprimées et IronOCR initialisé, supprimez le répertoire du modèle du référentiel et du contexte de build Docker.

Problème n° 3 : la durée de vie du singleton n'est plus respectée après la migration

PaddleSharp OCR : PaddleOcrAll a été enregistré comme singleton car son coût de construction rendait impraticable l'instanciation par demande. Le code de migration qui intègre IronOCR dans le même enregistrement singleton introduit un partage d'état inutile entre les requêtes. Bien qu'IronTesseract soit sûr en termes de threads lorsqu'il est utilisé de manière concurrente, il n'est pas nécessaire de partager une seule instance — chaque instance est indépendante.

Solution : Évaluez si l'enregistrement du singleton a une utilité autre que la performance. Pour la plupart des applications ASP.NET Core, l'enregistrement transitoire est le choix le plus judicieux avec IronOCR :

// 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)()
$vbLabelText   $csharpLabel

Pour les scénarios de traitement par lots à haut débit où vous souhaitez une réutilisation explicite des instances, un modèle singleton ou poolé fonctionne toujours — mais il s'agit d'un choix de performance, et non d'une exigence de correction.

Problème n° 4 : le classement des régions de résultats n'est plus nécessaire

PaddleSharp OCR : result.Regions retourne des régions de texte détectées dans l'ordre de détection, ce qui ne correspond pas nécessairement à l'ordre de lecture (de gauche à droite, de haut en bas). Les équipes appliquent généralement un tri par .Rect.Center.Y puis .Rect.Center.X avant de joindre le texte des régions — un modèle qui apparaît dans presque toutes les implémentations d'extraction de texte de PaddleSharp. La transposition littérale de ce modèle vers IronOCR génère du code redondant.

Solution : IronOCR renvoie les résultats par ordre de lecture par défaut. Supprimer le tri :

// 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(vbCrLf, 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
$vbLabelText   $csharpLabel

Problème n° 5 : les paquets conditionnels du backend interrompent la restauration

PaddleSharp OCR : Certains réglages PaddleSharp font référence sous condition à différents packages Sdcb.PaddleInference.runtime.* basés sur l'environnement cible (CUDA pour GPU, MKL pour OpenVINO, uniquement CPU). Cela apparaît parfois comme des conditions .csproj ou comme des fichiers de projet distincts par cible de déploiement. La matrice de build qui en résulte rompt les pipelines CI lorsque le mauvais ensemble de paquets est restauré.

Solution : Après avoir supprimé les packages PaddleSharp, examinez le fichier .csproj pour les blocs PackageReference conditionnels faisant référence à des packages Sdcb.* ou OpenCvSharp* et supprimez-les entièrement :

grep -n "Sdcb\|OpenCvSharp\|PaddleInference" *.csproj
grep -n "Sdcb\|OpenCvSharp\|PaddleInference" *.csproj
SHELL

IronOCR utilise une seule référence de package IronOcr sans conditionnel de plateforme. Le même package s'installe correctement sous Windows, Linux et macOS.

Problème n° 6 : la structure des résultats du tableau n'a pas d'équivalent direct

PaddleSharp OCR : PaddleOcrTable renvoie une structure basée sur des cellules avec des indices de ligne et de colonne par cellule reconnue. Le code qui consomme cette structure construit généralement un tableau à deux dimensions indexé par (row, column). IronOCR ne fournit pas une structure d'index de cellules identique — il fournit des coordonnées de mots et de lignes qui nécessitent un regroupement spatial pour reconstruire une grille de cellules.

Solution : Reconstruire la structure du tableau à partir des coordonnées des mots d'IronOCR en utilisant le regroupement par position Y pour les lignes et le tri par position X pour les colonnes. Pour les formats de tableau courants, le guide pratique de lecture des tableaux propose une approche de regroupement spatial. Pour des factures structurées avec des positions de champs connues, l'OCR basé sur des régions avec CropRectangle est un modèle plus propre qu'une extraction de table pleine page :

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
$vbLabelText   $csharpLabel

Liste de contrôle pour la migration vers PaddleSharp OCR

Pré-migration

Vérifiez toutes les références à PaddleSharp dans le code source avant de supprimer les paquets :

# 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" .
SHELL

Répertoriez les fichiers modèles sur le disque et notez leurs chemins d'accès. Inventoriez toutes les cibles de déploiement et si certaines ont des conditionnels NuGet spécifiques à GPU ou OpenVINO dans .csproj. Notez que certains services sont enregistrés en tant que singleton en raison du coût de construction de PaddleSharp.

Migration de code

  1. Supprimez tous les packages NuGet Sdcb.PaddleOCR, Sdcb.PaddleInference, OpenCvSharp4 et OpenCvSharp4.runtime.* de chaque fichier de projet.
  2. Installez le package NuGet IronOcr.
  3. Installez les packages NuGet de langue pour les langues requises (par exemple, IronOcr.Languages.ChineseSimplified).
  4. Ajoutez IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; au démarrage de l'application.
  5. Remplacez toutes les déclarations using Sdcb.PaddleOCR, using Sdcb.PaddleInference et using OpenCvSharp par using IronOcr.
  6. Remplacez l'instanciation PaddleOcrAll et le chargement du modèle par new IronTesseract().
  7. Supprimez tous les blocs de sélection de backend PaddleConfig (CPU, GPU, conditionnels OpenVINO).
  8. Remplacez les appels Cv2.ImRead(path) par input.LoadImage(path) en utilisant OcrInput.
  9. Remplacez les opérations de prétraitement OpenCV (CvtColor, GaussianBlur, Threshold, etc.) par des méthodes de filtrage OcrInput (Deskew(), DeNoise(), Contrast(), Binarize()).
  10. Remplacez les appels ocr.Run(mat) par ocr.Read(input).
  11. Remplacez l'énumération result.Regions par result.Pages, result.Pages[n].Lines ou result.Pages[n].Words.
  12. Retirez les chaînes de tri .OrderBy(r => r.Rect.Center.Y).ThenBy(r => r.Rect.Center.X) — l'ordre de lecture est automatique.
  13. Remplacez l'initialisation PaddleOcrTable et l'analyse des résultats par le ciblage basé sur les régions OcrInput ou le regroupement de mots basé sur les coordonnées.
  14. Ajoutez result.SaveAsSearchablePdf(path) partout où une archive PDF recherchable est requise.
  15. Réévaluer les enregistrements de durée de vie des services : les enregistrements singleton pilotés par le coût de construction de PaddleSharp peuvent généralement devenir transitoires ou limités à une portée.
  16. Supprimez les fichiers de modèle du disque et retirez les répertoires de modèle des contextes de build Docker.
  17. Supprimez tous les blocs conditionnels .csproj pour les packages d'exécution Paddle ou OpenCV spécifiques à la plateforme.

Après la migration

  • Vérifiez que les résultats de l'extraction de texte égalent ou dépassent ceux de PaddleSharp sur un échantillon représentatif de 20 à 30 documents de chaque type de document dans le pipeline.
  • Confirmez qu'il n'y a pas d'exceptions de chargement d'assemblage liées à OpenCvSharp dans les journaux de démarrage de l'application.
  • Testez le déploiement sur chaque plateforme cible (Windows, Linux, Docker) en utilisant le même artefact de build — aucune sélection de package spécifique à la plateforme ne devrait être nécessaire.
  • Vérifiez que les documents nécessitant précédemment un tri manuel des résultats produisent un texte correctement ordonné via result.Text.
  • Vérifiez que les fichiers PDF générés sont consultables dans Adobe Acrobat Reader ou dans le lecteur PDF de votre choix.
  • Exécutez l'application sous charge pour confirmer que les instances IronTesseract créées par demande ne produisent pas de pression mémoire comparable à la construction PaddleOcrAll par demande.
  • Vérifiez que les packs de langues installés sous forme de paquets NuGet se restaurent correctement en CI sans étapes de déploiement de fichiers supplémentaires.
  • Testez tous les scénarios d'extraction de tableaux par rapport à la structure attendue des lignes et des colonnes en utilisant l'approche par région ou par regroupement de coordonnées.
  • Confirmez que le temps de démarrage de l'application diminue après avoir éliminé la construction PaddleOcrAll singleton du chemin de démarrage.

Principaux avantages de la migration vers IronOCR

Un package remplace une pile de quatre packages. Après la migration, l'empreinte de dépendance de l'OCR est une seule référence NuGet IronOcr. La pile de quatre packages — Sdcb.PaddleOCR, Sdcb.PaddleInference, OpenCvSharp4 et un runtime spécifique à la plateforme — devient une seule entrée dans le fichier de projet. Les audits de dépendance, les analyses de licence et la surveillance des vulnérabilités couvrent désormais une seule surface au lieu de quatre.

Les artefacts de déploiement sont uniformes dans tous les environnements. Les conditions de sélection du backend — CPU, GPU ou OpenVINO — ont disparu. Le même artefact de build se déploie sur un ordinateur portable de développeur, un exécuteur CI, un conteneur Linux et une machine virtuelle cloud sans sélection de paquets spécifique à l'environnement ni branchement d'initialisation. Les images Docker se réduisent car il n'y a pas de fichiers de modèle à COPY et aucun package d'exécution de plateforme à installer.

Les pipelines d'archives de documents ne nécessitent plus une seconde bibliothèque. result.SaveAsSearchablePdf() élimine la dépendance à la bibliothèque PDF que la plupart des équipes PaddleSharp avaient ajoutée pour produire des archives recherchables. Le passage en OCR et l'écriture au format PDF consultable constituent un seul appel d'API. Pour les équipes traitant des milliers de documents numérisés par jour, cette simplification élimine toute une catégorie de conflits de versions entre bibliothèques. L'article de blog sur les PDF consultables aborde les considérations relatives à l'échelle de production.

Les décisions sur la durée de vie du service reflètent les besoins de l'application, et non les contraintes de la bibliothèque. IronTesseract a une construction légère. Le modèle singleton forcé induit par le chargement coûteux des modèles de PaddleSharp n'est plus nécessaire. Les services peuvent être délimités par requête dans .NET Core, ce qui permet une isolation plus nette entre les utilisateurs simultanés et élimine les problèmes liés au threading en état partagé. Pour en savoir plus sur les options de déploiement, consultez la page des cas d'utilisation d'ASP.NET OCR.

L'extension linguistique est une installation de package, pas un projet de recherche. Le catalogue de plus de 125 langues couvre les scripts européens, asiatiques, du Moyen-Orient et spécialisés sous forme de packages NuGet. Ajouter le français, l'allemand, l'arabe ou le japonais à un pipeline qui a commencé uniquement en chinois est dotnet add package IronOcr.Languages.French et une ligne de configuration. Pas de recherche de fichiers de modèle, pas de recherche de disponibilité en amont, pas de déploiement manuel de fichiers.

Le prétraitement fait partie de l'API OCR. Les connaissances OpenCV que le prétraitement de PaddleSharp nécessitait — comprendre les noyaux de filtre, gérer l'élimination Mat, sélectionner les paramètres de seuil adaptatif — ne sont plus une condition préalable pour le travail d'OCR. OcrInput fournit des opérations nommées avec des valeurs par défaut raisonnables. Les équipes qui n'étaient pas spécialisées dans OpenCV mais qui maintenaient du code de prétraitement OpenCV peuvent supprimer ce code sans le remplacer. La page des fonctionnalités de prétraitement répertorie tous les filtres disponibles, accompagnés d'une documentation indiquant quand les appliquer.

Veuillez noterAdobe Acrobat, PaddleOCR et Tesseract sont des marques déposées de leurs propriétaires respectifs. Ce site n'est pas affilié, approuvé ou sponsorisé par Adobe Inc., Baidu, Google ou PaddlePaddle. Tous les noms de produits, logos et marques sont la propriété de leurs propriétaires respectifs. Les comparaisons sont à titre informatif uniquement et reflètent les informations publiquement disponibles au moment de l'écriture.

Questions Fréquemment Posées

Pourquoi devrais-je migrer de PaddleSharp OCR vers IronOCR ?

Parmi les motivations communes, citons l'élimination de la complexité de l'interopérabilité COM, le remplacement de la gestion des licences basée sur les fichiers, l'absence de facturation à la page, l'activation du déploiement Docker/conteneur et l'adoption d'un flux de travail NuGet-natif qui s'intègre à l'outillage .NET standard.

Quels sont les principaux changements de code lors de la migration de PaddleSharp OCR vers IronOCR ?

Remplacer les séquences d'initialisation PaddleSharp par l'instanciation IronTesseract, supprimer la gestion du cycle de vie COM (modèles explicites Create/Load/Close) et mettre à jour les noms des propriétés des résultats. Le résultat est une réduction significative du nombre de lignes de code.

Comment installer IronOCR pour commencer la migration ?

Exécutez "Install-Package IronOcr" dans la console du Package Manager ou "dotnet add package IronOcr" dans le CLI. Les packs de langues sont des paquets distincts : 'dotnet add package IronOcr.Languages.French' pour le français, par exemple.

IronOCR offre-t-il la même précision d'OCR que PaddleSharp OCR pour les documents commerciaux standard ?

IronOcr atteint un niveau de précision élevé pour les contenus commerciaux standard, notamment les factures, les contrats, les reçus et les formulaires dactylographiés. Les filtres de prétraitement d'image (désalignement, suppression du bruit, amélioration du contraste) améliorent encore la reconnaissance sur des données dégradées.

Comment IronOCR gère-t-il les données linguistiques que PaddleSharp OCR installe séparément ?

Les données linguistiques de l'IronOcr sont distribuées sous forme de packages NuGet. 'dotnet add package IronOcr.Languages.German' installe la prise en charge de l'allemand. Il n'y a pas de placement manuel de fichiers ou de chemins d'accès aux répertoires.

La migration de PaddleSharp OCR vers IronOCR nécessite-t-elle des changements dans l'infrastructure de déploiement ?

IronOcr nécessite moins de changements d'infrastructure que PaddleSharp OCR. Il n'y a pas de chemin binaire pour le SDK, de placement de fichier de licence ou de configuration de serveur de licence. Le package NuGet contient le moteur d'OCR complet, et la clé de licence est une chaîne de caractères définie dans le code de l'application.

Comment configurer les licences IronOCR après la migration ?

Attribuer IronOcr.License.LicenseKey = "YOUR-KEY" dans le code de démarrage de l'application. Dans Docker ou Kubernetes, stockez la clé dans une variable d'environnement et lisez-la au démarrage. Utilisez License.IsValidLicense pour valider avant d'accepter le trafic.

IronOcr peut-il traiter les PDF de la même manière que PaddleSharp ?

Oui, IronOCR lit aussi bien les PDF natifs que les PDF numérisés. Instanciez IronTesseract, appelez ocr.Read(input) où l'input est un chemin PDF ou OcrPdfInput, et itérez les pages OcrResult. Aucun pipeline de rendu PDF séparé n'est nécessaire.

Comment IronOcr gère-t-il le threading dans le cadre d'un traitement à haut volume ?

IronTesseract est sûr pour l'instanciation par thread. Créez une instance par thread dans un Parallel.ForEach ou un Task pool, exécutez l'OCR simultanément et disposez de chaque instance lorsque vous avez terminé. Aucun état global ou verrouillage n'est nécessaire.

Quels formats de sortie IronOCR prend-il en charge après l'extraction du texte ?

IronOCR renvoie des résultats structurés comprenant le texte, les coordonnées des mots, les scores de confiance et la structure des pages. Les options d'exportation comprennent le texte brut, le PDF interrogeable et les objets de résultats structurés pour le traitement en aval.

La tarification d'IronOCR est-elle plus prévisible que celle de PaddleSharp OCR pour la mise à l'échelle des charges de travail ?

IronOCR utilise des licences perpétuelles forfaitaires sans frais par page ou par volume. Que vous traitiez 10 000 ou 10 millions de pages, le coût de la licence reste constant. Les options de licence en volume et en équipe sont disponibles sur la page de tarification d'IronOcr.

Qu'advient-il de mes tests existants après la migration de PaddleSharp OCR vers IronOCR ?

Les tests qui vérifient le contenu du texte extrait doivent continuer à passer après la migration. Les tests qui valident les modèles d'appel d'API ou le cycle de vie des objets COM devront être mis à jour pour refléter le modèle d'initialisation et de résultat plus simple d'IronOcr.

Kannaopat Udonpant
Ingénieur logiciel
Avant de devenir ingénieur logiciel, Kannapat a obtenu un doctorat en ressources environnementales à l'université d'Hokkaido au Japon. Pendant qu'il poursuivait son diplôme, Kannapat est également devenu membre du laboratoire de robotique de véhicules, qui fait partie du département de bioproduction. En 2022, il a utilisé ses compé...
Lire la suite

Équipe de soutien Iron

Nous sommes en ligne 24 heures sur 24, 5 jours sur 7.
Chat
Email
Appelez-moi