Przejdź do treści stopki
FILMY

Migracja z Charlesw Tesseract do IronOCR

Ten przewodnik wprowadza deweloperów .NET w proces migracji z pakietu NuGet charlesw/tesseract (Tesseract) do IronOCR. Migracja skupia się na jednym konkretnym problemie: modelu wdrażania natywnych binariów, który narzuca wraper charlesw i kodzie warunkowym platformy, który zmusza deweloperów do tworzenia. Zespoły, które miały trudności z DllNotFoundException w CI, zmagały się z Leptonica library paths na Linuxie, lub pisały bloki wykrywania systemu operacyjnego nie mające nic wspólnego z OCR, znajdą w tym przewodniku dokładne informacje o tym, co znika po zmianie.

Dlaczego warto przejść z Charlesw na Tesseract

Archiwizowany pakiet charlesw/tesseract powoduje problemy w nowych projektach nie ze względu na zły API, ale ponieważ wymaga modelu wdrażania, który został zaprojektowany na podstawie założeń nieaktualnych w nowoczesnej infrastrukturze .NET. Oto, co decyduje o wyborze migracji:

Natywne wdrażanie binarne na platformę. Pakiet NuGet Tesseract dostarcza platformowo-specyficzne natywne binaria: tesseract50.dll dla Windows x64, osobny build dla x86, libtesseract.so dla Linux x64. Te binaria muszą się znaleźć w poprawnej lokalizacji podczas działania, aby wywołania P/Invoke zakończyły się sukcesem. Na stacji roboczej programisty SDK kopiuje je automatycznie. W kontenerze Docker, agencie kompilacji ARM64 lub usłudze Azure App Service z niestandardowym katalogiem głównym aplikacji nie ma to miejsca. Każde nowe wdrożenie staje się sesją debugowania.

Leptonica jako ukryta zależność. Ładowanie obrazów w Tesseract jest obsługiwane przez bibliotekę Leptonica, która jest dostarczana jako osobny zestaw natywnych bibliotek DLL wraz z plikami binarnymi Tesseract. Na Windows leptonica-1.82.0.dll musi się znajdować w katalogu wyjściowym. W systemie Linux biblioteka współdzielona Leptonica musi być dołączona do pakietu lub zainstalowana jako pakiet systemowy. Obrazy Docker oparte na Debianie bez libleptonica-dev zawodzą przy Pix.LoadFromFile() z niewyjaśnionym wyjątkiem natywnym, a rozwiązanie tego wymaga znajomości systemowego pakietu rozwiązującego zależność.

Kod warunkowy platformy w logice aplikacji. Połączenie ładowania natywnego binariów i rozwiązywania ścieżki tessdata zmusza deweloperów do pisania sprawdzeń RuntimeInformation.IsOSPlatform(), wykrywania zmiennych środowiskowych w kontekstach kontenerowych i logiki budowania ścieżek zmieniającej się w zależności od celu. Żaden z tych kodów nie jest logiką OCR. Jest to infrastruktura wdrożeniowa, która istnieje wyłącznie dlatego, że zarządzanie plikami binarnymi pakietu jest niekompletne.

Zarchiwizowany pakiet bez ścieżki naprawy. Repozytorium zostało zarchiwizowane w 2021 roku. Gdy aktualizacja pakietu systemowego na hoście Linux zmienia Leptonica ABI lub gdy nowe środowisko uruchomieniowe .NET zmienia zachowanie ładowania natywnych plików binarnych, nie ma wersji, do której można by dokonać aktualizacji. Jedynymi opcjami są rozwidlenie natywnego potoku kompilacji lub wymiana biblioteki.

Tesseract 4.1.1 Engine Freeze. Pakiet zawiera Tesseract 4.1.1. Przepisany model LSTM w Tesseract 5 zapewnia znacznie wyższą dokładność w przypadku dokumentów o niskiej jakości. Ta aktualizacja nie jest dostępna w pakiecie charlesw — wymaga zmiany bibliotek.

Zarządzanie poziomem zaufania bez standardowego wzoru. Wraper charlesw wystawia page.GetMeanConfidence() jako liczbę zmiennoprzecinkową między 0 a 1, ale zastosowanie progów zaufania na poziomie słowa lub znaku wymaga wzorca iteratora z iter.GetConfidence(PageIteratorLevel.Word). Nie ma standardowego interfejsu API do filtrowania; każdy zespół wdraża własną logikę progową w inny sposób.

Podstawowy problem

Oprogramowanie Charlesw wymaga konfiguracji natywnego pliku binarnego specyficznego dla platformy, zanim będzie można uruchomić OCR:

// charlesw Tesseract: OS detection required just to find native DLLs
// DllNotFoundException on any platform where binaries do not resolve
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
    Environment.SetEnvironmentVariable("LD_LIBRARY_PATH", "/app/lib");
}
var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default);
using var img = Pix.LoadFromFile(imagePath); // Requires leptonica native DLL
using var page = engine.Process(img);
return page.GetText();
// charlesw Tesseract: OS detection required just to find native DLLs
// DllNotFoundException on any platform where binaries do not resolve
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
    Environment.SetEnvironmentVariable("LD_LIBRARY_PATH", "/app/lib");
}
var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default);
using var img = Pix.LoadFromFile(imagePath); // Requires leptonica native DLL
using var page = engine.Process(img);
return page.GetText();
Imports System
Imports System.Runtime.InteropServices
Imports Tesseract

' charlesw Tesseract: OS detection required just to find native DLLs
' DllNotFoundException on any platform where binaries do not resolve
If RuntimeInformation.IsOSPlatform(OSPlatform.Linux) Then
    Environment.SetEnvironmentVariable("LD_LIBRARY_PATH", "/app/lib")
End If

Dim engine As New TesseractEngine("./tessdata", "eng", EngineMode.Default)
Using img As Pix = Pix.LoadFromFile(imagePath) ' Requires leptonica native DLL
    Using page As Page = engine.Process(img)
        Return page.GetText()
    End Using
End Using
$vbLabelText   $csharpLabel

IronOCR nie posiada natywnej konfiguracji binarnej:

// IronOCR: no path management, no OS detection, no leptonica dependency
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var text = new IronTesseract().Read(imagePath).Text;
// IronOCR: no path management, no OS detection, no leptonica dependency
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var text = new IronTesseract().Read(imagePath).Text;
' IronOCR: no path management, no OS detection, no leptonica dependency
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim text As String = New IronTesseract().Read(imagePath).Text
$vbLabelText   $csharpLabel

IronOCR a Charlesw Tesseract: porównanie funkcji

Poniższa tabela przedstawia funkcje istotne dla zespołów oceniających tę migrację:

Funkcja Charlesw Tesseract IronOCR
Status konserwacji Archiwizowane (brak aktualizacji od 2021 r.) Aktywnie utrzymywane
Wersja silnika Tesseract 4.1.1 (zamrożone) 5 (aktualne, zoptymalizowane)
Licencja Apache 2.0 (bezpłatna) Komercyjna ($999–$2,999 wieczysta)
Instalacja NuGet Tesseract IronOcr
Natywne zarządzanie plikami binarnymi Ręczne wdrażanie bibliotek DLL na poszczególnych platformach W pakiecie, bez konfiguracji
Zależność od Leptonica Wymaga leptonica-1.82.0.dll / libleptonica-dev Nie dotyczy (obsługiwane wewnętrznie)
Zarządzanie danymi Tessdata Ręczne pobieranie i .csproj kopiowanie wpisów Pakiety językowe NuGet
Kod zależny od platformy Wymagane do wdrożenia w wielu językach Nie jest wymagane
Wdrożenie Docker Wymaga jawnego tessdata COPY + Leptonica apt-get Tylko standardowe wymagania dotyczące kontenerów .NET Standard
Obsługa ARM64 Niepotwierdzony wpis w archiwum W pakiecie, zweryfikowane
Formaty obrazów TIFF, PNG, BMP, JPG (za pośrednictwem Leptonica) JPG, PNG, BMP, TIFF, GIF i inne
Wielostronicowy plik TIFF Ręczna iteracja ramki input.LoadImageFrames()
Natywne wprowadzanie plików PDF Nie (wymaga biblioteki dodatkowej) Tak
Wynik w formacie PDF z możliwością wyszukiwania Nie Tak (result.SaveAsSearchablePdf())
Wbudowane przetwarzanie wstępne None Wyrównanie, Usuwanie szumów, Kontrast, Binaryzacja, Wyostrzanie, Skalowanie, Rozszerzanie, Erodowanie, Odwracanie
API filtrowania pewności Ręczny iterator z GetConfidence() result.Confidence, word.Confidence
Wyniki uporządkowane Wzorzec iteratora (ResultIterator) Bezpośrednie fragmenty (strony, akapity, wiersze, słowa)
Odczytywanie BarCode Nie Tak (podczas przetwarzania OCR)
OCR oparte na regionie Nie Tak (CropRectangle)
Bezpieczeństwo wątków Odpowiedzialność dzwoniącego Wbudowane
Ponad 125 pakietów językowych Ręczne pobieranie plików tessdata dotnet add package IronOcr.Languages.*
Wielopłatformowy .NET Tak (.NET Standard 2.0) Tak (.NET Framework 4.6.2+, .NET 5/6/7/8/9)
Częstotliwość wydawania poprawek bezpieczeństwa Brak (archiwum) Regularne wydania

