Jak odczytywać pliki GIF i TIFF z wieloma ramkami w języku C#
Ten przewodnik prowadzi programistów .NET przez pełną migrację zPaddleSharp OCR(Sdcb.PaddleOCR) na IronOCR. Obejmuje to zastąpienie zarządzania sesjami wnioskowania, wyeliminowanie zależności od przetwarzania wstępnego OpenCV, usunięcie logiki wyboru zaplecza dla procesorów CPU, GPU i OpenVINO oraz migrację procesów rozpoznawania tabel. Każda sekcja zawiera kod przed i po przetworzeniu, zaczerpnięty z wzorców specyficznych dla PaddleSharp, które nie pojawiają się w ogólnych porównaniach OCR.
Dlaczego warto przejść z PaddleSharp OCR
PaddleSharp udostępnia potok wnioskowania oparty na głębokim uczeniu się na poziomie warstwy aplikacji. Architektura ta zapewnia dostęp do wydajności modeli PaddlePaddle, ale wymaga, aby aplikacja zarządzała kwestiami, które w innym przypadku należałyby do infrastruktury. Następujące problemy skłaniają większość zespołów .NET do poszukiwania alternatyw.
Konfiguracja Backend Inference to kod aplikacji. Wybór między backendami CPU, GPU i OpenVINO w PaddleSharp wymaga konstrukcji i konfiguracji obiektów PaddleConfig, wybrania odpowiedniego pakietu NuGet dla natywnego środowiska uruchomieniowego na docelową platformę wdrożeniową oraz warunkowego rozgałęzienia kodu inicjalizacyjnego w zależności od dostępnego sprzętu podczas uruchamiania. Ta logika znajduje się w aplikacji, a nie w bibliotece, i przestaje działać, gdy zmienia się docelowe środowisko.
OpenCV jest wymaganym elementem zależności dla danych wejściowych obrazu. PaddleSharp nie może bezpośrednio akceptować ścieżki pliku ani strumienia. Każdy obraz przechodzi przez Cv2.ImRead() OpenCV zanim dotrze do silnika OCR. To wymusza OpenCvSharp4 oraz specyficzny dla platformy pakiet OpenCvSharp4.runtime.* do grafu zależności. Aktualizacja środowiska uruchomieniowego jednej platformy bez aktualizacji drugiej powoduje awarie środowiska uruchomieniowego, które trudno odtworzyć w różnych środowiskach.
Żywotność sesji inferencji wymaga wyraźnego projektowania. PaddleOcrAll ładuje trzy binaria modelu z dysku przy konstrukcji. Ten koszt — mierzony w setkach milisekund — oznacza, że obiekt nie może być instancjonowany per-żądanie. Zespoły muszą zaprojektować strategię cyklu życia: singleton, puli, lub zakres. W ASP.NET Core zazwyczaj oznacza to zarejestrowaną usługę z dokładną analizą bezpieczeństwa wątków, ponieważ PaddleOcrAll dzieli się podstawowym stanem natywnym.
Rozpoznawanie tabel wymaga pobrania oddzielnych modeli. Ekstrakcja dokumentów strukturalnych w PaddleSharp wymaga dedykowanego modelu rozpoznawania tabel oprócz standardowego trzyetapowego procesu wykrywania/klasyfikacji/rozpoznawania. Model ten jest czwartym plikiem, który należy pobrać, zaktualizować i skonfigurować. Nie ma ujednoliconego interfejsu API — rozpoznawanie tabel wykorzystuje odrębną ścieżkę kodu z własnym typem wyniku.
Brak możliwości tworzenia plików PDF z funkcją wyszukiwania. PaddleSharp generuje ciągi tekstowe. Nie może tworzyć plików PDF z możliwością wyszukiwania. Zespoły, które muszą archiwizować zeskanowane dokumenty jako pliki PDF z możliwością wyszukiwania tekstu, muszą zintegrować oddzielną bibliotekę PDF, zarządzać tą dodatkową zależnością i napisać warstwę konwersji. Luka w formacie wyjściowym jest całkowita: brak hOCR, brak strukturalnego, przeszukiwalnego pliku PDF, brak nakładki warstwy tekstowej.
Łańcuch zależności upstream nie jest własnością społeczności .NET. PaddleSharp stanowi nakładkę na framework wnioskowania PaddlePaddle firmy Baidu. Zmiany formatu modelu między wersjami PaddleOCR spowodowały w przeszłości uszkodzenie warstwy powiązania .NET. Większość działań związanych ze śledzeniem problemów, dokumentacją i dyskusjami na temat wydań odbywa się w języku chińskim. Dla zespołu .NET, w którym nie ma osób posługujących się językiem mandaryńskim monitorujących projekty nadrzędne, przełomowe zmiany pojawiają się bez ostrzeżenia.
Podstawowy problem
Wybór i inicjalizacja backendu w PaddleSharp wymaga kodu konfiguracyjnego, który należy do infrastruktury, a nie do logiki 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
// IronOCR: Nie backend selection. Nie 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: Nie backend selection. Nie 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: Nie backend selection. Nie 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
IronOCR a PaddleSharp OCR: porównanie funkcji
Oto bezpośrednie porównanie możliwości w wymiarach, które mają największe znaczenie podczas migracji:
| Funkcja | PaddleSharp OCR | IronOCR |
|---|---|---|
| Wymagane pakiety NuGet | Minimum 3–4 | 1 |
| Metoda wprowadzania obrazów | OpenCV Cv2.ImRead() |
Ścieżka bezpośrednia, strumień lub tablica bajtów |
| Wejście PDF (natywne) | Nie | Tak |
| Plik PDF chroniony hasłem | Nie | Tak |
| Wielostronicowy plik TIFF | Za pośrednictwem OpenCV | Język ojczysty |
| Wynik w formacie PDF z możliwością wyszukiwania | Nie | Tak (result.SaveAsSearchablePdf()) |
| eksport hOCR | Nie | Tak |
| Wybór zaplecza (CPU/GPU/OpenVINO) | Ręczny PaddleConfig |
Automatyczne |
| Potok przetwarzania wstępnego | Ręczne operacje OpenCV | Wbudowany (Deskew, DeNoise, Contrast, itd.) |
| Zarządzanie cyklem życia sesji wnioskowania | Podręcznik (kosztowna konstrukcja) | Lekki IronTesseract |
| Model rozpoznawania tabel | Oddzielna ścieżka pobierania i kodu | input.LoadImage() + uporządkowany wynik |
| Obsługiwane języki | ~10–20 | 125+ |
| Instalacja języka | Pobierz plik wzorcowy | Pakiet NuGet |
| Wielojęzyczne tłumaczenie symultaniczne | Ograniczone | Tak (OcrLanguage.French + OcrLanguage.German) |
| OCR oparte na regionie | Brak wbudowanych | CropRectangle |
| Odczytywanie BarCode podczas OCR | Nie | Tak (ocr.Configuration.ReadBarCodes = true) |
| Wyniki pewności | W podziale na regiony | Za słowo, za wiersz, za stronę |
| Strukturalna hierarchia wyników | Lista regionów płaskich | Strony → Akapity → Wiersze → Słowa → Znaki |
| Wdrażanie wielopłatformowe | Złożone (pakiety środowiska uruchomieniowego platformy) | Pojedynczy pakiet NuGet, wszystkie platformy |
| Wdrożenie Docker | Wiele warstw, pakiety uruchomieniowe | Pojedyncza warstwa |
| Wsparcie komercyjne | Zgłoszenia na GitHubie (głównie w języku chińskim) | Wsparcie e-mail |
| Model licencji | Apache 2.0 | Wieczysty ($999 Lite, 1 499 $ Pro, 2 999 $ Enterprise) |
Szybki start: Migracja zPaddleSharp OCRdo IronOCR
Krok 1: Zastąp pakiet NuGet
Usuń PaddleSharp i jego zależność od 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
Zainstaluj IronOCR z NuGet:
dotnet add package IronOcr
Krok 2: Aktualizacja przestrzeni nazw
Zastąp przestrzenie nazw PaddleSharp pojedynczą przestrzenią nazw 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
Krok 3: Inicjalizacja licencji
Dodaj inicjalizację licencji raz podczas uruchamiania aplikacji — w Program.cs, Startup.cs, lub w korzeniu kompozycji:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Przykłady migracji kodu
Wymiana cyklu życia sesji wnioskowania
Konstrukcja PaddleOcrAll PaddleSharp jest kosztowna, ponieważ ładuje trzy binaria modelu synchronicznie przy instancji. Aplikacje produkcyjne muszą traktować go jako obiekt długotrwały, co determinuje konkretny wzorzec wstrzykiwania zależności. Należy również zwrócić uwagę na łańcuch usuwania, ponieważ podstawowe zasoby natywne muszą być zwalniane we właściwej kolejności.
Podejście PaddleSharp OCR:
// 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)()
Podejście 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)()
Shift od wymuszonego singletonu do elastycznego cyklu życia jest znaczące. Koszt budowy PaddleSharp determinuje decyzję dotyczącą okresu eksploatacji usługi;IronOCR pozwala dokonać wyboru w oparciu o wymagania aplikacji dotyczące wielowątkowości i izolacji żądań. Podręcznik konfiguracji IronTesseract obejmuje opcje konfiguracyjne, które mają zastosowanie na poziomie instancji.
Migracja potoku przetwarzania wstępnego OpenCV
Zespoły korzystające z PaddleSharp w przypadku skanów o niskiej jakości zazwyczaj tworzą potok przetwarzania wstępnego OpenCV przed uruchomieniem silnika OCR. Potok ten wymaga znajomości interfejsu API OpenCV, który jest znacznie większy niż to, czego faktycznie wymaga jakiekolwiek zadanie przetwarzania wstępnego OCR. Typowe operacje — deskew, denoise, rozciąganie kontrastu — wymagają wielu operacji Mat i dokładnego zarządzania pamięcią z użyciem bloków using, aby zapobiec wyciekom pamięci natywnej.
Podejście PaddleSharp OCR:
// 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
Podejście 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
Brak przydziałów Mat. Brak wiedzy na temat parametrów progu adaptacyjnego. Bez niestandardowych obliczeń matematycznych dotyczących obracania w celu wyprostowania. Ten sam proces przetwarzania wstępnego, który wymagał 30–50 linii kodu OpenCV, sprowadza się teraz do czterech wywołań metod. Przewodnik po korekcji jakości obrazu dokumentuje każdy dostępny filtr wraz z przykładami przed i po. Dla dokumentów z dużym szumem tła, input.DeepCleanBackgroundNoise() idzie dalej niż DeNoise() bez żadnych dodatkowych parametrów.
Dla zespołów, których wymagania dotyczące przetwarzania wstępnego są niestandardowe, kreator filtrów zapewnia interaktywne narzędzie do oceny kombinacji filtrów na konkretnych typach dokumentów przed zatwierdzeniem kodu.
Eliminacja wyboru backendu
PaddleSharp udostępnia backend wnioskowania jako element na poziomie aplikacji. Wdrożenie, które musi działać na maszynie wirtualnej w chmurze wyposażonej wyłącznie w procesor, wykorzystuje inny kod inicjalizacyjny niż to przeznaczone dla stacji roboczej z procesorem graficznym lub urządzenia brzegowego obsługującego Intel OpenVINO. Ta logika warunkowa zazwyczaj znajduje się w kodzie uruchamiającym aplikację, sprawdzaniu zmiennych środowiskowych lub flagach funkcji — czyli w elementach infrastruktury, które nie mają nic wspólnego z odczytywaniem tekstu z obrazów.
Podejście PaddleSharp OCR:
// 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
Podejście IronOCR:
using IronOcr;
// Nie backend selection. Nie switch statement. Nie 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;
// Nie backend selection. Nie switch statement. Nie 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
' Nie backend selection. Nie switch statement. Nie 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
Wzorzec Parallel.ForEach tutaj jest wątkowo-bezpieczny prosto z pudełka. Każda instancja IronTesseract jest niezależna i nie współdzieli stanu natywnego. Dla zespołów, których wdrożenie PaddleSharp wymaga poświęcania czasu na zarządzanie warunkami backendowymi, uproszczenie to oznacza również poprawę niezawodności wdrożenia — ten sam artefakt kompilacji działa wszędzie bez kodu wykrywającego sprzęt. Przewodnik po optymalizacji szybkości obejmuje opcje konfiguracyjne dla scenariuszy, w których ważna jest przepustowość.
Migracja rozpoznawania tabel
Wyodrębnianie tabel w PaddleSharp wymaga dedykowanego modelu rozpoznawania tabel — czwartego pliku modelu, wykraczającego poza standardowy zestaw wykrywania, klasyfikacji i rozpoznawania. Model tabelowy wykorzystuje oddzielne wywołanie API i zwraca własną strukturę wyników. Zespoły tworzące potoki przetwarzania faktur, formularzy lub arkuszy kalkulacyjnych utrzymują dwie równoległe ścieżki inicjalizacji i dwie strategie analizowania wyników.
Podejście PaddleSharp OCR:
// 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
Podejście 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
W przypadku dokumentów, w których sama struktura tabeli musi zostać wyodrębniona jako wiersze i kolumny,IronOCR zapewnia dedykowaną funkcję wyodrębniania tabel:
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
Usunięto jedno pobranie modelu. Wyeliminowano jedną ścieżkę inicjalizacji. Strukturalna hierarchia wyników w IronOCR— z współrzędnymi X/Y na poziomie słów — zapewnia dane pozycyjne potrzebne do odtworzenia wierszy i kolumn tabeli bez oddzielnego modelu rozpoznawania. Przewodnik po odczytywaniu tabeli oraz przewodnik po wynikach odczytu obejmują pełny, ustrukturyzowany interfejs API.
Wyszukiwalny plik PDF z zeskanowanych dokumentów
PaddleSharp generuje ciągi tekstowe i nic więcej. Stworzenie archiwum dokumentów, w którym zeskanowane pliki PDF umożliwiają wyszukiwanie tekstu, wymaga zintegrowania oddzielnej biblioteki PDF, napisania warstwy nakładki tekstowej oraz współdziałania dwóch bibliotek. Zespoły, które zaakceptowały to ograniczenie, często uznają je za czynnik decydujący o migracji — wysiłek związany z integracją dwóch bibliotek przewyższa wysiłek związany ze zmianą dostawcy usług OCR.
Podejście PaddleSharp OCR:
// 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
Podejście 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
Brak biblioteki PDF. Bez mapowania współrzędnych. Brak pozycjonowania warstw tekstowych. Format wyjściowy PDF z możliwością wyszukiwania w IronOCR osadza niewidoczną warstwę tekstową na oryginalnym obrazie, tworząc plik, który jest zarówno wierny wizualnie zeskanowanemu dokumentówi, jak i w pełni przeszukiwalny pod kątem tekstu. Poradnik w formacie PDF z funkcją wyszukiwania obejmuje wybór stron, opcje jakości oraz kontrolę metadanych.
Odnośnik do dokumentacji APIPaddleSharp OCRdo IronOCR
| PaddleSharp OCR | IronOCR |
|---|---|
Sdcb.PaddleOCR (namespace) |
IronOcr (namespace) |
Sdcb.PaddleInference (namespace) |
Nie jest wymagane — konfiguracja automatyczna |
PaddleOcrAll |
IronTesseract |
new PaddleOcrAll(det, cls, rec) |
new IronTesseract() |
LocalFullModels.ChineseV3.DetectionModel |
Brak odpowiednika — brak wyboru modelu |
LocalFullModels.ChineseV3.ClassifierModel |
Brak odpowiednika — brak wyboru modelu |
LocalFullModels.ChineseV3.RecognitionModel |
Brak odpowiednika — brak wyboru modelu |
PaddleConfig.FromModelDir() |
Brak odpowiednika — brak obiektu konfiguracyjnego |
config.EnableGpu(memMB, deviceId) |
Brak odpowiednika — backend jest automatyczny |
config.EnableMkldnn() |
Brak odpowiednika — backend jest automatyczny |
config.SetCpuMathLibraryNumThreads(n) |
Brak odpowiednika — zarządzane wewnętrznie |
Cv2.ImRead(path) (OpenCV load) |
input.LoadImage(path) |
ocr.Run(mat) |
ocr.Read(input) lub ocr.Read("file.jpg") |
result.Regions |
result.Pages[0].Words lub result.Pages[0].Lines |
region.Text |
word.Text, line.Text, paragraph.Text |
region.Rect.Center.X/.Y |
word.X, word.Y |
region.Score (confidence) |
word.Confidence, result.Confidence |
| Zmiana języka na poziomie modelu | ocr.Language = OcrLanguage.French |
| Model tabelaryczny (do pobrania osobno) | Wbudowana hierarchia wyników strukturalnych |
Cv2.CvtColor(..., GRAY) |
input.Binarize() lub input.Contrast() |
Cv2.GaussianBlur(...) |
input.DeNoise() |
| Brak możliwości wyszukiwania w pliku PDF | result.SaveAsSearchablePdf("output.pdf") |
Typowe problemy związane z migracją i ich rozwiązania
Problem 1: Nieudane wyładowanie zależności OpenCV
PaddleSharp OCR: OpenCvSharp4.runtime.win i podobne specyficzne dla platformy pakiety uruchomieniowe instalują nienadzorowane natywne biblioteki DLL. Te biblioteki DLL mogą uniemożliwić prawidłowe czyszczenie w niektórych scenariuszach hostingu — w szczególności podczas recyklingu puli aplikacji IIS — i powodować błędy ładowania zestawów, gdy w czasie kompilacji odwołuje się do niewłaściwego pakietu środowiska uruchomieniowego platformy. Ich usunięcie wymaga zarówno usunięcia pakietu NuGet, jak i wyczyszczenia wszelkich buforowanych plików binarnych w katalogu wyjściowym.
Rozwiązanie: Po usunięciu pakietów OpenCvSharp4 oraz OpenCvSharp4.runtime.*, wyczyść katalog wyjściowy build przed ponownym zbudowaniem:
dotnet remove package OpenCvSharp4
dotnet remove package OpenCvSharp4.runtime.win
dotnet clean
dotnet build
dotnet remove package OpenCvSharp4
dotnet remove package OpenCvSharp4.runtime.win
dotnet clean
dotnet build
IronOCR wewnętrznie łączy swoje natywne zależności i obsługuje niezarządzany cykl życia. Nie jest wymagany wybór pakietu uruchomieniowego specyficznego dla danej platformy. Podręcznik konfiguracji IronTesseract zawiera informacje o wymaganiach platformy, które IronOCR obsługuje automatycznie.
Problem 2: Pliki modeli pozostawione na dysku po migracji
PaddleSharp OCR: Pliki modeli pobrane przez PaddleSharp (wykrywanie, klasyfikacja, rozpoznawanie oraz wszelkie modele tabel) są zazwyczaj przechowywane w katalogu models/ względnym do aplikacji lub w skonfigurowanej ścieżce. Pliki te nie są usuwane po odinstalowaniu pakietu NuGet. W obrazie Docker zwiększają one niepotrzebnie rozmiar warstwy. W potoku wdrażania nieaktualne pliki modeli znajdujące się w starych ścieżkach mogą powodować błędy uruchamiania, jeśli jakikolwiek pozostały kod inicjalizacyjny odwołuje się do nich.
Rozwiązanie: Wyraźnie usuń katalogi modeli w ramach migracji. Sprawdź konfigurację startową pod kątem wszelkich odniesień do ścieżek:
# 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" .
Po usunięciu odniesień do modeli i zainicjowaniu IronOCR należy usunąć katalog modelu z repozytorium i kontekstu kompilacji Docker.
Problem 3: Założenie dotyczące czasu życia singletonu przestaje obowiązywać po migracji
PaddleSharp OCR: PaddleOcrAll był zarejestrowany jako singleton, ponieważ koszt jego konstrukcji sprawiał, że instancjonowanie per-żądanie było niepraktyczne. Kod migracyjny, który przenosi IronOCR do tej samej rejestracji singletonowej, wprowadza niepotrzebne współdzielenie stanu między żądaniami. Podczas gdy IronTesseract jest wątkowo-bezpieczny, gdy jest używany równocześnie, nie ma potrzeby udostępniać jednej instancji — każda instancja jest niezależna.
Rozwiązanie: Należy ocenić, czy rejestracja singletonów służy celom wykraczającym poza wydajność. W przypadku większości aplikacji ASP.NET Core rejestracja tymczasowa jest lepszym wyborem w przypadku 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)()
W scenariuszach przetwarzania wsadowego o dużej przepustowości, w których wymagane jest wyraźne ponowne wykorzystanie instancji, wzorzec singleton lub wzorzec puli nadal działa — jest to jednak wybór związany z wydajnością, a nie wymóg poprawności.
Problem 4: Kolejność regionów wyników nie jest już wymagana
PaddleSharp OCR: result.Regions zwraca wykryte regiony tekstowe w kolejności wykrycia, która niekoniecznie odpowiada kolejności czytania (od lewej do prawej, z góry do dołu). Zespoły zazwyczaj stosują sortowanie według .Rect.Center.Y, a następnie .Rect.Center.X zanim połączą tekst regionu — wzorzec ten pojawia się w prawie każdym wdrożeniu ekstrakcji tekstu PaddleSharp. Dosłowne przeniesienie tego wzorca do IronOCR powoduje powstanie zbędnego kodu.
Rozwiązanie:IronOCR domyślnie zwraca wyniki w kolejności odczytu. Usuń sortowanie:
// PaddleSharp — manual reading-order sort required
var text = string.Join("\n", result.Regions
.OrderBy(r => r.Rect.Center.Y)
.ThenBy(r => r.Rect.Center.X)
.Select(r => r.Text));
//IronOCR— result.Text is already in reading order; no sort needed
var text = result.Text;
// For word-level access with position, use the structured hierarchy directly
foreach (var word in result.Pages[0].Words)
{
Console.WriteLine($"{word.Text} at ({word.X},{word.Y})");
}
// PaddleSharp — manual reading-order sort required
var text = string.Join("\n", result.Regions
.OrderBy(r => r.Rect.Center.Y)
.ThenBy(r => r.Rect.Center.X)
.Select(r => r.Text));
//IronOCR— result.Text is already in reading order; no sort needed
var text = result.Text;
// For word-level access with position, use the structured hierarchy directly
foreach (var word in result.Pages[0].Words)
{
Console.WriteLine($"{word.Text} at ({word.X},{word.Y})");
}
Imports System
Imports System.Linq
' PaddleSharp — manual reading-order sort required
Dim text = String.Join(vbLf, result.Regions _
.OrderBy(Function(r) r.Rect.Center.Y) _
.ThenBy(Function(r) r.Rect.Center.X) _
.Select(Function(r) r.Text))
' IronOCR— result.Text is already in reading order; no sort needed
text = result.Text
' For word-level access with position, use the structured hierarchy directly
For Each word In result.Pages(0).Words
Console.WriteLine($"{word.Text} at ({word.X},{word.Y})")
Next
Problem 5: Przywracanie uszkodzonych pakietów zależnych od backendu
PaddleSharp OCR: Niektóre konfiguracje PaddleSharp warunkowo odwołują się do różnych pakietów Sdcb.PaddleInference.runtime.* w zależności od docelowego środowiska (CUDA dla GPU, MKL dla OpenVINO, tylko CPU). Czasami pojawia się to jako warunki .csproj lub jako oddzielne pliki projektu na docelowe wdrożenie. Powstała w ten sposób macierz kompilacji powoduje przerwanie działania potoków CI, gdy przywracany jest niewłaściwy zestaw pakietów.
Rozwiązanie: Po usunięciu pakietów PaddleSharp, przeprowadź audyt pliku .csproj dla warunkowych bloków PackageReference odwołujących się do jakichkolwiek pakietów Sdcb.* lub OpenCvSharp* i usuń je całkowicie:
grep -n "Sdcb\|OpenCvSharp\|PaddleInference" *.csproj
grep -n "Sdcb\|OpenCvSharp\|PaddleInference" *.csproj
IronOCR używa pojedynczego odniesienia do pakietu IronOcr bez warunków platformowych. Ten sam pakiet działa poprawnie w systemach Windows, Linux i macOS.
Problem 6: Struktura wyników tabeli nie ma bezpośredniego odpowiednika
PaddleSharp OCR: PaddleOcrTable zwraca strukturę opartą na komórkach z indeksami wierszy i kolumn dla każdej rozpoznanej komórki. Kod, który konsumuje tę strukturę, zazwyczaj buduje dwuwymiarową tablicę indeksowaną przez (row, column).IronOCR nie zapewnia identycznej struktury indeksu komórek — dostarcza współrzędne słów i wierszy, które wymagają grupowania przestrzennego w celu odtworzenia siatki komórek.
Rozwiązanie: Odtworzyć strukturę tabeli na podstawie współrzędnych słów z IronOCR, stosując grupowanie według pozycji Y dla wierszy i sortowanie według pozycji X dla kolumn. W przypadku popularnych formatów tabel instrukcja czytania tabel przedstawia podejście oparte na grupowaniu przestrzennym. Dla ustrukturyzowanych faktur z znanymi pozycjami pól, regionowe OCR z CropRectangle jest czystszym wzorcem niż pełne wyodrębnianie tabeli na stronie:
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
Lista kontrolna migracji PaddleSharp OCR
Przed migracją
Przed usunięciem pakietów sprawdź wszystkie odwołania do PaddleSharp w kodzie źródłowym:
# 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" .
Zrób spis plików modeli na dysku i zanotuj ich ścieżki. Zinwentaryzuj wszystkie docelowe wdrożenia i czy jakiekolwiek mają specyficzne dla GPU lub OpenVINO warunki NuGet w .csproj. Należy zwrócić uwagę na usługi zarejestrowane jako singletony ze względu na koszt konstrukcji PaddleSharp.
Migracja kodu
- Usuń wszystkie pakiety NuGet
Sdcb.PaddleOCR,Sdcb.PaddleInference,OpenCvSharp4iOpenCvSharp4.runtime.*ze wszystkich plików projektów. - Zainstaluj pakiet NuGet
IronOcr. - Zainstaluj pakiety językowe NuGet dla wymaganych języków (np.
IronOcr.Languages.ChineseSimplified). - Dodaj
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";do uruchamiania aplikacji. - Zastąp wszystkie instrukcje
using Sdcb.PaddleOCR,using Sdcb.PaddleInference, iusing OpenCvSharpzusing IronOcr. - Zastąp instancjonowanie i ładowanie modelu
PaddleOcrAllznew IronTesseract(). - Usuń wszystkie bloki wyboru backendu
PaddleConfig(warunki CPU, GPU, OpenVINO). - Zastąp wywołania
Cv2.ImRead(path)używającinput.LoadImage(path)iOcrInput. - Zastąp operacje przedprzetwarzania OpenCV (
CvtColor,GaussianBlur,Threshold, itp.) metodami filtrówOcrInput(Deskew(),DeNoise(),Contrast(),Binarize()). - Zastąp wywołania
ocr.Run(mat)zocr.Read(input). - Zastąp enumerację
result.Regionszresult.Pages,result.Pages[n].Lines, lubresult.Pages[n].Words. - Usuń łańcuchy sortowania
.OrderBy(r => r.Rect.Center.Y).ThenBy(r => r.Rect.Center.X)— kolejność czytania jest automatyczna. - Zastąp inicjalizację i parsowanie wyników
PaddleOcrTablez ukierunkowaniem na region lub grupowaniem słów za pomocą współrzędnych. - Dodaj
result.SaveAsSearchablePdf(path)wszędzie tam, gdzie wymagane jest archiwum przeszukiwalne PDF. - Ponowna ocena rejestracji na cały okres użytkowania usługi: rejestracje singletonów oparte na kosztach konstrukcji PaddleSharp mogą zazwyczaj stać się przejściowe lub ograniczone zakresem.
- Usuń pliki modeli z dysku i usuń katalogi modeli z kontekstów kompilacji Docker.
- Usuń wszystkie warunkowe bloki
.csprojdla specyficznych dla platformy Paddle lub OpenCV pakietów uruchomieniowych.
Po migracji
- Sprawdź, czy wyniki ekstrakcji tekstu są zgodne z wynikami PaddleSharp lub je przewyższają na reprezentatywnej próbie 20–30 dokumentów z każdego typu dokumentu w potoku.
- Potwierdź brak żadnych wyjątków ładowania zestawów związanych z
OpenCvSharpw logach startowych aplikacji. - Przetestuj wdrożenie na każdej platformie docelowej (Windows, Linux, Docker) przy użyciu tego samego artefaktu kompilacji — nie powinno być konieczne wybieranie pakietów specyficznych dla danej platformy.
- Zweryfikuj, że dokumenty, które wcześniej wymagały ręcznego sortowania wyników, produkują poprawnie uporządkowany tekst przez
result.Text. - Sprawdź, czy pliki wyjściowe w formacie PDF z możliwością wyszukiwania umożliwiają wyszukiwanie tekstu w programie Adobe Acrobat Reader lub innej przeglądarce plików PDF.
- Uruchom aplikację pod obciążeniem, aby potwierdzić, że instancje
IronTesseracttworzone per-żądanie nie wywołują presji na pamięć porównywalnej do konstrukcjiPaddleOcrAllper-żądanie. - Sprawdź, czy pakiety językowe zainstalowane jako pakiety NuGet przywracają się poprawnie w CI bez dodatkowych kroków wdrażania plików.
- Przetestuj wszystkie scenariusze wyodrębniania tabel pod kątem oczekiwanej struktury wierszy/kolumn, stosując podejście oparte na regionach lub grupowaniu współrzędnych.
- Potwierdź, że czas uruchamiania aplikacji maleje po wyeliminowaniu konstrukcji singletonu
PaddleOcrAllze ścieżki startowej.
Kluczowe korzyści z migracji do IronOCR
Jeden pakiet zastępuje stos czterech pakietów. Po migracji, ślad zależności OCR to pojedyncze odniesienie NuGet IronOcr. Stos czterech pakietów — Sdcb.PaddleOCR, Sdcb.PaddleInference, OpenCvSharp4 i specyficzne dla platformy środowisko uruchomieniowe — staje się jednym wpisem w pliku projektu. Audyty zależności, skany licencji i monitorowanie podatności pokrywają teraz jedną powierzchnię zamiast czterech.
Artefakty wdrożeniowe są jednolite we wszystkich środowiskach. Warunki wyboru backendu — CPU kontra GPU kontra OpenVINO — zostały usunięte. Ten sam artefakt kompilacji jest wdrażany na laptopie programisty, w środowisku CI, kontenerze Linux oraz maszynie wirtualnej w chmurze bez konieczności dokonywania wyboru pakietów specyficznych dla danego środowiska lub rozgałęzień inicjalizacyjnych. Obrazy Dockera zmniejszają się, ponieważ nie ma plików modeli do COPY i nie ma pakietów środowiska uruchomieniowego do zainstalowania.
Rurociągi archiwizacji dokumentów nie wymagają już drugiej biblioteki. result.SaveAsSearchablePdf() eliminuje zależność od biblioteki PDF, którą większość zespołów PaddleSharp dodała, aby tworzyć przeszukiwalne archiwa. Przeprowadzenie OCR i zapisanie pliku PDF z możliwością wyszukiwania to jedno wywołanie API. Dla zespołów przetwarzających tysiące zeskanowanych dokumentów dziennie to uproszczenie eliminuje całą klasę konfliktów między wersjami bibliotek. Wpis na blogu dotyczący plików PDF z funkcją wyszukiwania omawia kwestie związane ze skalą produkcyjną.
Decyzje dotyczące żywotności usług odzwierciedlają wymagania aplikacji, a nie ograniczenia biblioteki. IronTesseract ma lekką konstrukcję. Wzorzec wymuszonego singletonu, wynikający z kosztownego ładowania modeli przez PaddleSharp, nie jest już konieczny. W .NET Core zakres usług można określać na żądanie, co pozwala na wyraźniejsze oddzielenie współbieżnych użytkowników i eliminuje problemy związane z wątkami o współdzielonym stanie. Więcej informacji na temat opcji wdrażania można znaleźć na stronie poświęconej przypadkom użycia ASP.NET OCR.
Rozszerzenie językowe to instalacja pakietu, a nie projekt badawczy. Katalog ponad 125 języków obejmuje skrypty europejskie, azjatyckie, bliskowschodnie oraz specjalistyczne w postaci pakietów NuGet. Dodanie francuskiego, niemieckiego, arabskiego lub japońskiego do rurociągu, który zaczynał tylko od chińskiego, to dotnet add package IronOcr.Languages.French i jedna linia konfiguracji. Bez źródeł plików modeli, bez badań dostępności w górnym biegu, bez ręcznego wdrażania plików.
Przetwarzanie wstępne jest częścią API OCR. Wiedza o OpenCV, której wymagało przetwarzanie wstępne PaddleSharp — zrozumienie jąder filtrów, zarządzanie usuwaniem Mat, wybór parametrów adaptacyjnego progu — nie jest już warunkiem wstępnym dla pracy OCR. OcrInput zapewnia nazwane operacje z rozsądnymi domyślnymi ustawieniami. Zespoły, które nie były specjalistami w zakresie OpenCV, ale utrzymywały kod przetwarzania wstępnego OpenCV, mogą usunąć ten kod bez konieczności jego zastępowania. Strona poświęcona funkcjom przetwarzania wstępnego zawiera listę wszystkich dostępnych filtrów wraz z dokumentacją dotyczącą tego, kiedy należy je stosować.
Często Zadawane Pytania
Dlaczego warto przejść z PaddleSharp OCR 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 PaddleSharp OCR do IronOCR?
Zastąp sekwencje inicjalizacji PaddleSharp 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 bibliotece PaddleSharp OCR 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 PaddleSharp OCR 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 PaddleSharp OCR do IronOCR wymaga zmian w infrastrukturze wdrożeniowej?
IronOCR wymaga mniej zmian w infrastrukturze niż PaddleSharp OCR. Nie ma ścieżek binarnych SDK, lokalizacji plików licencyjnych 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 PaddleSharp?
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ż PaddleSharp OCR 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 PaddleSharp OCR 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.

