Hautes Utilisations de Mémoire lors de l'OCR en Bloc

This article was translated from English: Does it need improvement?
Translated
View the article in English

Exécuter l'OCR sur plusieurs segments PDF à la fois multiplie l'utilisation de la mémoire. Chaque tâche rend des bitmaps pleine-page via un OcrInput, et un moteur IronTesseract frais par segment recharge les fichiers modèle de langue à chaque fois. À pleine concurrence de processeur, cela pousse le pic de mémoire dans la gamme des multi-GB, avec des pointes qui échouent dans des environnements à mémoire limitée.

L'OCR est naturellement gourmand en mémoire. Tout OcrInput rend des bitmaps pleine-page, et tout moteur IronTesseract charge les fichiers modèle de langue en mémoire. Créer un nouveau moteur par segment recharge ces modèles de manière répétée, et exécuter une tâche OCR par cœur de CPU (Environment.ProcessorCount) permet à de nombreux travaux lourds en bitmap de s'exécuter côte à côte. Rien ne limitant le nombre de tâches actives, le pic de mémoire évolue directement avec la concurrence.

La solution consiste à limiter le nombre de tâches en cours : restreindre la concurrence, réutiliser les moteurs à partir d'un pool, et réguler le travail avec un sémaphore.

Solution

1. Limiter la concurrence OCR

Limitez le nombre de tâches OCR simultanées à un petit plafond. Moins de tâches simultanées signifient moins de bitmaps pleine-page en mémoire à la fois, ce qui réduit directement le pic. Ajustez le plafond à la capacité de la machine.

// Clamp concurrency to avoid memory saturation and CPU over-subscription.
int concurrency = Math.Clamp(Environment.ProcessorCount / 2, 1, 4);
// Clamp concurrency to avoid memory saturation and CPU over-subscription.
int concurrency = Math.Clamp(Environment.ProcessorCount / 2, 1, 4);
Imports System

' Clamp concurrency to avoid memory saturation and CPU over-subscription.
Dim concurrency As Integer = Math.Clamp(Environment.ProcessorCount \ 2, 1, 4)
$vbLabelText   $csharpLabel

2. Regrouper les moteurs

Créez exactement un moteur IronTesseract par créneau concurrent lors du démarrage et réutilisez-les à travers chaque segment, plutôt que de construire un nouveau moteur et de recharger le modèle de langue à chaque fois.

// Pre-create one engine per concurrent slot and reuse them across segments.
var enginePool = new ConcurrentBag<IronTesseract>(
    Enumerable.Range(0, concurrency).Select(_ => new IronTesseract())
);
// Pre-create one engine per concurrent slot and reuse them across segments.
var enginePool = new ConcurrentBag<IronTesseract>(
    Enumerable.Range(0, concurrency).Select(_ => new IronTesseract())
);
' Pre-create one engine per concurrent slot and reuse them across segments.
Dim enginePool As New ConcurrentBag(Of IronTesseract)(
    Enumerable.Range(0, concurrency).Select(Function(_) New IronTesseract())
)
$vbLabelText   $csharpLabel

Construire le pool une seule fois amortit le coût de chargement du modèle de langue sur l'ensemble de l'exécution au lieu de le payer par segment.

3. Réguler le travail avec un sémaphore

Initialisez un SemaphoreSlim à la limite de concurrence et enveloppez-le dans using. Chaque tâche appelle WaitAsync() avant de commencer et Release() dans un finally, ainsi seul le nombre autorisé de segments est en vol à la fois.

using var semaphore = new SemaphoreSlim(concurrency);
await semaphore.WaitAsync();
try
{
    // Rent a pre-loaded engine from the pool.
    if (!enginePool.TryTake(out var ocr))
        ocr = new IronTesseract(); // Defensive fallback; should never be reached.
    try
    {
        using var input = new OcrInput();
        input.LoadPdf(segmentStream); // page-range segment produced upstream
        var ocrResult = await ocr.ReadAsync(input);
        ocrResult.SaveAsSearchablePdf(outputPath);
    }
    finally
    {
        enginePool.Add(ocr); // Return engine to pool for the next waiting segment.
    }
}
finally
{
    semaphore.Release();
}
using var semaphore = new SemaphoreSlim(concurrency);
await semaphore.WaitAsync();
try
{
    // Rent a pre-loaded engine from the pool.
    if (!enginePool.TryTake(out var ocr))
        ocr = new IronTesseract(); // Defensive fallback; should never be reached.
    try
    {
        using var input = new OcrInput();
        input.LoadPdf(segmentStream); // page-range segment produced upstream
        var ocrResult = await ocr.ReadAsync(input);
        ocrResult.SaveAsSearchablePdf(outputPath);
    }
    finally
    {
        enginePool.Add(ocr); // Return engine to pool for the next waiting segment.
    }
}
finally
{
    semaphore.Release();
}
Imports System.Threading
Imports IronOcr

Dim semaphore As New SemaphoreSlim(concurrency)
Await semaphore.WaitAsync()
Try
    ' Rent a pre-loaded engine from the pool.
    Dim ocr As IronTesseract = Nothing
    If Not enginePool.TryTake(ocr) Then
        ocr = New IronTesseract() ' Defensive fallback; should never be reached.
    End If
    Try
        Using input As New OcrInput()
            input.LoadPdf(segmentStream) ' page-range segment produced upstream
            Dim ocrResult = Await ocr.ReadAsync(input)
            ocrResult.SaveAsSearchablePdf(outputPath)
        End Using
    Finally
        enginePool.Add(ocr) ' Return engine to pool for the next waiting segment.
    End Try
Finally
    semaphore.Release()
End Try
$vbLabelText   $csharpLabel

L'appel WaitAsync() bloque jusqu'à ce qu'un créneau se libère, et retourner le moteur dans le finally intérieur transmet un moteur préchargé directement au segment suivant en attente.

4. Dispose OcrInput per segment

Enveloppez chaque OcrInput dans un using afin que ses bitmaps page rendus soient libérés dès que le segment est lu, avant que la tâche suivante ne revendique le créneau.

ConseilsLe using sur OcrInput est ce qui empêche la mémoire bitmap de s'accumuler à travers les segments; sans cela, les créneaux libérés conservent encore leurs bitmaps de page.

Curtis Chau
Rédacteur technique

Curtis Chau détient un baccalauréat en informatique (Université de Carleton) et se spécialise dans le développement front-end avec expertise en Node.js, TypeScript, JavaScript et React. Passionné par la création d'interfaces utilisateur intuitives et esthétiquement plaisantes, Curtis aime travailler avec des frameworks modernes ...

Lire la suite
Prêt à commencer?
Nuget Téléchargements 6,136,090 | Version : 2026.7 vient de sortir
Still Scrolling Icon

Vous faites encore défiler ?

Vous voulez une preuve rapidement ? PM > Install-Package IronOcr
lancez un échantillon regardez votre image se transformer en texte consultable.