Szybki start: Migracja zCharlesw Tesseractdo IronOCR

Krok 1: Zastąp pakiet NuGet

Usuń pakiet charlesw Tesseract:

dotnet remove package Tesseract
dotnet remove package Tesseract
SHELL

Zainstaluj IronOCR z NuGet:

dotnet add package IronOcr

Krok 2: Aktualizacja przestrzeni nazw

// Before (charlesw Tesseract)
using Tesseract;

// After (IronOCR)
using IronOcr;
// Before (charlesw Tesseract)
using Tesseract;

// After (IronOCR)
using IronOcr;
Imports IronOcr
$vbLabelText   $csharpLabel

Krok 3: Inicjalizacja licencji

Dodaj to wywołanie raz podczas uruchamiania aplikacji, przed wykonaniem jakichkolwiek operacji OCR:

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

Bezpłatna licencja próbna jest dostępna na stronie licencyjnej IronOCR. Wersja próbna usuwa znak wodny z wyników i zapewnia pełny dostęp do API.

Przykłady migracji kodu

Usuwanie konfiguracji natywnej ścieżki plików binarnych

Najczęstszym wzorcem inicjalizacji w projektach charlesw/tesseract jest klasa fabryczna lub pomocnicza, która tworzy ścieżkę tessdata i konfiguruje ładowanie bibliotek natywnych dla danego środowiska. Ten kod istnieje wyłącznie ze względu na model wdrażania opakowania.

Podejście Charlesw Tesseract:

// A realistic factory found in production charlesw/Tesseract projects
public static class OcrEngineFactory
{
    private static string GetTessDataPath()
    {
        // Different path per environment — all wrong until explicitly configured
        if (Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER") == "true")
            return "/app/tessdata";                                    // Docker
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            return Path.Combine(AppContext.BaseDirectory, "tessdata"); // Linux bare metal
        if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
            return "/usr/local/share/tessdata";                        // macOS Homebrew install
        return @".\tessdata";                                          // Windows dev machine
    }

    public static TesseractEngine Create(string language = "eng")
    {
        // If leptonica-1.82.0.dll is not in output directory: DllNotFoundException at this line
        // If tessdata folder is missing: TesseractException at engine construction
        return new TesseractEngine(GetTessDataPath(), language, EngineMode.Default);
    }
}

// Call site
using var engine = OcrEngineFactory.Create();
using var img = Pix.LoadFromFile("invoice.jpg");
using var page = engine.Process(img);
Console.WriteLine(page.GetText());
// A realistic factory found in production charlesw/Tesseract projects
public static class OcrEngineFactory
{
    private static string GetTessDataPath()
    {
        // Different path per environment — all wrong until explicitly configured
        if (Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER") == "true")
            return "/app/tessdata";                                    // Docker
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            return Path.Combine(AppContext.BaseDirectory, "tessdata"); // Linux bare metal
        if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
            return "/usr/local/share/tessdata";                        // macOS Homebrew install
        return @".\tessdata";                                          // Windows dev machine
    }

    public static TesseractEngine Create(string language = "eng")
    {
        // If leptonica-1.82.0.dll is not in output directory: DllNotFoundException at this line
        // If tessdata folder is missing: TesseractException at engine construction
        return new TesseractEngine(GetTessDataPath(), language, EngineMode.Default);
    }
}

// Call site
using var engine = OcrEngineFactory.Create();
using var img = Pix.LoadFromFile("invoice.jpg");
using var page = engine.Process(img);
Console.WriteLine(page.GetText());
Imports System
Imports System.IO
Imports System.Runtime.InteropServices
Imports Tesseract

Public Module OcrEngineFactory
    Private Function GetTessDataPath() As String
        ' Different path per environment — all wrong until explicitly configured
        If Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER") = "true" Then
            Return "/app/tessdata"                                    ' Docker
        End If
        If RuntimeInformation.IsOSPlatform(OSPlatform.Linux) Then
            Return Path.Combine(AppContext.BaseDirectory, "tessdata") ' Linux bare metal
        End If
        If RuntimeInformation.IsOSPlatform(OSPlatform.OSX) Then
            Return "/usr/local/share/tessdata"                        ' macOS Homebrew install
        End If
        Return ".\tessdata"                                           ' Windows dev machine
    End Function

    Public Function Create(Optional language As String = "eng") As TesseractEngine
        ' If leptonica-1.82.0.dll is not in output directory: DllNotFoundException at this line
        ' If tessdata folder is missing: TesseractException at engine construction
        Return New TesseractEngine(GetTessDataPath(), language, EngineMode.Default)
    End Function
End Module

' Call site
Using engine As TesseractEngine = OcrEngineFactory.Create()
    Using img As Pix = Pix.LoadFromFile("invoice.jpg")
        Using page As Page = engine.Process(img)
            Console.WriteLine(page.GetText())
        End Using
    End Using
End Using
$vbLabelText   $csharpLabel

Podejście IronOCR:

// IronOCR: no factory, no path logic, no OS detection
// Runs identically on Windows, Linux, macOS, and ARM64
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";

var result = new IronTesseract().Read("invoice.jpg");
Console.WriteLine(result.Text);
// IronOCR: no factory, no path logic, no OS detection
// Runs identically on Windows, Linux, macOS, and ARM64
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";

var result = new IronTesseract().Read("invoice.jpg");
Console.WriteLine(result.Text);
' IronOCR: no factory, no path logic, no OS detection
' Runs identically on Windows, Linux, macOS, and ARM64
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"

Dim result = New IronTesseract().Read("invoice.jpg")
Console.WriteLine(result.Text)
$vbLabelText   $csharpLabel

Cała klasa OcrEngineFactory usuwa się. Logiczne ścieżki warunkowe platformy, kontrola DOTNET_RUNNING_IN_CONTAINER oraz zależność od Leptonica DLL znikają wraz z nią. Każde środowisko — stacja robocza programisty, agent CI, kontener Docker, maszyna wirtualna w chmurze — wykonuje te same dwie linijki kodu. Podręcznik konfiguracji IronTesseract opisuje opcje konfiguracyjne, które należy dostosować w przypadku konieczności zmiany ustawień domyślnych, jednak w większości przypadków nie są one wymagane.

Zastąpienie konwersji obrazu Leptonica

Wraper charlesw używa typu Pix Leptonica jako reprezentacji obrazu. Każdy kod, który zmienia obrazy przed OCR, musi przeprowadzić konwersję przez Pix, co wymaga załadowania i działania natywnego DLL Leptonica. Zastąpienie tego wzorca OcrInput całkowicie eliminuje zależność od Leptonica.

Podejście Charlesw Tesseract:

// Pix is Leptonica's image type — requires leptonica native DLL
// Converting from System.Drawing.Bitmap requires a temp file round-trip
public string ProcessInMemoryImage(Bitmap bitmap)
{
    // Nie direct Bitmap → Pix conversion; must write to temp file
    var tempPath = Path.Combine(Path.GetTempPath(), $"ocr_{Guid.NewGuid()}.png");
    try
    {
        bitmap.Save(tempPath, System.Drawing.Imaging.ImageFormat.Png);

        using var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default);
        using var pix = Pix.LoadFromFile(tempPath);   // Leptonica file I/O
        using var page = engine.Process(pix);

        return page.GetText();
    }
    finally
    {
        if (File.Exists(tempPath)) File.Delete(tempPath);
    }
}
// Pix is Leptonica's image type — requires leptonica native DLL
// Converting from System.Drawing.Bitmap requires a temp file round-trip
public string ProcessInMemoryImage(Bitmap bitmap)
{
    // Nie direct Bitmap → Pix conversion; must write to temp file
    var tempPath = Path.Combine(Path.GetTempPath(), $"ocr_{Guid.NewGuid()}.png");
    try
    {
        bitmap.Save(tempPath, System.Drawing.Imaging.ImageFormat.Png);

        using var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default);
        using var pix = Pix.LoadFromFile(tempPath);   // Leptonica file I/O
        using var page = engine.Process(pix);

        return page.GetText();
    }
    finally
    {
        if (File.Exists(tempPath)) File.Delete(tempPath);
    }
}
Imports System
Imports System.Drawing
Imports System.IO
Imports Tesseract

