Como ler GIFs e TIFFs com várias frames em C#
Este guia orienta os desenvolvedores .NET em uma migração completa doPaddleSharp OCR(Sdcb.PaddleOCR) para IronOCR. Abrange a substituição do gerenciamento de sessões de inferência, a eliminação da dependência do pré-processamento do OpenCV, a remoção da lógica de seleção de backend para CPU, GPU e OpenVINO e a migração de fluxos de trabalho de reconhecimento de tabelas. Cada seção fornece o código antes e depois, extraído de padrões específicos do PaddleSharp que não aparecem em comparações genéricas de OCR.
Por que migrar do PaddleSharp OCR?
O PaddleSharp expõe um pipeline de inferência de aprendizado profundo na camada de aplicação. Essa arquitetura permite o acesso ao desempenho do modelo PaddlePaddle, mas exige que seu aplicativo gerencie o que, de outra forma, seriam problemas de infraestrutura. Os seguintes problemas levam a maioria das equipes .NET a buscar alternativas.
A Configuração do Backend de Inferência É Código de Aplicação. Escolher entre backends CPU, GPU e OpenVINO no PaddleSharp requer construir e configurar objetos PaddleConfig, selecionando o pacote NuGet de runtime nativo adequado para o alvo de implementação e ramificando condicionalmente o código de inicialização com base no hardware disponível em tempo de execução. Esta lógica está na sua aplicação, não na biblioteca, e quebra quando o ambiente alvo muda.
O OpenCV é uma dependência necessária para entrada de imagens. O PaddleSharp não aceita um caminho de arquivo ou um fluxo diretamente. Toda imagem passa pelo Cv2.ImRead() do OpenCV antes de chegar ao motor OCR. Isso força OpenCvSharp4 e um pacote OpenCvSharp4.runtime.* específico da plataforma em seu gráfico de dependências. A atualização de um ambiente de execução de plataforma sem a atualização do outro causa falhas de execução difíceis de reproduzir em diferentes ambientes.
O Tempo de Vida da Sessão de Inferência Requer Design Explícito. PaddleOcrAll carrega três binários de modelo do disco no tempo de construção. Esse custo — mensurável em centenas de milissegundos — significa que o objeto não pode ser instanciado por solicitação. As equipes devem desenhar uma estratégia de ciclo de vida: singleton, agrupado ou com escopo. Não ASP.NET Core, isso geralmente significa um serviço registrado com uma análise cuidadosa de segurança de threads, pois PaddleOcrAll compartilha o estado nativo subjacente.
O reconhecimento de tabelas requer downloads de modelos separados. A extração de documentos estruturados no PaddleSharp exige um modelo dedicado para reconhecimento de tabelas, além do pipeline padrão de três etapas de detecção/classificação/reconhecimento. Esse modelo é um quarto arquivo que precisa ser baixado, versionado e configurado. Não existe uma interface de API unificada — o reconhecimento de tabelas utiliza um caminho de código distinto com seu próprio tipo de resultado.
Sem saída em PDF pesquisável. O PaddleSharp gera sequências de texto. Não consegue gravar arquivos PDF pesquisáveis. As equipes que precisam arquivar documentos digitalizados como PDFs pesquisáveis por texto devem integrar uma biblioteca de PDF separada, gerenciar essa dependência adicional e escrever uma camada de conversão. A lacuna no formato de saída é total: sem hOCR, sem PDF estruturado pesquisável, sem sobreposição de camada de texto.
A cadeia de dependências upstream não pertence à comunidade .NET . O PaddleSharp utiliza o framework de inferência PaddlePaddle da Baidu. As alterações no formato do modelo entre as versões do PaddleOCR já causaram problemas na camada de vinculação do .NET . A maior parte do acompanhamento de problemas, da documentação e das discussões sobre as versões ocorre em chinês. Para uma equipe .NET sem falantes de mandarim monitorando os projetos upstream, alterações que quebram a compatibilidade chegam sem aviso prévio.
O problema fundamental
A seleção e inicialização de um backend no PaddleSharp requer código de configuração que pertence à infraestrutura, não à lógica de 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: Não backend selection. Não 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: Não backend selection. Não 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: Não backend selection. Não 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 vs PaddleSharp OCR: Comparação de Recursos
Aqui está uma comparação direta das capacidades nas dimensões mais importantes durante a migração:
| Recurso | PaddleSharp OCR | IronOCR |
|---|---|---|
| Pacotes NuGet necessários | 3–4 mínimo | 1 |
| Método de entrada de imagem | OpenCV Cv2.ImRead() |
Caminho direto, fluxo ou matriz de bytes |
| Entrada de PDF (nativa) | Não | Sim |
| PDF protegido por senha | Não | Sim |
| TIFF de várias páginas | Via OpenCV | Nativo |
| Saída em PDF pesquisável | Não | Sim (result.SaveAsSearchablePdf()) |
| Exportação hOCR | Não | Sim |
| Seleção do backend (CPU/GPU/OpenVINO) | Manual PaddleConfig |
Automático |
| Pipeline de pré-processamento | Operações manuais do OpenCV | Integrado (Deskew, DeNoise, Contrast, etc.) |
| Gerenciamento do ciclo de vida da sessão de inferência | Manual (construção dispendiosa) | Leve IronTesseract |
| Modelo de reconhecimento de tabela | Caminhos de download e código separados | input.LoadImage() + resultado estruturado |
| Idiomas suportados | ~10–20 | 125+ |
| Instalação de idioma | Download do arquivo do modelo | Pacote NuGet |
| Simultaneidade multilíngue | Limitado | Sim (OcrLanguage.French + OcrLanguage.German) |
| OCR baseado em região | Sem nenhum embutido | CropRectangle |
| Leitura de código de barras durante OCR | Não | Sim (ocr.Configuration.ReadBarCodes = true) |
| Pontuações de confiança | Por região | Por palavra, por linha, por página |
| hierarquia de saída estruturada | Lista de regiões planas | Páginas → Parágrafos → Linhas → Palavras → Caracteres |
| Implantação multiplataforma | Complexo (pacotes de tempo de execução da plataforma) | NuGet único, para todas as plataformas. |
| Implantação em Docker | Múltiplas camadas, pacotes de tempo de execução | Camada única |
| Suporte comercial | Problemas do GitHub (principalmente em chinês) | Suporte por e-mail |
| Modelo de licença | Apache 2.0 | Perpetual ($999 Lite, $1,499 Pro, $2,999 Enterprise) |
Guia rápido: Migração doPaddleSharp OCRpara o IronOCR
Passo 1: Substitua o pacote NuGet
Remova o PaddleSharp e sua dependência do 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
Instale o IronOCR a partir do NuGet :
dotnet add package IronOcr
Etapa 2: Atualizar Namespaces
Substitua os namespaces do PaddleSharp pelo namespace único do 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
Etapa 3: Inicializar a licença
Adicione a inicialização da licença uma vez na inicialização da aplicação — em Program.cs, Startup.cs, ou na sua raiz de composição:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Exemplos de migração de código
Substituição do ciclo de vida da sessão de inferência
O PaddleOcrAll do PaddleSharp é caro para construir porque carrega três binários de modelo de forma síncrona na instanciação. Aplicações em produção devem tratá-lo como um objeto de longa duração, o que exige um padrão específico de injeção de dependência. A cadeia de descarte também requer atenção, pois os recursos naturais subjacentes devem ser liberados na ordem correta.
Abordagem 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)()
Abordagem 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)()
A Shift de um modelo de singleton forçado para um modelo de tempo de vida flexível é significativa. O custo de construção do PaddleSharp define sua decisão quanto à vida útil do produto; O IronOCR permite que você escolha com base nas necessidades de multithreading e isolamento de requisições da sua aplicação. O guia de configuração do IronTesseract aborda opções de configuração que se aplicam ao nível da instância.
Migração do pipeline de pré-processamento do OpenCV
As equipes da PaddleSharp que trabalham com digitalizações de baixa qualidade geralmente criam um pipeline de pré-processamento com OpenCV antes de invocar o mecanismo de OCR. Esse pipeline exige conhecimento da API do OpenCV, que é substancialmente maior do que o necessário para qualquer tarefa de pré-processamento de OCR. Operações comuns — deskew, denoise, contraste — requerem múltiplas operações Mat e gerenciamento cuidadoso de memória com blocos using para prevenir vazamentos de memória nativa.
Abordagem 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
Abordagem 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
Sem alocações Mat. Não há conhecimento dos parâmetros de limiar adaptativo. Sem cálculos personalizados de rotação de desalinhamento. O mesmo pipeline de pré-processamento que exigia de 30 a 50 linhas de código OpenCV passa a exigir apenas quatro chamadas de método. O guia de correção da qualidade da imagem documenta todos os filtros disponíveis com exemplos de antes e depois. Para documentos com ruído de fundo pesado, input.DeepCleanBackgroundNoise() vai além de DeNoise() sem quaisquer parâmetros adicionais.
Para equipes cujos requisitos de pré-processamento não são padronizados, o assistente de filtros oferece uma ferramenta interativa para avaliar combinações de filtros em seus tipos de documento específicos antes de inserir o código.
Eliminação da seleção de backend
O PaddleSharp expõe o backend de inferência como uma preocupação em nível de aplicação. Uma implementação que precisa ser executada em uma máquina virtual na nuvem com apenas CPU usa um código de inicialização diferente de uma implementação destinada a uma estação de trabalho com GPU ou a um dispositivo de borda compatível com Intel OpenVINO. Essa lógica condicional geralmente acaba no código de inicialização do aplicativo, em verificações de variáveis de ambiente ou em sinalizadores de recursos — trabalho de infraestrutura que não tem nada a ver com a leitura de texto de imagens.
Abordagem 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
Abordagem IronOCR:
using IronOcr;
// Não backend selection. Não switch statement. Não 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;
// Não backend selection. Não switch statement. Não 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
' Não backend selection. Não switch statement. Não 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
O padrão Parallel.ForEach aqui é seguro para threads desde o início. Cada instância de IronTesseract é independente, sem estado nativo compartilhado. Para equipes cuja implementação do PaddleSharp envolve o gerenciamento de condicionais de backend, essa simplificação também representa uma melhoria na confiabilidade da implementação — o mesmo artefato de compilação é executado em qualquer lugar sem a necessidade de código de detecção de hardware. O guia de otimização de velocidade aborda opções de configuração para cenários sensíveis à taxa de transferência.
Migração de reconhecimento de tabela
A extração de tabelas no PaddleSharp requer um modelo de reconhecimento de tabelas dedicado — um quarto arquivo de modelo além do conjunto padrão de detecção, classificação e reconhecimento. O modelo de tabela utiliza uma chamada de API separada e retorna sua própria estrutura de resultados. As equipes que desenvolvem fluxos de processamento de faturas, formulários ou planilhas mantêm dois caminhos de inicialização paralelos e duas estratégias de análise de resultados.
Abordagem 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
Abordagem 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
Para documentos em que a própria estrutura da tabela precisa ser extraída como linhas e colunas, o IronOCR oferece um recurso dedicado de extração de tabelas:
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
Um download de modelo foi eliminado. Um caminho de inicialização eliminado. A hierarquia de resultados estruturada no IronOCR— com coordenadas X/Y em nível de palavra — fornece os dados posicionais necessários para reconstruir linhas e colunas de tabelas sem um modelo de reconhecimento separado. O guia de leitura de tabelas e o guia de resultados de leitura abrangem toda a API de saída estruturada.
Saída em PDF pesquisável a partir de documentos digitalizados
O PaddleSharp produz apenas sequências de texto. Criar um arquivo de documentos que permita a busca de texto em PDFs digitalizados exige a integração de uma biblioteca de PDF separada, a criação de uma camada de sobreposição de texto e o gerenciamento simultâneo de duas bibliotecas. As equipes que aceitaram essa restrição muitas vezes descobrem que ela é o gatilho para a migração — o esforço da integração entre as duas bibliotecas supera o esforço da troca de fornecedores de OCR.
Abordagem 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
Abordagem 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
Nenhuma biblioteca de PDFs. Sem mapeamento de coordenadas. Sem posicionamento de camada de texto. O formato de saída PDF pesquisável do IronOCR incorpora uma camada de texto invisível sobre a imagem original, produzindo um arquivo que é visualmente fiel ao documento digitalizado e totalmente pesquisável por texto. O guia prático sobre PDFs pesquisáveis aborda seleção de páginas, opções de qualidade e controle de metadados.
Referência de mapeamento da API OCR PaddleSharp para IronOCR
| PaddleSharp OCR | IronOCR |
|---|---|
Sdcb.PaddleOCR (namespace) |
IronOcr (namespace) |
Sdcb.PaddleInference (namespace) |
Não é necessário — configurado automaticamente |
PaddleOcrAll |
IronTesseract |
new PaddleOcrAll(det, cls, rec) |
new IronTesseract() |
LocalFullModels.ChineseV3.DetectionModel |
Sem equivalente — sem seleção de modelo |
LocalFullModels.ChineseV3.ClassifierModel |
Sem equivalente — sem seleção de modelo |
LocalFullModels.ChineseV3.RecognitionModel |
Sem equivalente — sem seleção de modelo |
PaddleConfig.FromModelDir() |
Não há equivalente — nenhum objeto de configuração |
config.EnableGpu(memMB, deviceId) |
Não há equivalente — o backend é automático |
config.EnableMkldnn() |
Não há equivalente — o backend é automático |
config.SetCpuMathLibraryNumThreads(n) |
Não há equivalente — gerenciado internamente |
Cv2.ImRead(path) (OpenCV load) |
input.LoadImage(path) |
ocr.Run(mat) |
ocr.Read(input) ou ocr.Read("file.jpg") |
result.Regions |
result.Pages[0].Words ou result.Pages[0].Lines |
region.Text |
word.Text, line.Text, paragraph.Text |
region.Rect.Center.X/.Y |
word.X, word.Y |
region.Score (confidence) |
word.Confidence, result.Confidence |
| Troca de linguagem em nível de modelo | ocr.Language = OcrLanguage.French |
| Modelo de tabela (download separado) | hierarquia de resultados estruturada integrada |
Cv2.CvtColor(..., GRAY) |
input.Binarize() ou input.Contrast() |
Cv2.GaussianBlur(...) |
input.DeNoise() |
| Não há saída em PDF pesquisável. | result.SaveAsSearchablePdf("output.pdf") |
Problemas e soluções comuns em migrações
Problema 1: A dependência do OpenCV não é descarregada
PaddleSharp OCR: OpenCvSharp4.runtime.win e pacotes de tempo de execução específicos da plataforma semelhante instalam DLLs nativas não gerenciadas. Essas DLLs podem impedir a limpeza adequada em alguns cenários de hospedagem — particularmente na reciclagem do pool de aplicativos do IIS — e causar falhas no carregamento de assemblies quando o pacote de tempo de execução da plataforma incorreta é referenciado durante a compilação. A remoção delas requer tanto a remoção do pacote NuGet quanto a limpeza de quaisquer binários nativos em cache no diretório de saída.
Solução: Depois de remover os pacotes OpenCvSharp4 e OpenCvSharp4.runtime.*, limpe o diretório de saída de compilação antes de reconstruir:
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
O IronOCR inclui internamente suas dependências nativas e gerencia o ciclo de vida não gerenciado. Não é necessário selecionar nenhum pacote de tempo de execução específico da plataforma. O guia de configuração do IronTesseract documenta os requisitos de plataforma que o IronOCR gerencia automaticamente.
Problema 2: Arquivos de modelo deixados no disco após a migração
PaddleSharp OCR: Arquivos de modelo baixados pelo PaddleSharp (detecção, classificação, reconhecimento e quaisquer modelos de tabela) são tipicamente armazenados em um diretório models/ relativo à aplicação ou em um caminho configurado. Esses arquivos não são removidos quando o pacote NuGet é desinstalado. Em uma imagem Docker, elas adicionam tamanho de camada desnecessário. Em um pipeline de implantação, arquivos de modelo obsoletos em caminhos antigos podem causar falhas de inicialização se algum código de inicialização remanescente fizer referência a eles.
Solução: Remova explicitamente os diretórios de modelos como parte da migração. Verifique a configuração de inicialização em busca de referências a caminhos:
# 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" .
Após remover as referências ao modelo e inicializar o IronOCR, exclua o diretório do modelo do repositório e do contexto de construção do Docker.
Problema 3: A suposição de tempo de vida único deixa de existir após a migração.
PaddleSharp OCR: PaddleOcrAll foi registrado como um singleton porque seu custo de construção tornou a instanciação por solicitação impraticável. O código de migração que eleva o IronOCR para o mesmo registro singleton introduz um compartilhamento de estado desnecessário entre as solicitações. Enquanto IronTesseract é seguro para threads quando usado simultaneamente, não é necessário compartilhar uma única instância — cada instância é independente.
Solução: Avaliar se o registro singleton serve a um propósito além do desempenho. Para a maioria das aplicações ASP.NET Core , o registro transitório é a opção mais adequada com o 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)()
Para cenários de processamento em lote de alto volume, onde se deseja a reutilização explícita de instâncias, um padrão singleton ou em pool ainda funciona — mas é uma escolha de desempenho, não um requisito de correção.
Problema 4: A ordenação por região de resultados não é mais necessária.
PaddleSharp OCR: result.Regions retorna regiões de texto detectadas na ordem de detecção, que não necessariamente correspondem à ordem de leitura (esquerda para a direita, de cima para baixo). As equipes geralmente aplicam uma classificação por .Rect.Center.Y e depois .Rect.Center.X antes de juntar o texto da região — um padrão que aparece em quase todas as implementações de extração de texto PaddleSharp. Migrar esse padrão literalmente para o IronOCR produz código redundante.
Solução: Por padrão, o IronOCR retorna os resultados na ordem de leitura. Remover a classificação:
// 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
Problema 5: Pacotes condicionais de backend interrompem a restauração
PaddleSharp OCR: Algumas configurações do PaddleSharp referenciam condicionalmente diferentes pacotes Sdcb.PaddleInference.runtime.* com base no ambiente alvo (CUDA para GPU, MKL para OpenVINO, apenas CPU). Isso às vezes aparece como condições .csproj ou como arquivos de projeto separados por alvo de implementação. A matriz de compilação resultante quebra os pipelines de CI quando o conjunto de pacotes incorreto é restaurado.
Solução: Depois de remover os pacotes PaddleSharp, audite o arquivo .csproj para blocos condicionais PackageReference referenciando quaisquer pacotes Sdcb.* ou OpenCvSharp* e remova-os completamente:
grep -n "Sdcb\|OpenCvSharp\|PaddleInference" *.csproj
grep -n "Sdcb\|OpenCvSharp\|PaddleInference" *.csproj
IronOCR usa uma única referência de pacote IronOcr sem condicionais de plataforma. O mesmo pacote é restaurado corretamente no Windows, Linux e macOS.
Problema 6: A estrutura de resultados da tabela não possui equivalente direto.
PaddleSharp OCR: PaddleOcrTable retorna uma estrutura baseada em células com índices de linha e coluna por célula reconhecida. O código que consome esta estrutura normalmente constrói um array bidimensional indexado por (row, column). O IronOCR não fornece uma estrutura de índice de células idêntica — ele fornece coordenadas de palavras e linhas que exigem agrupamento espacial para reconstruir uma grade de células.
Solução: Reconstruir a estrutura da tabela a partir das coordenadas das palavras do IronOCR, usando o agrupamento por posição Y para as linhas e a ordenação por posição X para as colunas. Para formatos de tabela comuns, o guia de leitura de tabelas fornece uma abordagem de agrupamento espacial. Para faturas estruturadas com posições de campo conhecidas, OCR baseado em região com CropRectangle é um padrão mais limpo do que a extração de tabelas de página completa:
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 de verificação para migração do PaddleSharp OCR
Pré-migração
Antes de remover pacotes, verifique todas as referências ao PaddleSharp no código-fonte:
# 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" .
Faça um inventário dos arquivos do modelo no disco e anote seus caminhos. Inventarie todos os alvos de implementação e se algum deles tem condicionais NuGet específicos para GPU ou OpenVINO em .csproj. Observe que quaisquer serviços registrados como individuais devido ao custo de construção do PaddleSharp são importantes.
Migração de código
- Remova todos os pacotes NuGet
Sdcb.PaddleOCR,Sdcb.PaddleInference,OpenCvSharp4eOpenCvSharp4.runtime.*de cada arquivo de projeto. - Instale o pacote NuGet
IronOcr. - Instale os pacotes NuGet de idiomas para os idiomas necessários (por exemplo,
IronOcr.Languages.ChineseSimplified). - Adicione
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";à inicialização da aplicação. - Substitua todas as instruções
using Sdcb.PaddleOCR,using Sdcb.PaddleInferenceeusing OpenCvSharpporusing IronOcr. - Substitua a instanciação e carregamento de modelo
PaddleOcrAllpornew IronTesseract(). - Exclua todos os blocos de seleção de backend
PaddleConfig(condicionais de CPU, GPU, OpenVINO). - Substitua chamadas
Cv2.ImRead(path)porinput.LoadImage(path)usandoOcrInput. - Substitua operações de pré-processamento OpenCV (
CvtColor,GaussianBlur,Threshold, etc.) por métodos de filtroOcrInput(Deskew(),DeNoise(),Contrast(),Binarize()). - Substitua chamadas
ocr.Run(mat)porocr.Read(input). - Substitua enumerações
result.Regionsporresult.Pages,result.Pages[n].Linesouresult.Pages[n].Words. - Remova cadeias de classificação
.OrderBy(r => r.Rect.Center.Y).ThenBy(r => r.Rect.Center.X)— a ordem de leitura é automática. - Substitua a inicialização
PaddleOcrTablee análise de resultado por direcionamento baseado em regiãoOcrInputou agrupamento de palavras baseado em coordenadas. - Adicione
result.SaveAsSearchablePdf(path)em qualquer lugar onde um arquivo PDF pesquisável seja necessário. - Reavaliar os registros de tempo de vida útil do serviço: registros únicos, motivados pelo custo de construção do PaddleSharp, podem normalmente se tornar transitórios ou com escopo definido.
- Exclua os arquivos de modelo do disco e remova os diretórios de modelo dos contextos de construção do Docker.
- Remova quaisquer blocos condicionais
PackageReference.csprojpara pacotes de runtime Paddle ou OpenCV específicos da plataforma.
Pós-migração
- Verificar se a saída da extração de texto corresponde ou excede a saída do PaddleSharp em uma amostra representativa de 20 a 30 documentos de cada tipo de documento no fluxo de trabalho.
- Confirme que não há exceções de carregamento de assembly relacionadas a
OpenCvSharpnos logs de inicialização da aplicação. - Teste a implantação em cada plataforma de destino (Windows, Linux, Docker) usando o mesmo artefato de compilação — não deve ser necessário selecionar nenhum pacote específico da plataforma.
- Verifique se documentos que anteriormente exigiam classificação manual de resultados produzem texto corretamente ordenado via
result.Text. - Confirme se os arquivos PDF pesquisáveis permitem a busca de texto no Adobe Acrobat Reader ou em um visualizador de PDF de sua escolha.
- Execute a aplicação sob carga para confirmar que instâncias
IronTesseractcriadas por solicitação não produzem pressão de memória comparável à construçãoPaddleOcrAllpor solicitação. - Verificar se os pacotes de idiomas instalados como pacotes NuGet são restaurados corretamente na CI sem etapas adicionais de implantação de arquivos.
- Teste quaisquer cenários de extração de tabelas em relação à estrutura esperada de linhas/colunas usando a abordagem baseada em região ou em agrupamento por coordenadas.
- Confirme que o tempo de inicialização da aplicação diminui após eliminar a construção
PaddleOcrAllsingleton do caminho de inicialização.
Principais benefícios da migração para o IronOCR
Um Pacote Substitui uma Pilha de Quatro Pacotes. Após a migração, a pegada de dependência OCR é uma única referência NuGet IronOcr. A pilha de quatro pacotes — Sdcb.PaddleOCR, Sdcb.PaddleInference, OpenCvSharp4, e um runtime específico de plataforma — torna-se uma entrada no arquivo do projeto. Auditorias de dependência, varreduras de licença e monitoramento de vulnerabilidade agora cobrem uma superfície em vez de quatro.
Os artefatos de implantação são uniformes em todos os ambientes. As condições de seleção de backend — CPU versus GPU versus OpenVINO — foram eliminadas. O mesmo artefato de compilação é implantado em um laptop de desenvolvedor, um executor de CI, um contêiner Linux e uma VM na nuvem sem qualquer seleção de pacote específica do ambiente ou ramificação de inicialização. As imagens Docker encolhem porque não há arquivos de modelo para COPY e nenhum pacote de runtime de plataforma para instalar.
Pipelines de Arquivo de Documentos Não Requerem Mais Uma Segunda Biblioteca. result.SaveAsSearchablePdf() elimina a dependência da biblioteca PDF que a maioria das equipes do PaddleSharp adicionou para produzir arquivos pesquisáveis. A verificação OCR e a gravação do PDF pesquisável são feitas em uma única chamada de API. Para equipes que processam milhares de documentos digitalizados por dia, essa simplificação elimina toda uma classe de conflitos de versões entre bibliotecas. A postagem do blog sobre PDFs pesquisáveis aborda considerações sobre escala de produção.
Decisões de Tempo de Vida do Serviço Refletem Requisitos da Aplicação, Não Restrições da Biblioteca. IronTesseract tem construção leve. O padrão singleton forçado, impulsionado pelo custoso carregamento de modelos do PaddleSharp, não é mais necessário. Não ASP.NET Core, os serviços podem ser definidos por escopo de solicitação, criando um isolamento mais claro entre usuários simultâneos e eliminando preocupações com threads de estado compartilhado. Para obter mais informações sobre opções de implantação, consulte a página de casos de uso de OCR do ASP.NET .
A expansão de idiomas é uma instalação de pacote, não um projeto de pesquisa. O catálogo com mais de 125 idiomas abrange scripts europeus, asiáticos, do Oriente Médio e especializados, disponíveis como pacotes NuGet . Adicionar francês, alemão, árabe ou japonês a um pipeline que começou como apenas chinês é dotnet add package IronOcr.Languages.French e uma linha de configuração. Sem busca de arquivos de modelo, sem pesquisa de disponibilidade upstream, sem implantação manual de arquivos.
O Pré-processamento Faz Parte da API OCR. O conhecimento do OpenCV que o pré-processamento PaddleSharp exigia — entender núcleos de filtro, gerenciar descarte Mat, selecionar parâmetros de limiar adaptativo — não é mais um pré-requisito para o trabalho OCR. OcrInput fornece operações nomeadas com padrão sensato. Equipes que não eram especialistas em OpenCV, mas que mantinham o código de pré-processamento do OpenCV, podem excluir esse código sem substituí-lo. A página de recursos de pré-processamento lista todos os filtros disponíveis, com documentação sobre quando aplicar cada um.
Perguntas frequentes
Por que devo migrar do PaddleSharp OCR para o IronOCR?
Entre os principais motivos, incluem-se a eliminação da complexidade da interoperabilidade COM, a substituição do gerenciamento de licenças baseado em arquivos, a eliminação da cobrança por página, a viabilização da implantação em Docker/contêineres e a adoção de um fluxo de trabalho nativo do NuGet que se integra às ferramentas padrão do .NET.
Quais são as principais alterações de código ao migrar do PaddleSharp OCR para o IronOCR?
Substitua as sequências de inicialização do PaddleSharp pela instanciação do IronTesseract, remova o gerenciamento do ciclo de vida COM (padrões explícitos de Criação/Carregamento/Fechamento) e atualize os nomes das propriedades de resultado. O resultado é uma redução significativa no número de linhas de código repetitivo.
Como faço para instalar o IronOCR para iniciar a migração?
Execute 'Install-Package IronOcr' no Console do Gerenciador de Pacotes ou 'dotnet add package IronOcr' na CLI. Os pacotes de idiomas são pacotes separados: 'dotnet add package IronOcr.Languages.French' para francês, por exemplo.
O IronOCR atinge a mesma precisão de OCR que o PaddleSharp OCR para documentos comerciais padrão?
O IronOCR alcança alta precisão para conteúdo comercial padrão, incluindo faturas, contratos, recibos e formulários digitados. Filtros de pré-processamento de imagem (correção de distorção, remoção de ruído, aprimoramento de contraste) melhoram ainda mais o reconhecimento em entradas de baixa qualidade.
Como o IronOCR lida com os dados de idioma que o PaddleSharp OCR instala separadamente?
Os dados de idioma no IronOCR são distribuídos como pacotes NuGet. O comando 'dotnet add package IronOcr.Languages.German' instala o suporte ao alemão. Não é necessário inserir arquivos manualmente nem configurar caminhos de diretório.
A migração do PaddleSharp OCR para o IronOCR exige alterações na infraestrutura de implantação?
O IronOCR requer menos alterações de infraestrutura do que o PaddleSharp OCR. Não há caminhos binários do SDK, necessidade de instalar arquivos de licença ou configurar servidores de licença. O pacote NuGet contém o mecanismo OCR completo e a chave de licença é uma string definida no código do aplicativo.
Como configuro o licenciamento do IronOCR após a migração?
Atribua `IronOcr.License.LicenseKey = "YOUR-KEY"` no código de inicialização do aplicativo. No Docker ou Kubernetes, armazene a chave como uma variável de ambiente e leia-a na inicialização. Use `License.IsValidLicense` para validar a licença antes de aceitar o tráfego.
O IronOCR consegue processar PDFs da mesma forma que o PaddleSharp?
Sim. O IronOCR lê PDFs nativos e digitalizados. Instancie o IronTesseract, chame ocr.Read(input) onde input é um caminho para um PDF ou OcrPdfInput, e itere pelas páginas do OcrResult. Não é necessário um pipeline de renderização de PDF separado.
Como o IronOCR lida com multithreading em processamento de alto volume?
O IronTesseract pode ser instanciado com segurança por thread. Crie uma instância por thread em um Parallel.ForEach ou pool de Tasks, execute o OCR simultaneamente e descarte cada instância ao terminar. Não é necessário nenhum estado global ou bloqueio.
Quais formatos de saída o IronOCR suporta após a extração de texto?
O IronOCR retorna resultados estruturados, incluindo texto, coordenadas de palavras, níveis de confiança e estrutura da página. As opções de exportação incluem texto simples, PDF pesquisável e objetos de resultados estruturados para processamento posterior.
O preço do IronOCR é mais previsível do que o do PaddleSharp OCR para cargas de trabalho escaláveis?
O IronOCR utiliza licenciamento perpétuo com preço fixo, sem cobranças por página ou volume. Independentemente de você processar 10.000 ou 10 milhões de páginas, o custo da licença permanece constante. As opções de licenciamento por volume e para equipes estão disponíveis na página de preços do IronOCR.
O que acontece com meus testes existentes após a migração do PaddleSharp OCR para o IronOCR?
Os testes que verificam o conteúdo de texto extraído devem continuar a ser aprovados após a migração. Os testes que validam padrões de chamadas de API ou o ciclo de vida de objetos COM precisarão ser atualizados para refletir o modelo de inicialização e resultados mais simples do IronOCR.