Public Class ImageProcessor
    ' Pix is Leptonica's image type — requires leptonica native DLL
    ' Converting from System.Drawing.Bitmap requires a temp file round-trip
    Public Function ProcessInMemoryImage(bitmap As Bitmap) As String
        ' Nie direct Bitmap → Pix conversion; must write to temp file
        Dim tempPath As String = Path.Combine(Path.GetTempPath(), $"ocr_{Guid.NewGuid()}.png")
        Try
            bitmap.Save(tempPath, System.Drawing.Imaging.ImageFormat.Png)

            Using engine As New TesseractEngine("./tessdata", "eng", EngineMode.Default)
                Using pix As Pix = Pix.LoadFromFile(tempPath) ' Leptonica file I/O
                    Using page As Page = engine.Process(pix)
                        Return page.GetText()
                    End Using
                End Using
            End Using
        Finally
            If File.Exists(tempPath) Then File.Delete(tempPath)
        End Try
    End Function
End Class
$vbLabelText   $csharpLabel

Podejście IronOCR:

// OcrInput accepts byte arrays and streams — no temp file, no Leptonica
public string ProcessInMemoryImage(byte[] imageBytes)
{
    using var input = new OcrInput();
    input.LoadImage(imageBytes);   // Direct byte array loading

    var result = new IronTesseract().Read(input);
    return result.Text;
}

// Or from a stream — same pattern
public string ProcessFromStream(Stream imageStream)
{
    using var input = new OcrInput();
    input.LoadImage(imageStream);

    var result = new IronTesseract().Read(input);
    return result.Text;
}
// OcrInput accepts byte arrays and streams — no temp file, no Leptonica
public string ProcessInMemoryImage(byte[] imageBytes)
{
    using var input = new OcrInput();
    input.LoadImage(imageBytes);   // Direct byte array loading

    var result = new IronTesseract().Read(input);
    return result.Text;
}

// Or from a stream — same pattern
public string ProcessFromStream(Stream imageStream)
{
    using var input = new OcrInput();
    input.LoadImage(imageStream);

    var result = new IronTesseract().Read(input);
    return result.Text;
}
Imports System.IO

' OcrInput accepts byte arrays and streams — no temp file, no Leptonica
Public Function ProcessInMemoryImage(imageBytes As Byte()) As String
    Using input As New OcrInput()
        input.LoadImage(imageBytes)   ' Direct byte array loading

        Dim result = New IronTesseract().Read(input)
        Return result.Text
    End Using
End Function

' Or from a stream — same pattern
Public Function ProcessFromStream(imageStream As Stream) As String
    Using input As New OcrInput()
        input.LoadImage(imageStream)

        Dim result = New IronTesseract().Read(input)
        Return result.Text
    End Using
End Function
$vbLabelText   $csharpLabel

Znikają operacje związane z plikami tymczasowymi. Żaden plik nie jest zapisywany na dysku, żaden DLL Leptonica nie jest wywoływany do konwersji i nie ma bloku finally do czyszczenia. Przewodnik po wczytywaniu obrazów image input guide i przewodnik po strumieniach stream input guide dokumentują wszystkie obsługiwane źródła wejścia, w tym ładowanie z adresów URL i plików mapowanych w pamięci.

Filtrowanie progu pewności

Wraper charlesw wystawia poziomy zaufania na dwóch poziomach: page.GetMeanConfidence() dla całej strony i iter.GetConfidence(PageIteratorLevel.Word) dla poszczególnych słów. Odfiltrowanie słów o niskim poziomie pewności z wyniku wymaga ręcznego zarządzania pętlą iteracyjną.IronOCR udostępnia poziom pewności bezpośrednio w obiektach wynikowych, dzięki czemu logika progowa staje się wyrażeniem LINQ.

Podejście Charlesw Tesseract:

// Word-level confidence filtering requires iterator boilerplate
public List<string> ExtractHighConfidenceWords(string imagePath, float minConfidence = 0.8f)
{
    var highConfidenceWords = new List<string>();

    using var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default);
    using var img = Pix.LoadFromFile(imagePath);
    using var page = engine.Process(img);

    // Page-level confidence only: fine-grained requires the iterator
    Console.WriteLine($"Page confidence: {page.GetMeanConfidence():P1}");

    using var iter = page.GetIterator();
    iter.Begin();
    do
    {
        if (iter.IsAtBeginningOf(PageIteratorLevel.Word))
        {
            var wordText = iter.GetText(PageIteratorLevel.Word)?.Trim();
            var wordConf  = iter.GetConfidence(PageIteratorLevel.Word) / 100f; // Returns 0-100
            if (!string.IsNullOrEmpty(wordText) && wordConf >= minConfidence)
                highConfidenceWords.Add(wordText);
        }
    } while (iter.Next(PageIteratorLevel.Para, PageIteratorLevel.Word));

    return highConfidenceWords;
}
// Word-level confidence filtering requires iterator boilerplate
public List<string> ExtractHighConfidenceWords(string imagePath, float minConfidence = 0.8f)
{
    var highConfidenceWords = new List<string>();

    using var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default);
    using var img = Pix.LoadFromFile(imagePath);
    using var page = engine.Process(img);

    // Page-level confidence only: fine-grained requires the iterator
    Console.WriteLine($"Page confidence: {page.GetMeanConfidence():P1}");

    using var iter = page.GetIterator();
    iter.Begin();
    do
    {
        if (iter.IsAtBeginningOf(PageIteratorLevel.Word))
        {
            var wordText = iter.GetText(PageIteratorLevel.Word)?.Trim();
            var wordConf  = iter.GetConfidence(PageIteratorLevel.Word) / 100f; // Returns 0-100
            if (!string.IsNullOrEmpty(wordText) && wordConf >= minConfidence)
                highConfidenceWords.Add(wordText);
        }
    } while (iter.Next(PageIteratorLevel.Para, PageIteratorLevel.Word));

    return highConfidenceWords;
}
Imports System
Imports Tesseract

Public Class WordExtractor
    Public Function ExtractHighConfidenceWords(imagePath As String, Optional minConfidence As Single = 0.8F) As List(Of String)
        Dim highConfidenceWords As New List(Of String)()

        Using engine As New TesseractEngine("./tessdata", "eng", EngineMode.Default)
            Using img As Pix = Pix.LoadFromFile(imagePath)
                Using page As Page = engine.Process(img)
                    ' Page-level confidence only: fine-grained requires the iterator
                    Console.WriteLine($"Page confidence: {page.GetMeanConfidence():P1}")

                    Using iter As ResultIterator = page.GetIterator()
                        iter.Begin()
                        Do
                            If iter.IsAtBeginningOf(PageIteratorLevel.Word) Then
                                Dim wordText As String = iter.GetText(PageIteratorLevel.Word)?.Trim()
                                Dim wordConf As Single = iter.GetConfidence(PageIteratorLevel.Word) / 100.0F ' Returns 0-100
                                If Not String.IsNullOrEmpty(wordText) AndAlso wordConf >= minConfidence Then
                                    highConfidenceWords.Add(wordText)
                                End If
                            End If
                        Loop While iter.Next(PageIteratorLevel.Para, PageIteratorLevel.Word)
                    End Using
                End Using
            End Using
        End Using

        Return highConfidenceWords
    End Function
End Class
$vbLabelText   $csharpLabel

Podejście IronOCR:

// Confidence is a property on each result object — no iterator required
public List<string> ExtractHighConfidenceWords(string imagePath, double minConfidence = 80.0)
{
    var result = new IronTesseract().Read(imagePath);

    Console.WriteLine($"Page confidence: {result.Confidence}%");

    // LINQ directly on the word collection — no iterator state management
    return result.Pages
        .SelectMany(p => p.Lines)
        .SelectMany(l => l.Words)
        .Where(w => w.Confidence >= minConfidence && !string.IsNullOrWhiteSpace(w.Text))
        .Select(w => w.Text)
        .ToList();
}
// Confidence is a property on each result object — no iterator required
public List<string> ExtractHighConfidenceWords(string imagePath, double minConfidence = 80.0)
{
    var result = new IronTesseract().Read(imagePath);

    Console.WriteLine($"Page confidence: {result.Confidence}%");

    // LINQ directly on the word collection — no iterator state management
    return result.Pages
        .SelectMany(p => p.Lines)
        .SelectMany(l => l.Words)
        .Where(w => w.Confidence >= minConfidence && !string.IsNullOrWhiteSpace(w.Text))
        .Select(w => w.Text)
        .ToList();
}
Imports System
Imports System.Collections.Generic
Imports System.Linq

Public Function ExtractHighConfidenceWords(imagePath As String, Optional minConfidence As Double = 80.0) As List(Of String)
    Dim result = New IronTesseract().Read(imagePath)

    Console.WriteLine($"Page confidence: {result.Confidence}%")

    ' LINQ directly on the word collection — no iterator state management
    Return result.Pages _
        .SelectMany(Function(p) p.Lines) _
        .SelectMany(Function(l) l.Words) _
        .Where(Function(w) w.Confidence >= minConfidence AndAlso Not String.IsNullOrWhiteSpace(w.Text)) _
        .Select(Function(w) w.Text) _
        .ToList()
End Function
$vbLabelText   $csharpLabel

Maszyna stanów iteratora zniknęła. Wartości pewności w IronOCRsą konsekwentnie podawane w skali od 0 do 100, nie jest wymagane dzielenie przez 100. Przewodnik po wskaźnikach pewności obejmuje wzorce pewności w odniesieniu do poszczególnych słów, wierszy i stron. Przewodnik po wynikach wyszukiwania pokazuje, jak poruszać się po pełnej hierarchii wyników.

Przetwarzanie wielostronicowych plików TIFF w trybie wsadowym

Pliki TIFF zawierające wiele klatek są powszechne w procesach skanowania dokumentów. Oprogramowanie CharlesWrapper nie posiada wbudowanej obsługi wieloklatkowych plików TIFF; Każda klatka musi zostać wyodrębniona ręcznie przed przetworzeniem.IronOCR obsługuje natywnie wielo-ramkowe pliki TIFF za pomocą jednego wywołania ładowania.

Podejście Charlesw Tesseract:

// charlesw/Tesseract has no multi-frame TIFF support
// Each frame must be extracted via System.Drawing before OCR can run
public string ProcessMultiFrameTiff(string tiffPath)
{
    var fullText = new StringBuilder();

    using var tiffImage = Image.FromFile(tiffPath);
    var frameCount = tiffImage.GetFrameCount(FrameDimension.Page);

    using var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default);

    for (int i = 0; i < frameCount; i++)
    {
        tiffImage.SelectActiveFrame(FrameDimension.Page, i);

        // Must save each frame as a temp file for Pix to load
        var tempPath = Path.Combine(Path.GetTempPath(), $"tiff_frame_{i}.png");
        try
        {
            tiffImage.Save(tempPath, System.Drawing.Imaging.ImageFormat.Png);

            using var pix  = Pix.LoadFromFile(tempPath);
            using var page = engine.Process(pix);
            fullText.AppendLine(page.GetText());
        }
        finally
        {
            if (File.Exists(tempPath)) File.Delete(tempPath);
        }
    }

    return fullText.ToString();
}
// charlesw/Tesseract has no multi-frame TIFF support
// Each frame must be extracted via System.Drawing before OCR can run
public string ProcessMultiFrameTiff(string tiffPath)
{
    var fullText = new StringBuilder();

    using var tiffImage = Image.FromFile(tiffPath);
    var frameCount = tiffImage.GetFrameCount(FrameDimension.Page);

    using var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default);

    for (int i = 0; i < frameCount; i++)
    {
        tiffImage.SelectActiveFrame(FrameDimension.Page, i);

        // Must save each frame as a temp file for Pix to load
        var tempPath = Path.Combine(Path.GetTempPath(), $"tiff_frame_{i}.png");
        try
        {
            tiffImage.Save(tempPath, System.Drawing.Imaging.ImageFormat.Png);

            using var pix  = Pix.LoadFromFile(tempPath);
            using var page = engine.Process(pix);
            fullText.AppendLine(page.GetText());
        }
        finally
        {
            if (File.Exists(tempPath)) File.Delete(tempPath);
        }
    }

    return fullText.ToString();
}
Imports System.Drawing
Imports System.Drawing.Imaging
Imports Tesseract
Imports System.IO
Imports System.Text

Public Function ProcessMultiFrameTiff(tiffPath As String) As String
    Dim fullText As New StringBuilder()

    Using tiffImage As Image = Image.FromFile(tiffPath)
        Dim frameCount As Integer = tiffImage.GetFrameCount(FrameDimension.Page)

        Using engine As New TesseractEngine("./tessdata", "eng", EngineMode.Default)
            For i As Integer = 0 To frameCount - 1
                tiffImage.SelectActiveFrame(FrameDimension.Page, i)

                ' Must save each frame as a temp file for Pix to load
                Dim tempPath As String = Path.Combine(Path.GetTempPath(), $"tiff_frame_{i}.png")
                Try
                    tiffImage.Save(tempPath, Imaging.ImageFormat.Png)

                    Using pix As Pix = Pix.LoadFromFile(tempPath)
                        Using page As Page = engine.Process(pix)
                            fullText.AppendLine(page.GetText())
                        End Using
                    End Using
                Finally
                    If File.Exists(tempPath) Then File.Delete(tempPath)
                End Try
            Next
        End Using
    End Using

    Return fullText.ToString()
End Function
$vbLabelText   $csharpLabel

Podejście IronOCR:

// LoadImageFrames handles multi-frame TIFFs natively — no frame extraction loop
public string ProcessMultiFrameTiff(string tiffPath)
{
    using var input = new OcrInput();
    input.LoadImageFrames(tiffPath);   // All frames loaded in one call

    var result = new IronTesseract().Read(input);

    // Pages maps directly to TIFF frames
    foreach (var page in result.Pages)
        Console.WriteLine($"Frame {page.PageNumber}: {page.Words.Count()} words");

    return result.Text;
}
// LoadImageFrames handles multi-frame TIFFs natively — no frame extraction loop
public string ProcessMultiFrameTiff(string tiffPath)
{
    using var input = new OcrInput();
    input.LoadImageFrames(tiffPath);   // All frames loaded in one call

    var result = new IronTesseract().Read(input);

    // Pages maps directly to TIFF frames
    foreach (var page in result.Pages)
        Console.WriteLine($"Frame {page.PageNumber}: {page.Words.Count()} words");

    return result.Text;
}
Imports System
Imports IronOcr

Public Class TiffProcessor
    ' LoadImageFrames handles multi-frame TIFFs natively — no frame extraction loop
    Public Function ProcessMultiFrameTiff(tiffPath As String) As String
        Using input As New OcrInput()
            input.LoadImageFrames(tiffPath) ' All frames loaded in one call

            Dim result = New IronTesseract().Read(input)

            ' Pages maps directly to TIFF frames
            For Each page In result.Pages
                Console.WriteLine($"Frame {page.PageNumber}: {page.Words.Count()} words")
            Next

            Return result.Text
        End Using
    End Function
End Class
$vbLabelText   $csharpLabel

Pętla wyodrębniania plików tymczasowych oraz łańcuch usuwania na poziomie klatek zostały usunięte. Wykrywanie liczby klatek za pomocą FrameDimension.Page znika.IronOCR mapuje klatki TIFF na OcrResult.Pages, więc dostęp do tekstu dla każdej klatki nie wymaga dodatkowej logiki iteracyjnej. Przewodnik dotyczący plików wejściowych TIFF/GIF obejmuje dodatkowe opcje wyboru klatek i częściowego przetwarzania plików TIFF.

Generowanie plików PDF z możliwością wyszukiwania

Oprogramowanie CharlesW generuje wyłącznie dane tekstowe. Konwersja zeskanowanego dokumentu do przeszukiwalnego PDF — częsty wymóg dla systemów zarządzania dokumentami — wymaga wtórnej biblioteki PDF (IronPDF, PDFSharp lub podobnej), aby nałożyć wyodrębniony tekst na oryginalne strony obrazu.IronOCR tworzy pliki PDF z możliwością wyszukiwania za pomocą jednego wywołania metody, bez konieczności korzystania z dodatkowej biblioteki.

Podejście Charlesw Tesseract:

// charlesw/Tesseract produces text only.
// Creating a searchable PDF requires a second library and significant code.
// The pattern below is representative — actual implementation varies by PDF library.
public void CreateSearchablePdf(string imagePath, string outputPdfPath)
{
    // Step 1: Extract text from image
    string extractedText;
    using var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default);
    using var img = Pix.LoadFromFile(imagePath);
    using var page = engine.Process(img);
    extractedText = page.GetText();

    // Step 2: Build a PDF with the image as background and text overlay
    // Requires a separate PDF library (not shown — 50-100+ additional lines)
    // The text layer must be positioned to match the original image layout
    // Word-level coordinates from the iterator are needed for accurate alignment
    throw new NotImplementedException(
        "Searchable PDF generation requires a separate PDF library. " +
        "Add PdfSharp, IronPDF, or similar, then implement text layer overlay.");
}
// charlesw/Tesseract produces text only.
// Creating a searchable PDF requires a second library and significant code.
// The pattern below is representative — actual implementation varies by PDF library.
public void CreateSearchablePdf(string imagePath, string outputPdfPath)
{
    // Step 1: Extract text from image
    string extractedText;
    using var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default);
    using var img = Pix.LoadFromFile(imagePath);
    using var page = engine.Process(img);
    extractedText = page.GetText();

    // Step 2: Build a PDF with the image as background and text overlay
    // Requires a separate PDF library (not shown — 50-100+ additional lines)
    // The text layer must be positioned to match the original image layout
    // Word-level coordinates from the iterator are needed for accurate alignment
    throw new NotImplementedException(
        "Searchable PDF generation requires a separate PDF library. " +
        "Add PdfSharp, IronPDF, or similar, then implement text layer overlay.");
}
Imports Tesseract

Public Sub CreateSearchablePdf(imagePath As String, outputPdfPath As String)
    ' Step 1: Extract text from image
    Dim extractedText As String
    Using engine As New TesseractEngine("./tessdata", "eng", EngineMode.Default)
        Using img As Pix = Pix.LoadFromFile(imagePath)
            Using page As Page = engine.Process(img)
                extractedText = page.GetText()
            End Using
        End Using
    End Using

    ' Step 2: Build a PDF with the image as background and text overlay
    ' Requires a separate PDF library (not shown — 50-100+ additional lines)
    ' The text layer must be positioned to match the original image layout
    ' Word-level coordinates from the iterator are needed for accurate alignment
    Throw New NotImplementedException("Searchable PDF generation requires a separate PDF library. " & _
                                      "Add PdfSharp, IronPDF, or similar, then implement text layer overlay.")
End Sub
$vbLabelText   $csharpLabel

Podejście IronOCR:

// SaveAsSearchablePdf produces a PDF/A-compatible searchable document
// Nie secondary library, no text overlay code, no coordinate mapping
public void CreateSearchablePdf(string imagePath, string outputPdfPath)
{
    var result = new IronTesseract().Read(imagePath);
    result.SaveAsSearchablePdf(outputPdfPath);
    Console.WriteLine($"Searchable PDF saved: {outputPdfPath}");
}

// Same API works for multi-page TIFF or existing PDF input
public void MakePdfSearchable(string scannedPdfPath, string outputPdfPath)
{
    var result = new IronTesseract().Read(scannedPdfPath);
    result.SaveAsSearchablePdf(outputPdfPath);
}
// SaveAsSearchablePdf produces a PDF/A-compatible searchable document
// Nie secondary library, no text overlay code, no coordinate mapping
public void CreateSearchablePdf(string imagePath, string outputPdfPath)
{
    var result = new IronTesseract().Read(imagePath);
    result.SaveAsSearchablePdf(outputPdfPath);
    Console.WriteLine($"Searchable PDF saved: {outputPdfPath}");
}

// Same API works for multi-page TIFF or existing PDF input
public void MakePdfSearchable(string scannedPdfPath, string outputPdfPath)
{
    var result = new IronTesseract().Read(scannedPdfPath);
    result.SaveAsSearchablePdf(outputPdfPath);
}
' SaveAsSearchablePdf produces a PDF/A-compatible searchable document
' Nie secondary library, no text overlay code, no coordinate mapping
Public Sub CreateSearchablePdf(imagePath As String, outputPdfPath As String)
    Dim result = New IronTesseract().Read(imagePath)
    result.SaveAsSearchablePdf(outputPdfPath)
    Console.WriteLine($"Searchable PDF saved: {outputPdfPath}")
End Sub

' Same API works for multi-page TIFF or existing PDF input
Public Sub MakePdfSearchable(scannedPdfPath As String, outputPdfPath As String)
    Dim result = New IronTesseract().Read(scannedPdfPath)
    result.SaveAsSearchablePdf(outputPdfPath)
End Sub
$vbLabelText   $csharpLabel

SaveAsSearchablePdf() osadza tekst OCR jako niewidzialną warstwę wyrównaną do rozpoznanych słów, sprawiając, że dokument jest w pełni przeszukiwalny bez zmiany jego wyglądu wizualnego. Przewodnik w formacie PDF z funkcją wyszukiwania obejmuje wybór zakresu stron i opcje kompresji. Przykład działającego kodu jest dostępny na stronie z przykładami w formacie PDF z możliwością wyszukiwania.

Charlesw TesseractAPI do IronOCR– dokumentacja API

Charlesw Tesseract Odpowiednik IronOCR
new TesseractEngine(tessDataPath, "eng", EngineMode.Default) new IronTesseract()
Pix.LoadFromFile(imagePath) input.LoadImage(imagePath)
Pix.LoadFromMemory(bytes) input.LoadImage(imageBytes)
engine.Process(pix) ocr.Read(input)
page.GetText() result.Text
page.GetMeanConfidence() result.Confidence (skala 0–100)
page.GetIterator() result.Pages, result.Words (bezpośrednie kolekcje)
iter.GetText(PageIteratorLevel.Word) word.Text
iter.GetConfidence(PageIteratorLevel.Word) word.Confidence
iter.TryGetBoundingBox(PageIteratorLevel.Word, out var b) word.X, word.Y, word.Width, word.Height
iter.GetText(PageIteratorLevel.Para) paragraph.Text
iter.IsAtBeginningOf(PageIteratorLevel.Block) page.Paragraphs (bezpośrednia iteracja)
EngineMode.Default Automatyczne (domyślnie Tesseract 5 LSTM)
EngineMode.TesseractOnly ocr.Configuration.PageSegmentationMode
Ręczny plik .traineddata tessdata dotnet add package IronOcr.Languages.French
TessDataPath stała + .csproj kopiowanie wpisów Nie dotyczy — w pakiecie
Pix.LoadFromFile() przez Leptonica DLL input.LoadImage() — brak wymaganego natywnego DLL
Metoda platformy GetTessDataPath() Nie dotyczy — usunięto
leptonica-1.82.0.dll / libleptonica-dev Nie dotyczy — brak zależności od Leptonica
Ręczne wyodrębnianie ramek plików tymczasowych dla formatu TIFF input.LoadImageFrames(tiffPath)
Brak możliwości wyszukiwania w pliku PDF result.SaveAsSearchablePdf(outputPath)
new TesseractEngine() na każdy wątek Jeden IronTesseract — bezpieczny dla wątków

Typowe problemy związane z migracją i ich rozwiązania

Problem 1: Wyjątek DllNotFoundException dla plików binarnych Leptonica lub Tesseract

Charlesw Tesseract: System.DllNotFoundException: Unable to load DLL 'leptonica-1.82.0': The specified module could not be found. Ten wyjątek występuje, gdy natywny DLL Leptonica nie znajduje się w oczekiwanej lokalizacji. Jest to powszechnie spotykane w świeżych kontenerach Dockera, agentach CI, lub jakimkolwiek środowisku, gdzie folder runtimes/ pakietu NuGet nie został poprawnie skopiowany.

Rozwiązanie: Usuń pakiet Tesseract. Zainstaluj IronOcr.IronOCRłączy wewnętrznie wszystkie natywne pliki binarne i nie korzysta z P/Invoke do systemowego Leptonica. Wyjątek nie może wystąpić, ponieważ nie ma zewnętrznej zależności od Leptonica:

dotnet remove package Tesseract
:InstallCmd dotnet add package IronOcr
dotnet remove package Tesseract
:InstallCmd dotnet add package IronOcr
SHELL

Nie wymaga się apt-get install libleptonica-dev. Brak wpisów <CopyToOutputDirectory> dla natywnych DLLs.

Problem 2: Błędy ścieżki Tessdata po wdrożeniu

Charlesw Tesseract: Tesseract.TesseractException: Failed to initialise tesseract engine. Występuje to, gdy TessDataPath nie zostanie rozwiązany podczas działania. Kompiluje się bez błędu, ale zawodzi tylko podczas działania, a ścieżka błędu zależy od środowiska wdrażania.

Rozwiązanie: Koncepcja ścieżki tessdata nie istnieje w IronOCR. Usuń stałą, usuń CopyToOutputDirectory XML w .csproj, oraz usuń metodę fabryczną, która go buduje. Dane językowe są dystrybuowane jako pakiety NuGet:

# Replace this manual tessdata file management:
#   tessdata/eng.traineddata  (15 MB, manually downloaded)
#   tessdata/fra.traineddata  (15 MB, manually downloaded)
#   .csproj <CopyToOutputDirectory> entry

# With NuGet packages:
dotnet add package IronOcr.Languages.French
# Replace this manual tessdata file management:
#   tessdata/eng.traineddata  (15 MB, manually downloaded)
#   tessdata/fra.traineddata  (15 MB, manually downloaded)
#   .csproj <CopyToOutputDirectory> entry

# With NuGet packages:
dotnet add package IronOcr.Languages.French
SHELL

Przewodnik dotyczący wielu języków pokazuje, jak skonfigurować rozpoznawanie wielu języków po dodaniu pakietów językowych.

Problem 3: Kompilacja kontenera kończy się niepowodzeniem po aktualizacji obrazu bazowego

Charlesw Tesseract: Plik Dockerfile zawiera apt-get install -y libleptonica-dev, aby spełnić natywną zależność Leptonica. Gdy obraz bazowy przechodzi z Debiana Bullseye na Bookworm lub gdy nazwa pakietu Leptonica zmienia się w różnych dystrybucjach, kompilacja kończy się niepowodzeniem z błędem apt. Naprawienie tego wymaga wiedzy na temat nazwy pakietu, której należy użyć w nowej dystrybucji.

Rozwiązanie: Całkowicie usuń linię Leptonica apt-get.IronOCR na Linux wymaga tylko standardowego pakietu libgdiplus, który każda aplikacja .NET używająca System.Drawing już potrzebuje:

# Before: Leptonica explicit install — breaks on base image updates
RUN apt-get update && apt-get install -y libleptonica-dev

# After: standard .NET Linux requirement only
RUN apt-get update && apt-get install -y libgdiplus

Przewodnik wdrażania Docker zawiera przetestowane szablony plików Dockerfile dla popularnych obrazów bazowych. Nie jest wymagany żaden kod infrastruktury specyficzny dla charlesw.

Problem 4: Wzorzec iteratora zatrzymuje się na pustych stronach lub stronach zawierających spacje

Charlesw Tesseract: ResultIterator zwraca null z iter.GetText() na niektórych segmentach strony, wymagając jawnych sprawdzeń null w całej pętli. Brak sprawdzenia null powoduje NullReferenceException na pustych stronach lub obrazach bez rozpoznawalnego tekstu.

Rozwiązanie: Zbiory wyników IronOCR nigdy nie są puste. Puste strony zwracają puste kolekcje. Sprawdź treść tekstu, a nie odwołania do wartości null:

// Before: null checks required at every iterator level
var wordText = iter.GetText(PageIteratorLevel.Word);
if (wordText != null && wordText.Trim().Length > 0)
    results.Add(wordText.Trim());

// After: collection is safe to enumerate; check content as needed
foreach (var word in result.Pages.SelectMany(p => p.Lines).SelectMany(l => l.Words))
{
    if (!string.IsNullOrWhiteSpace(word.Text))
        results.Add(word.Text);
}
// Before: null checks required at every iterator level
var wordText = iter.GetText(PageIteratorLevel.Word);
if (wordText != null && wordText.Trim().Length > 0)
    results.Add(wordText.Trim());

// After: collection is safe to enumerate; check content as needed
foreach (var word in result.Pages.SelectMany(p => p.Lines).SelectMany(l => l.Words))
{
    if (!string.IsNullOrWhiteSpace(word.Text))
        results.Add(word.Text);
}
' Before: null checks required at every iterator level
Dim wordText = iter.GetText(PageIteratorLevel.Word)
If wordText IsNot Nothing AndAlso wordText.Trim().Length > 0 Then
    results.Add(wordText.Trim())
End If

' After: collection is safe to enumerate; check content as needed
For Each word In result.Pages.SelectMany(Function(p) p.Lines).SelectMany(Function(l) l.Words)
    If Not String.IsNullOrWhiteSpace(word.Text) Then
        results.Add(word.Text)
    End If
Next
$vbLabelText   $csharpLabel

Problem 5: Naruszenia bezpieczeństwa wątków pod obciążeniem

Charlesw Tesseract: TesseractEngine nie jest bezpieczny dla wątków. Współdzielenie jednej instancji między równoczesnymi żądaniami w aplikacji ASP.NET powoduje naruszenia dostępu lub uszkodzone wyniki. Standardowym rozwiązaniem jest utworzenie jednego silnika na wątek, ale nie wynika to wprost z API, a komunikaty o błędach w przypadku niepowodzenia są niejasnymi wyjątkami natywnymi.

Rozwiązanie: IronTesseract jest bezpieczny dla wątków. Jedna instancja może obsługiwać jednoczesne żądania, a dla maksymalnej przepustowości można utworzyć jedną instancję na wątek w Parallel.ForEach — oba wzorce działają bez modyfikacji:

// Thread-safe parallel processing — IronTesseract handles concurrent access
var results = new System.Collections.Concurrent.ConcurrentBag<string>();
Parallel.ForEach(imageFiles, imagePath =>
{
    var ocr    = new IronTesseract();
    var result = ocr.Read(imagePath);
    results.Add(result.Text);
});
// Thread-safe parallel processing — IronTesseract handles concurrent access
var results = new System.Collections.Concurrent.ConcurrentBag<string>();
Parallel.ForEach(imageFiles, imagePath =>
{
    var ocr    = new IronTesseract();
    var result = ocr.Read(imagePath);
    results.Add(result.Text);
});
Imports System.Collections.Concurrent
Imports System.Threading.Tasks

' Thread-safe parallel processing — IronTesseract handles concurrent access
Dim results As New ConcurrentBag(Of String)()
Parallel.ForEach(imageFiles, Sub(imagePath)
    Dim ocr As New IronTesseract()
    Dim result = ocr.Read(imagePath)
    results.Add(result.Text)
End Sub)
$vbLabelText   $csharpLabel

Przewodnik po asynchronicznym OCR obejmuje wzorce asynchroniczne dla kontrolerów ASP.NET Core, w których blokowanie wątków jest niedopuszczalne.

Problem 6: Brak pliku binarnego ARM64 w czasie wykonywania

Charlesw Tesseract: Na agentach CI AWS Graviton (Linux ARM64) lub Apple Silicon zarchiwizowany pakiet może nie zawierać natywnego pliku binarnego ARM64. Niepowodzenie to DllNotFoundException lub BadImageFormatException przy tworzeniu silnika — błąd w czasie działania na platformie, której pakiet nie wspiera.

Rozwiązanie:IronOCR dostarcza sprawdzone pliki binarne ARM64 zarówno dla systemu Linux, jak i macOS. Wdrażaj na ARM64 bez zmian w kodzie. Przewodnik wdrożeniowy dla systemu Linux oraz przewodnik wdrożeniowy dla systemu macOS potwierdzają obsługiwane identyfikatory środowiska uruchomieniowego.

Lista kontrolna migracji Charlesw Tesseract

Przed migracją

Przeprowadź audyt kodu źródłowego, aby zidentyfikować wszystkie wzorce, które ulegną zmianie:

# Find all references to Tesseract namespace (engine creation, Pix usage, iterator usage)
grep -rn "using Tesseract" --include="*.cs" .

# Find TesseractEngine instantiation points
grep -rn "TesseractEngine" --include="*.cs" .

# Find Pix usage (Leptonica image type)
grep -rn "Pix\." --include="*.cs" .

# Find tessdata path constants and methods
grep -rn "tessdata\|TessDataPath\|traineddata" --include="*.cs" .

# Find platform-conditional deployment code
grep -rn "IsOSPlatform\|DOTNET_RUNNING_IN_CONTAINER\|LD_LIBRARY_PATH" --include="*.cs" .

# Find iterator pattern usage
grep -rn "GetIterator\|ResultIterator\|PageIteratorLevel" --include="*.cs" .

# Find confidence calls
grep -rn "GetMeanConfidence\|GetConfidence" --include="*.cs" .

# Find .csproj tessdata copy entries
grep -rn "tessdata" --include="*.csproj" .
# Find all references to Tesseract namespace (engine creation, Pix usage, iterator usage)
grep -rn "using Tesseract" --include="*.cs" .

# Find TesseractEngine instantiation points
grep -rn "TesseractEngine" --include="*.cs" .

# Find Pix usage (Leptonica image type)
grep -rn "Pix\." --include="*.cs" .

# Find tessdata path constants and methods
grep -rn "tessdata\|TessDataPath\|traineddata" --include="*.cs" .

# Find platform-conditional deployment code
grep -rn "IsOSPlatform\|DOTNET_RUNNING_IN_CONTAINER\|LD_LIBRARY_PATH" --include="*.cs" .

# Find iterator pattern usage
grep -rn "GetIterator\|ResultIterator\|PageIteratorLevel" --include="*.cs" .

# Find confidence calls
grep -rn "GetMeanConfidence\|GetConfidence" --include="*.cs" .

# Find .csproj tessdata copy entries
grep -rn "tessdata" --include="*.csproj" .
SHELL

Zwróć uwagę, na jakie środowiska wdrożeniowe jest skierowany projekt (Docker, Linux, ARM64, Azure, AWS) — są to środowiska, w których charlesw/tesseract wymaga najwięcej konfiguracji, którą eliminuje IronOCR.

Migracja kodu

  1. Uruchom dotnet remove package Tesseract, aby odinstalować wraper charlesw
  2. Uruchom dotnet add package IronOcr, aby zainstalować IronOCR
  3. Dodaj IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; podczas uruchamiania aplikacji
  4. Zastąp wszystkie instrukcje using Tesseract; instrukcjami using IronOcr;
  5. Usuń stałą ścieżki tessdata oraz wszelkie metody, które tworzą ścieżkę dla danego środowiska
  6. Usuń wszystkie bloki RuntimeInformation.IsOSPlatform() napisane do wyboru ścieżki tessdata
  7. Usuń wpisy <CopyToOutputDirectory> dla plików tessdata z wszystkich plików .csproj
  8. Usuń pliki .traineddata tessdata z kontroli źródła lub magazynów artefaktów wdrażania
  9. Dodaj dotnet add package IronOcr.Languages.* dla każdego języka wdrażanego wcześniej jako plik .traineddata
  10. Zastąp łańcuchy TesseractEngine + Pix.LoadFromFile() + engine.Process() przez new IronTesseract().Read()
  11. Zastąp wszystkie wywołania Pix.LoadFromFile() i Pix.LoadFromMemory() przez input.LoadImage()
  12. Zastąp wszystkie wywołania page.GetText() przez result.Text
  13. Zastąp ekstrakcję słów/wierszy opartą na witeratorze bezpośrednim dostępem do kolekcji w result.Pages
  14. Zastąp logikę progów iter.GetConfidence() przez LINQ na result.Words lub result.Lines
  15. Usuń libleptonica-dev / leptonica-1.82.0.dll z plików Dockerfile i skryptów wdrażania

Po migracji

Po zakończeniu aktualizacji kodu sprawdź, czy:

  • OCR działa poprawnie w systemie Windows bez żadnych błędów natywnych bibliotek DLL
  • OCR działa pomyślnie w kontenerze Docker na Linux bez żadnych zmian apt-get poza libgdiplus
  • OCR generuje tekst na platformie ARM64, jeśli ta platforma znajduje się w macierzy wdrożeniowej
  • Wielostronicowe pliki TIFF zwracają tekst ze wszystkich ramek, a nie tylko z pierwszej
  • Filtrowanie według pewności zwraca ten sam logiczny zbiór słów o wysokim poziomie pewności, co poprzednia implementacja iteratora
  • Dokumenty specyficzne dla danego języka (francuski, niemiećki itp.) są rozpoznawane poprawnie po zainstalowaniu pakietów NuGet dla danego języka
  • Operacje równoległego OCR są wykonywane bez wyjątków i uszkodzonych wyników
  • Generowany jest plik PDF z możliwością wyszukiwania, podczas gdy poprzednia implementacja zwracała wyłącznie tekst
  • Kompilacje w ramach procesu CI/CD bez żadnych etapów pobierania danych tessdata ani poleceń instalacyjnych Leptonica
  • Test sprawdzający na tym samym korpusie obrazów, który został użyty do walidacji poprzedniej implementacji

Kluczowe korzyści z migracji do IronOCR

Samodzielny model wdrażania. Po migracji zależność OCR jest w pełni opisana przez pojedyncze odwołanie do pakietu NuGet. Brak plików tessdata w kontroli źródła, brak wpisów CopyToOutputDirectory, brak kroków wdrażania natywnych DLLs, brak systemowych pakietów Leptonica. Pipelines CI/CD, które dotychczas wymagały zarządzania artefaktami w wielu krokach, są zredukowane do dotnet publish. Kod związany z wdrażaniem, który zgromadzono w celu obsługi opakowania charlesw, został trwałe usunięty.

Przenośność platformy bez logiki warunkowej. Ten sam plik binarny aplikacji działa na systemach Windows x64, Linux x64, Linux ARM64, macOS x64 i macOS ARM64 bez modyfikacji. Zespoły dodające cel wdrożeniowy ARM64 — czy to AWS Graviton, Apple Silicon CI, czy Raspberry Pi — nie piszą nowego kodu wykrywającego platformę. Przewodnik wdrożeniowy dla systemu Linux oraz przewodnik wdrożeniowy dla AWS potwierdzają sprawdzone konfiguracje.

Dokładność Tesseract 5 dzięki wbudowanemu przetwarzaniu wstępnemu. Przejście z Tesseract 4.1.1 na Tesseract 5 poprawia rozpoznawanie dokumentów o obniżonej jakości.IronOCR dodaje automatyczne przetwarzanie wstępne do aktualizacji silnika, stosując korekcję pochylenia, usuwanie szumów, normalizację kontrastu i binarizację przed przetworzeniem każdego obrazu przez silnik. Dokumenty, które wcześniej wymagały niestandardowego procesu przetwarzania wstępnego w celu osiągnięcia akceptowalnych progów dokładności, teraz osiągają te progi bez dodatkowego kodu. Przewodnik po korekcji jakości obrazu dokumentuje konkretne opcje przetwarzania wstępnego dla przypadków, które wymagają dostosowania wykraczającego poza ustawienia domyślne.

Bezpośrednia nawigacja wynikami zastępuje typowy wzorzec iteratora. Wzorzec iteratora charlesw — GetIterator(), Begin(), Next(), IsAtBeginningOf(), sprawdzanie null przez całość — jest zastępowany przez proste kolekcje. Słowa, wiersze, akapity i strony są właściwościami obiektu wynikowego. Filtrowanie oparte na pewności jest wyrażeniem LINQ. Kod, który wcześniej wyciągał dane na poziomie słów, wymagał 15–30 linii kodu do zarządzania iteratorami; Odpowiednik w IronOCR to 2–3 wiersze. Strona z wynikami OCR zawiera podsumowanie pełnego modelu wyników strukturalnych.

Wynikowy plik PDF przeszukiwalny bez dodatkowej biblioteki. result.SaveAsSearchablePdf() generuje PDF z warstwą tekstową wyrównaną do rozpoznanych słów, nie wymagając żadnej dodatkowej biblioteki PDF. Systemy zarządzania dokumentami, które przetwarzają pliki PDF z możliwością wyszukiwania, nie wymagają już oddzielnego etapu generowania plików PDF. Ten sam obiekt wynikowy, który dostarcza wyodrębniony tekst, zapisuje również plik z możliwością wyszukiwania, dzięki czemu przetwarzanie dokumentów opiera się na jednej bibliotece.

Aktywna konserwacja i obsługa poprawek bezpieczeństwa.IronOCR otrzymuje regularne aktualizacje uwzględniające ulepszenia modelu Tesseract 5, weryfikację zgodności środowiska uruchomieniowego .NET oraz obsługę poprawek bezpieczeństwa dla podstawowego silnika C++. Ta zależność nie wiąże się już z profilem ryzyka pakietu archiwalnego — przeglądy zgodności nie wykazują braków w ścieżce poprawek bezpieczeństwa. W miarę jak .NET 10 będzie stawał się ogólnodostępny do 2026 r., centrum dokumentacji IronOCR będzie odzwierciedlać aktualną kompatybilność bez konieczności stosowania obejść lub rozgałęzień.

Zwróć uwagęPDFSharp i Tesseract są zarejestrowanymi znakami towarowymi ich odpowiednich właścicieli. Ta strona nie jest powiązana z, autoryzowana przez, ani sponsorowana przez Google lub empira Software GmbH. Wszystkie nazwy produktów, logo i marki są własnością ich odpowiednich właścicieli. Porównania mają charakter wyłącznie informacyjny i odzwierciedlają informacje dostępne publicznie w momencie pisania.

Często Zadawane Pytania

Dlaczego warto przejść z charlesw/tesseract (opakowanie Tesseract dla .NET) na IronOCR?

Typowe czynniki motywujące to eliminacja złożoności interoperacyjności COM, zastąpienie zarządzania licencjami opartego na plikach, uniknięcie rozliczeń za stronę, umożliwienie wdrażania w Dockerze/kontenerach oraz przyjęcie natywnego dla NuGet przepływu pracy, który integruje się ze standardowymi narzędziami .NET.

Jakie są główne zmiany w kodzie podczas migracji z charlesw/tesseract (opakowanie Tesseract dla .NET) do IronOCR?

Zastąp sekwencje inicjalizacji charlesw/tesseract instancjonowaniem IronTesseract, usuń zarządzanie cyklem życia COM (jawne wzorce Create/Load/Close) i zaktualizuj nazwy właściwości wyników. W rezultacie znacznie zmniejsza się liczba powtarzających się linii kodu.

Jak zainstalować IronOCR, aby rozpocząć migrację?

Uruchom polecenie „Install-Package IronOcr” w konsoli menedżera pakietów lub „dotnet add package IronOcr” w interfejsie CLI. Pakiety językowe są oddzielnymi pakietami: na przykład „dotnet add package IronOcr.Languages.French” dla języka francuskiego.

Czy IronOCR dorównuje dokładnością OCR charlesw/tesseract (opakowanie Tesseract dla .NET) w przypadku standardowych dokumentów biznesowych?

IronOCR zapewnia wysoką dokładność w przypadku standardowych treści biznesowych, w tym faktur, umów, paragonów i formularzy wypełnionych na komputerze. Filtry przetwarzania wstępnego obrazu (prostowanie, usuwanie szumów, wzmacnianie kontrastu) dodatkowo poprawiają rozpoznawanie w przypadku pogorszonej jakości danych wejściowych.

W jaki sposób IronOCR obsługuje dane językowe, które charlesw/tesseract (opakowanie Tesseract dla .NET) instaluje oddzielnie?

Dane językowe w IronOCR są dystrybuowane jako pakiety NuGet. Polecenie „dotnet add package IronOcr.Languages.German” instaluje obsługę języka niemiećkiego. Nie wymaga to ręcznego umieszczania plików ani podawania ścieżek katalogów.

Czy migracja z charlesw/tesseract (opakowanie Tesseract dla .NET) do IronOCR wymaga zmian w infrastrukturze wdrożeniowej?

IronOCR wymaga mniej zmian w infrastrukturze niż charlesw/tesseract (opakowanie Tesseract dla .NET). Nie ma ścieżek do plików binarnych SDK, lokalizacji plików licencji ani konfiguracji serwerów licencyjnych. Pakiet NuGet zawiera kompletny silnik OCR, a klucz licencyjny jest ciągiem znaków ustawionym w kodzie aplikacji.

Jak skonfigurować licencjonowanie IronOCR po migracji?

W kodzie uruchamiającym aplikację przypisz IronOcr.License.LicenseKey = „YOUR-KEY”. W Dockerze lub Kubernetesie zapisz klucz jako zmienną środowiskową i odczytaj go podczas uruchamiania. Użyj License.IsValidLicense do sprawdzenia ważności przed przyjęciem ruchu.

Czy IronOCR może przetwarzać pliki PDF w taki sam sposób jak charlesw/tesseract?

Tak. IronOCR odczytuje zarówno natywne, jak i zeskanowane pliki PDF. Należy utworzyć instancję IronTesseract, wywołać ocr.Read(input), gdzie input jest ścieżką do pliku PDF lub obiektem OcrPdfInput, a następnie iterować strony OcrResult. Nie jest wymagany oddzielny proces renderowania plików PDF.

W jaki sposób IronOCR radzi sobie z wątkami podczas przetwarzania dużych ilości danych?

IronTesseract można bezpiecznie instancjonować dla każdego wątku. Uruchom jedną instancję na wątek w Parallel.ForEach lub puli zadań, uruchom OCR równolegle i usuń każdą instancję po zakończeniu. Nie jest wymagany żaden stan globalny ani blokowanie.

Jakie formaty wyjściowe obsługuje IronOCR po wyodrębnieniu tekstu?

IronOCR zwraca ustrukturyzowane wyniki, w tym tekst, współrzędne słów, wyniki pewności i strukturę strony. Opcje eksportu obejmują zwykły tekst, PDF z możliwością wyszukiwania oraz obiekty wyników ustrukturyzowanych do dalszego przetwarzania.

Czy ceny IronOCR są bardziej przewidywalne niż charlesw/tesseract (opakowanie Tesseract dla .NET) w przypadku skalowania obciążeń?

IronOCR stosuje licencję wieczystą z opłatą ryczałtową, bez opłat za stronę lub wolumen. Niezależnie od tego, czy przetwarzasz 10 000, czy 10 milionów stron, koszt licencji pozostaje stały. Opcje licencji wolumenowych i zespołowych znajdują się na stronie z cennikiem IronOCR.

Co stanie się z moimi istniejącymi testami po migracji z charlesw/tesseract (opakowanie Tesseract dla .NET) do IronOCR?

Testy sprawdzające wyodrębnioną treść tekstową powinny nadal przechodzić pomyślnie po migracji. Testy weryfikujące wzorce wywołań API lub cykl życia obiektów COM będą wymagały aktualizacji, aby odzwierciedlały prostszy model inicjalizacji i wyników IronOCR.

Kannaopat Udonpant
Inżynier oprogramowania
Zanim stał się inżynierem oprogramowania, Kannapat ukończył doktorat z zasobów środowiskowych na Uniwersytecie Hokkaido w Japonii. W czasie studiowania, Kannapat również został członkiem Laboratorium Robotyki Pojazdów, które jest częścią Wydziału Inżynierii Bioprodukcji. W 2022 roku wykorzystał swoje umiejętności w ...
Czytaj więcej

Zespół wsparcia Iron

Jesteśmy online 24 godziny, 5 dni w tygodniu.
Czat
E-mail
Zadzwoń do mnie