Como melhorar a qualidade da imagem para obter melhores resultados de OCR usando C#
Este guia orienta os desenvolvedores .NET através de uma migração completa da família de pacotes Sdcb.PaddleOCR para IronOCR. Abrange todo o processo de substituição: remoção do conjunto de vários pacotes PaddlePaddle, eliminação do gerenciamento de arquivos de modelo e da configuração de GPU, e substituição do pipeline de inferência dependente do OpenCV por uma única instalação do NuGet . Cada seção é independente — não é necessário ler o artigo comparativo previamente.
Por que migrar do PaddleOCR?
O wrapper Sdcb.PaddleOCR é uma ponte mantida pela comunidade entre o ecossistema de aprendizado profundo Python da PaddlePaddle e .NET. Ele cumpre a função, mas carrega consigo todo o peso dessa ponte — arquivos de modelo, binários de inferência nativos, OpenCV para carregamento de imagens e infraestrutura CUDA opcional. Para a maioria das cargas de trabalho de OCR em .NET , essa é uma infraestrutura que o projeto nunca precisou.
Três diretórios de modelos antes da leitura de um único caractere. O pipeline de inferência do PaddleOCR encadeia três redes neurais: um modelo de detecção, um modelo de classificação de direção e um modelo de reconhecimento. Cada rede é um diretório separado de arquivos .pdmodel e .pdiparams que devem existir no disco antes de new PaddleOcrAll(models) compilar para um motor funcional. Se esses arquivos chegarem via download assíncrono — que se conecta ao armazenamento bj.bcebos.com da Baidu na China — ou via uma árvore de diretório models/ mantida manualmente, o desenvolvedor é permanentemente responsável pela versão do modelo. Quando Sdcb.PaddleOCR atualiza, os modelos pré-baixados da versão anterior podem exigir novo download. O IronOCR não possui arquivos de modelo, diretórios de modelo ou problemas de sincronização de versão. O mecanismo está incluído no pacote NuGet .
OpenCV Não É Opcional. Não há caminho de um caminho de arquivo para inferência PaddleOCR que ignore o OpenCvSharp. Toda imagem, independentemente do formato, deve passar por Cv2.ImRead(path) para se tornar um objeto Mat antes que ocr.Run(mat) possa aceitá-la. Isso significa dois pacotes NuGet extras (OpenCvSharp4 e OpenCvSharp4.runtime.win), DLLs nativas específicas da plataforma na saída de implantação e uma linha apt-get install libopencv-dev no Dockerfile.IronOCR aceita caminhos de arquivos, streams, arrays de bytes e System.Drawing.Bitmap diretamente. O intermediário Mat não existe.
A configuração da GPU é um projeto que leva vários dias. Os números de desempenho da GPU anunciados pelo PaddleOCR — 50-100 ms por imagem contra 300-500 ms na CPU — são reais. Para isso, são necessários drivers NVIDIA em uma versão específica, CUDA Toolkit 11.8 (não 12.x), cuDNN 8.6+ instalado nos locais corretos do PATH e um pacote NuGet de tempo de execução da GPU separado. Não Docker, a imagem base deve ser nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04 e o host deve ter nvidia-container-toolkit instalado. Equipes sem infraestrutura de GPU existente gastam de 2 a 8 horas na configuração do CUDA por ambiente. O IronOCR foi projetado para inferência por CPU, oferece um tempo de processamento de 150 a 300 ms por imagem em hardware padrão e não requer nenhuma configuração de GPU.
Artefatos de Implantação São 4-6x Maiores. A saída de implantação do PaddleOCR inclui paddle_inference.dll (~200MB), paddle2onnx.dll (~5MB), arquivos opencv_world*.dll (combinados ~50MB) e os diretórios de modelo (~21MB). Uma imagem Docker tem aproximadamente 1,5 GB. Uma instalação do IronOCR tem um tamanho total aproximado de 80 MB; A imagem Docker tem aproximadamente 400 MB. A diferença reside no CI/CD: cada execução de pipeline que restaura pacotes NuGet deve baixar modelos do Baidu ou obtê-los de uma camada de cache mantida separadamente.
Sem saída em PDF pesquisável. O PaddleOCR retorna regiões de texto de imagens e não possui mecanismo para incorporar o texto reconhecido de volta em um PDF como uma camada pesquisável. Criar um PDF pesquisável a partir da saída do PaddleOCR requer uma biblioteca PDF de terceiros, injeção de camadas de texto página por página e remapeamento de coordenadas.IronOCR produz um PDF totalmente pesquisável com uma linha: result.SaveAsSearchablePdf("output.pdf").
O problema fundamental
O PaddleOCR requer a configuração de três diretórios de modelos antes que a inferência possa começar:
// PaddleOCR: three model directories, all must exist and match the wrapper version
FullOcrModel models = new FullOcrModel(
LocalDetectionModel.FromDirectory("models/ch_PP-OCRv4_det_infer"), // ~5MB
LocalClassificationModel.FromDirectory("models/ch_ppocr_mobile_v2.0_cls_infer"), // ~2MB
LocalRecognitionModel.FromDirectory("models/ch_PP-OCRv4_rec_infer") // ~15MB
);
using PaddleOcrAll ocr = new PaddleOcrAll(models);
using Mat mat = Cv2.ImRead("document.png"); // OpenCvSharp required for every image
PaddleOcrResult result = ocr.Run(mat);
// PaddleOCR: three model directories, all must exist and match the wrapper version
FullOcrModel models = new FullOcrModel(
LocalDetectionModel.FromDirectory("models/ch_PP-OCRv4_det_infer"), // ~5MB
LocalClassificationModel.FromDirectory("models/ch_ppocr_mobile_v2.0_cls_infer"), // ~2MB
LocalRecognitionModel.FromDirectory("models/ch_PP-OCRv4_rec_infer") // ~15MB
);
using PaddleOcrAll ocr = new PaddleOcrAll(models);
using Mat mat = Cv2.ImRead("document.png"); // OpenCvSharp required for every image
PaddleOcrResult result = ocr.Run(mat);
Imports OpenCvSharp
Imports PaddleOCR
' PaddleOCR: three model directories, all must exist and match the wrapper version
Dim models As New FullOcrModel(
LocalDetectionModel.FromDirectory("models/ch_PP-OCRv4_det_infer"), ' ~5MB
LocalClassificationModel.FromDirectory("models/ch_ppocr_mobile_v2.0_cls_infer"), ' ~2MB
LocalRecognitionModel.FromDirectory("models/ch_PP-OCRv4_rec_infer") ' ~15MB
)
Using ocr As New PaddleOcrAll(models)
Using mat As Mat = Cv2.ImRead("document.png") ' OpenCvSharp required for every image
Dim result As PaddleOcrResult = ocr.Run(mat)
End Using
End Using
O IronOCR não possui arquivos de modelo, diretórios de modelo ou dependência do OpenCV:
// IronOCR: one package, zero model management
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("document.png");
var result = ocr.Read(input);
Console.WriteLine(result.Text);
// IronOCR: one package, zero model management
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("document.png");
var result = ocr.Read(input);
Console.WriteLine(result.Text);
Imports IronOcr
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadImage("document.png")
Dim result = ocr.Read(input)
Console.WriteLine(result.Text)
End Using
IronOCR vs PaddleOCR (.NET): Comparação de Recursos
A tabela abaixo abrange as dimensões mais importantes durante o planejamento da migração.
| Recurso | PaddleOCR (Sdcb) | IronOCR |
|---|---|---|
| Pacotes NuGet necessários | 4-5 | 1 |
| Arquivos de modelo necessários | Sim (3 diretórios, ~21 MB) | Não (incluído no pacote) |
| Fonte para download do modelo | Servidores Baidu (bj.bcebos.com) | Restauração do NuGet (Iron Software) |
| Dependência do OpenCV | Requerido (OpenCvSharp4) | None |
| Entrada de imagem | Via Mat mat = Cv2.ImRead() |
Caminho direto do arquivo, fluxo, matriz de bytes |
| Entrada nativa de PDF | Não | Sim (input.LoadPdf()) |
| Saída em PDF pesquisável | Não | Sim (result.SaveAsSearchablePdf()) |
| Entrada TIFF multiframe | Loop manual por quadro | input.LoadImageFrames() |
| suporte a GPU | Sim (CUDA 11.8 + cuDNN necessários) | Otimizado para CPU (não requer GPU) |
| Pré-processamento integrado | Não (a rede neural lida com distorção/ruído) | Sim (Corrigir distorção, Reduzir ruído, Contraste, Binarizar, Nitidez) |
| Idiomas suportados | 14 | 125+ |
| Método de instalação do idioma | DownloadAsync() por modelo de linguagem |
dotnet add package IronOcr.Languages.* |
| Simultaneidade multilíngue | Não (modelo separado por idioma) | Sim (OcrLanguage.English + OcrLanguage.French) |
| Saída estruturada | result.Regions (espacial, não ordenado) |
Páginas, Parágrafos, Linhas, Palavras, Caracteres |
| Pontuação de confiança | Flutuação por região (0-1) | Percentagem por palavra (0-100) |
| Leitura de código de barras | Não | Sim (ocr.Configuration.ReadBarCodes = true) |
| Exportação hOCR | Não | Sim |
| Tamanho da implantação | 300-500 MB | ~80MB |
| tamanho da imagem Docker | ~1,5 GB (com CUDA base) | ~400MB |
| Tempo de partida a frio | 3-5 segundos (carregamento do modelo) | Menos de 1 segundo |
| Multiplataforma | Windows, Linux (parcial) | Windows, Linux, macOS, Docker, Azure, AWS |
| Compatibilidade com .NET | .NET 6+ (wrapper da comunidade) | .NET Framework 4.6.2+, .NET 5/6/7/8/9 |
| Suporte comercial | Comunidade / Problemas do GitHub | Sim (Iron Software, com SLA) |
| Licença | Apache 2.0 (gratuito) | Perpetual ($999 Lite / $1,499 Pro / $2,999 Enterprise) |
Guia de Início Rápido: Migração do PaddleOCR (.NET) para o IronOCR
Passo 1: Substituir pacotes NuGet
Remova todos os cinco pacotes relacionados ao PaddleOCR:
dotnet remove package Sdcb.PaddleOCR
dotnet remove package Sdcb.PaddleOCR.Models.Online
dotnet remove package Sdcb.PaddleInference.runtime.win64.mkl
dotnet remove package OpenCvSharp4
dotnet remove package OpenCvSharp4.runtime.win
dotnet remove package Sdcb.PaddleOCR
dotnet remove package Sdcb.PaddleOCR.Models.Online
dotnet remove package Sdcb.PaddleInference.runtime.win64.mkl
dotnet remove package OpenCvSharp4
dotnet remove package OpenCvSharp4.runtime.win
Se o ambiente de execução da GPU estiver instalado, remova-o também:
dotnet remove package Sdcb.PaddleInference.runtime.win64.cuda118
dotnet remove package Sdcb.PaddleInference.runtime.win64.cuda118
Instale o IronOCR a partir da página de pacotes NuGet :
dotnet add package IronOcr
Etapa 2: Atualizar Namespaces
Substitua todas as importações de namespace do PaddleOCR e do OpenCvSharp:
// Before (PaddleOCR)
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using Sdcb.PaddleOCR.Models.Online;
using Sdcb.PaddleInference;
using OpenCvSharp;
// After (IronOCR)
using IronOcr;
// Before (PaddleOCR)
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using Sdcb.PaddleOCR.Models.Online;
using Sdcb.PaddleInference;
using OpenCvSharp;
// After (IronOCR)
using IronOcr;
Imports IronOcr
Imports Sdcb.PaddleOCR
Imports Sdcb.PaddleOCR.Models
Imports Sdcb.PaddleOCR.Models.Online
Imports Sdcb.PaddleInference
Imports OpenCvSharp
Etapa 3: Inicializar a licença
Adicione a inicialização da licença uma vez ao iniciar o aplicativo, antes de qualquer instância IronTesseract ser criada:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Uma chave de avaliação gratuita está disponível na página de licenciamento do IronOCR . A versão de avaliação gera imagens com marca d'água e permite testar todos os recursos antes da compra.
Exemplos de migração de código
Eliminação da configuração do caminho do modelo local
Projetos que fazem o download prévio de arquivos de modelo do PaddleOCR para evitar conexões com o servidor Baidu em tempo de execução devem configurar três caminhos de diretório separados. Essa configuração deve ser atualizada sempre que a versão do wrapper for alterada.
Abordagem PaddleOCR:
// Local model configuration — developer owns the directory structure
// Each wrapper update may require re-downloading model files
string modelsRoot = Path.Combine(AppContext.BaseDirectory, "models");
FullOcrModel models = new FullOcrModel(
LocalDetectionModel.FromDirectory(
Path.Combine(modelsRoot, "ch_PP-OCRv4_det_infer")),
LocalClassificationModel.FromDirectory(
Path.Combine(modelsRoot, "ch_ppocr_mobile_v2.0_cls_infer")),
LocalRecognitionModel.FromDirectory(
Path.Combine(modelsRoot, "ch_PP-OCRv4_rec_infer"))
);
// Fails at runtime if any of the three directories is missing or stale
using PaddleOcrAll ocr = new PaddleOcrAll(models)
{
AllowRotateDetection = true,
Enable180Classification = true
};
using Mat mat = Cv2.ImRead("document.png");
PaddleOcrResult result = ocr.Run(mat);
Console.WriteLine(result.Text);
// Local model configuration — developer owns the directory structure
// Each wrapper update may require re-downloading model files
string modelsRoot = Path.Combine(AppContext.BaseDirectory, "models");
FullOcrModel models = new FullOcrModel(
LocalDetectionModel.FromDirectory(
Path.Combine(modelsRoot, "ch_PP-OCRv4_det_infer")),
LocalClassificationModel.FromDirectory(
Path.Combine(modelsRoot, "ch_ppocr_mobile_v2.0_cls_infer")),
LocalRecognitionModel.FromDirectory(
Path.Combine(modelsRoot, "ch_PP-OCRv4_rec_infer"))
);
// Fails at runtime if any of the three directories is missing or stale
using PaddleOcrAll ocr = new PaddleOcrAll(models)
{
AllowRotateDetection = true,
Enable180Classification = true
};
using Mat mat = Cv2.ImRead("document.png");
PaddleOcrResult result = ocr.Run(mat);
Console.WriteLine(result.Text);
Imports System
Imports System.IO
' Local model configuration — developer owns the directory structure
' Each wrapper update may require re-downloading model files
Dim modelsRoot As String = Path.Combine(AppContext.BaseDirectory, "models")
Dim models As New FullOcrModel(
LocalDetectionModel.FromDirectory(
Path.Combine(modelsRoot, "ch_PP-OCRv4_det_infer")),
LocalClassificationModel.FromDirectory(
Path.Combine(modelsRoot, "ch_ppocr_mobile_v2.0_cls_infer")),
LocalRecognitionModel.FromDirectory(
Path.Combine(modelsRoot, "ch_PP-OCRv4_rec_infer"))
)
' Fails at runtime if any of the three directories is missing or stale
Using ocr As New PaddleOcrAll(models) With {
.AllowRotateDetection = True,
.Enable180Classification = True
}
Using mat As Mat = Cv2.ImRead("document.png")
Dim result As PaddleOcrResult = ocr.Run(mat)
Console.WriteLine(result.Text)
End Using
End Using
Abordagem IronOCR:
// Não model directories, no path configuration, no version matching
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("document.png");
var result = ocr.Read(input);
Console.WriteLine(result.Text);
// Não model directories, no path configuration, no version matching
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("document.png");
var result = ocr.Read(input);
Console.WriteLine(result.Text);
Imports IronOcr
' Não model directories, no path configuration, no version matching
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadImage("document.png")
Dim result = ocr.Read(input)
Console.WriteLine(result.Text)
End Using
A árvore de diretórios models/, as três chamadas FromDirectory() e a preocupação de sincronização de versão desaparecem. O mecanismo IronOCR está incluído no pacote NuGet durante a restauração e não requer resolução de caminho em tempo de execução. Veja o guia de configuração do IronTesseract para opções de inicialização, incluindo o posicionamento da chave de licença em appsettings.json.
Consolidação de pipeline de detecção e reconhecimento em dois estágios
O pipeline de rotação e orientação do PaddleOCR é configurado através de propriedades em PaddleOcrAll. Replicar esse comportamento no IronOCR usa os métodos de pré-processamento OcrInput, que lidam com os mesmos problemas de documento com uma superfície de chamada mais simples.
Abordagem PaddleOCR:
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models.Online;
using OpenCvSharp;
// Separate async initialization step — blocks startup for 3-5 seconds on cold run
FullOcrModel models = await OnlineFullModels.EnglishV4.DownloadAsync();
using PaddleOcrAll ocr = new PaddleOcrAll(models)
{
AllowRotateDetection = true, // Enables 0/90/180/270 degree rotation detection
Enable180Classification = true // Additional pass for upside-down text
};
// OpenCV Mat required — no direct file path support
using Mat mat = Cv2.ImRead("rotated-scan.png");
if (mat.Empty())
{
throw new FileNotFoundException("Image could not be loaded by OpenCvSharp");
}
// Three neural network passes: detection → classification → recognition
PaddleOcrResult result = ocr.Run(mat);
// Regions arrive in spatial order, not reading order
// Manual sort required for top-to-bottom, left-to-right output
var orderedRegions = result.Regions
.OrderBy(r => r.Rect.Center.Y)
.ThenBy(r => r.Rect.Center.X);
foreach (var region in orderedRegions)
{
Console.WriteLine($"{region.Text} (confidence: {region.Score:P1})");
}
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models.Online;
using OpenCvSharp;
// Separate async initialization step — blocks startup for 3-5 seconds on cold run
FullOcrModel models = await OnlineFullModels.EnglishV4.DownloadAsync();
using PaddleOcrAll ocr = new PaddleOcrAll(models)
{
AllowRotateDetection = true, // Enables 0/90/180/270 degree rotation detection
Enable180Classification = true // Additional pass for upside-down text
};
// OpenCV Mat required — no direct file path support
using Mat mat = Cv2.ImRead("rotated-scan.png");
if (mat.Empty())
{
throw new FileNotFoundException("Image could not be loaded by OpenCvSharp");
}
// Three neural network passes: detection → classification → recognition
PaddleOcrResult result = ocr.Run(mat);
// Regions arrive in spatial order, not reading order
// Manual sort required for top-to-bottom, left-to-right output
var orderedRegions = result.Regions
.OrderBy(r => r.Rect.Center.Y)
.ThenBy(r => r.Rect.Center.X);
foreach (var region in orderedRegions)
{
Console.WriteLine($"{region.Text} (confidence: {region.Score:P1})");
}
Imports Sdcb.PaddleOCR
Imports Sdcb.PaddleOCR.Models.Online
Imports OpenCvSharp
' Separate async initialization step — blocks startup for 3-5 seconds on cold run
Dim models As FullOcrModel = Await OnlineFullModels.EnglishV4.DownloadAsync()
Using ocr As New PaddleOcrAll(models) With {
.AllowRotateDetection = True, ' Enables 0/90/180/270 degree rotation detection
.Enable180Classification = True ' Additional pass for upside-down text
}
' OpenCV Mat required — no direct file path support
Using mat As Mat = Cv2.ImRead("rotated-scan.png")
If mat.Empty() Then
Throw New FileNotFoundException("Image could not be loaded by OpenCvSharp")
End If
' Three neural network passes: detection → classification → recognition
Dim result As PaddleOcrResult = ocr.Run(mat)
' Regions arrive in spatial order, not reading order
' Manual sort required for top-to-bottom, left-to-right output
Dim orderedRegions = result.Regions _
.OrderBy(Function(r) r.Rect.Center.Y) _
.ThenBy(Function(r) r.Rect.Center.X)
For Each region In orderedRegions
Console.WriteLine($"{region.Text} (confidence: {region.Score:P1})")
Next
End Using
End Using
Abordagem IronOCR:
using IronOcr;
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("rotated-scan.png");
input.Deskew(); // Corrects rotation and skew automatically
var result = ocr.Read(input);
// Output is already in reading order — no sort needed
foreach (var page in result.Pages)
{
foreach (var line in page.Lines)
{
Console.WriteLine($"{line.Text} (confidence: {line.Confidence}%)");
}
}
using IronOcr;
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("rotated-scan.png");
input.Deskew(); // Corrects rotation and skew automatically
var result = ocr.Read(input);
// Output is already in reading order — no sort needed
foreach (var page in result.Pages)
{
foreach (var line in page.Lines)
{
Console.WriteLine($"{line.Text} (confidence: {line.Confidence}%)");
}
}
Imports IronOcr
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadImage("rotated-scan.png")
input.Deskew() ' Corrects rotation and skew automatically
Dim result = ocr.Read(input)
' Output is already in reading order — no sort needed
For Each page In result.Pages
For Each line In page.Lines
Console.WriteLine($"{line.Text} (confidence: {line.Confidence}%)")
Next
Next
End Using
O método Deskew()IronOCR lida com a detecção de rotação como parte do pipeline de pré-processamento. A coleção Lines do resultado é entregue em ordem de leitura pelo motor de layout Tesseract, eliminando o padrão de ordenação manual. O guia de correção de orientação de imagem documenta toda a gama de opções de rotação e correção de inclinação.
Remoção da seleção de dispositivos de GPU e CPU
Aplicações PaddleOCR que executam inferência em GPU têm a maior superfície de migração: o pacote NuGet de runtime da GPU, os pré-requisitos de ambiente CUDA/cuDNN e a chamada de configuração PaddleDevice.Gpu(). Tudo isso é removido durante a migração.
Abordagem PaddleOCR:
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models.Online;
using Sdcb.PaddleInference; // GPU configuration namespace
using OpenCvSharp;
// Prerequisites must exist on every deployment environment:
// - NVIDIA Driver 452.39+ (Windows) / 450.80.02+ (Linux)
// - CUDA Toolkit 11.8 (not 12.x — version must match exactly)
// - cuDNN 8.6.0+ placed in CUDA bin directory
// - dotnet add package Sdcb.PaddleInference.runtime.win64.cuda118
FullOcrModel models = await OnlineFullModels.ChineseV4.DownloadAsync();
// GPU device 0, 1000MB initial memory pool
// Throws native load exception if CUDA_PATH not set or cuDNN DLL missing
using PaddleOcrAll ocr = new PaddleOcrAll(models, PaddleDevice.Gpu(deviceId: 0))
{
AllowRotateDetection = true,
Enable180Classification = true
};
using Mat mat = Cv2.ImRead("scanned-batch.png");
PaddleOcrResult result = ocr.Run(mat);
Console.WriteLine($"Text regions: {result.Regions.Length}");
Console.WriteLine(result.Text);
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models.Online;
using Sdcb.PaddleInference; // GPU configuration namespace
using OpenCvSharp;
// Prerequisites must exist on every deployment environment:
// - NVIDIA Driver 452.39+ (Windows) / 450.80.02+ (Linux)
// - CUDA Toolkit 11.8 (not 12.x — version must match exactly)
// - cuDNN 8.6.0+ placed in CUDA bin directory
// - dotnet add package Sdcb.PaddleInference.runtime.win64.cuda118
FullOcrModel models = await OnlineFullModels.ChineseV4.DownloadAsync();
// GPU device 0, 1000MB initial memory pool
// Throws native load exception if CUDA_PATH not set or cuDNN DLL missing
using PaddleOcrAll ocr = new PaddleOcrAll(models, PaddleDevice.Gpu(deviceId: 0))
{
AllowRotateDetection = true,
Enable180Classification = true
};
using Mat mat = Cv2.ImRead("scanned-batch.png");
PaddleOcrResult result = ocr.Run(mat);
Console.WriteLine($"Text regions: {result.Regions.Length}");
Console.WriteLine(result.Text);
Imports Sdcb.PaddleOCR
Imports Sdcb.PaddleOCR.Models.Online
Imports Sdcb.PaddleInference ' GPU configuration namespace
Imports OpenCvSharp
' Prerequisites must exist on every deployment environment:
' - NVIDIA Driver 452.39+ (Windows) / 450.80.02+ (Linux)
' - CUDA Toolkit 11.8 (not 12.x — version must match exactly)
' - cuDNN 8.6.0+ placed in CUDA bin directory
' - dotnet add package Sdcb.PaddleInference.runtime.win64.cuda118
Dim models As FullOcrModel = Await OnlineFullModels.ChineseV4.DownloadAsync()
' GPU device 0, 1000MB initial memory pool
' Throws native load exception if CUDA_PATH not set or cuDNN DLL missing
Using ocr As New PaddleOcrAll(models, PaddleDevice.Gpu(deviceId:=0)) With {
.AllowRotateDetection = True,
.Enable180Classification = True
}
Using mat As Mat = Cv2.ImRead("scanned-batch.png")
Dim result As PaddleOcrResult = ocr.Run(mat)
Console.WriteLine($"Text regions: {result.Regions.Length}")
Console.WriteLine(result.Text)
End Using
End Using
Abordagem IronOCR:
A abordagem IronOCR é idêntica ao exemplo acima — IronTesseract lida com esse cenário com a mesma chamada de API. Não são necessários pacotes de GPU, pré-requisitos CUDA ou seleção de dispositivo. Substitua new PaddleOcrAll(models, PaddleDevice.Gpu(deviceId: 0)) por new IronTesseract() e remova toda a configuração relacionada à GPU.
O IronOCR oferece um tempo de processamento de 150 a 300 ms por imagem na CPU — mais rápido que o PaddleOCR na CPU (300 a 500 ms) e suficiente para a maioria das cargas de trabalho de APIs web e pipelines de documentos, sem a necessidade de infraestrutura de GPU. Para cenários de alto desempenho, o guia de otimização de velocidade abrange opções de configuração, incluindo gerenciamento de threads e ajuste do modo de segmentação de páginas.
Extração de dados de documentos estruturados
O PaddleOCR retorna um array plano de objetos PaddleOcrResultRegion ordenados espacialmente, não por fluxo de leitura. A extração da estrutura em nível de parágrafo ou de linha requer lógica de agrupamento manual baseada na proximidade da caixa delimitadora. O IronOCR fornece uma árvore de resultados hierárquica com ordem de leitura garantida.
Abordagem PaddleOCR:
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models.Online;
using OpenCvSharp;
using System.Collections.Generic;
FullOcrModel models = await OnlineFullModels.EnglishV4.DownloadAsync();
using PaddleOcrAll ocr = new PaddleOcrAll(models);
using Mat mat = Cv2.ImRead("invoice.png");
PaddleOcrResult result = ocr.Run(mat);
// Não paragraph or line grouping — must implement manually
// Group regions into lines by proximity on the Y axis
var lineGroups = new Dictionary<int, List<PaddleOcrResultRegion>>();
foreach (var region in result.Regions)
{
// Round Y center to nearest 15 pixels to approximate line grouping
int lineKey = (int)(region.Rect.Center.Y / 15) * 15;
if (!lineGroups.ContainsKey(lineKey))
lineGroups[lineKey] = new List<PaddleOcrResultRegion>();
lineGroups[lineKey].Add(region);
}
// Sort lines top to bottom, then regions left to right within each line
foreach (var line in lineGroups.OrderBy(kv => kv.Key))
{
var lineText = string.Join(" ", line.Value
.OrderBy(r => r.Rect.Center.X)
.Select(r => r.Text));
Console.WriteLine(lineText);
}
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models.Online;
using OpenCvSharp;
using System.Collections.Generic;
FullOcrModel models = await OnlineFullModels.EnglishV4.DownloadAsync();
using PaddleOcrAll ocr = new PaddleOcrAll(models);
using Mat mat = Cv2.ImRead("invoice.png");
PaddleOcrResult result = ocr.Run(mat);
// Não paragraph or line grouping — must implement manually
// Group regions into lines by proximity on the Y axis
var lineGroups = new Dictionary<int, List<PaddleOcrResultRegion>>();
foreach (var region in result.Regions)
{
// Round Y center to nearest 15 pixels to approximate line grouping
int lineKey = (int)(region.Rect.Center.Y / 15) * 15;
if (!lineGroups.ContainsKey(lineKey))
lineGroups[lineKey] = new List<PaddleOcrResultRegion>();
lineGroups[lineKey].Add(region);
}
// Sort lines top to bottom, then regions left to right within each line
foreach (var line in lineGroups.OrderBy(kv => kv.Key))
{
var lineText = string.Join(" ", line.Value
.OrderBy(r => r.Rect.Center.X)
.Select(r => r.Text));
Console.WriteLine(lineText);
}
Imports Sdcb.PaddleOCR
Imports Sdcb.PaddleOCR.Models.Online
Imports OpenCvSharp
Imports System.Collections.Generic
Dim models As FullOcrModel = Await OnlineFullModels.EnglishV4.DownloadAsync()
Using ocr As New PaddleOcrAll(models)
Using mat As Mat = Cv2.ImRead("invoice.png")
Dim result As PaddleOcrResult = ocr.Run(mat)
' Não paragraph or line grouping — must implement manually
' Group regions into lines by proximity on the Y axis
Dim lineGroups As New Dictionary(Of Integer, List(Of PaddleOcrResultRegion))()
For Each region In result.Regions
' Round Y center to nearest 15 pixels to approximate line grouping
Dim lineKey As Integer = CInt(region.Rect.Center.Y / 15) * 15
If Not lineGroups.ContainsKey(lineKey) Then
lineGroups(lineKey) = New List(Of PaddleOcrResultRegion)()
End If
lineGroups(lineKey).Add(region)
Next
' Sort lines top to bottom, then regions left to right within each line
For Each line In lineGroups.OrderBy(Function(kv) kv.Key)
Dim lineText As String = String.Join(" ", line.Value _
.OrderBy(Function(r) r.Rect.Center.X) _
.Select(Function(r) r.Text))
Console.WriteLine(lineText)
Next
End Using
End Using
Abordagem IronOCR:
using IronOcr;
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("invoice.png");
var result = ocr.Read(input);
// Hierarchical structure: Pages → Paragraphs → Lines → Words → Characters
// All delivered in reading order by the layout engine
foreach (var page in result.Pages)
{
Console.WriteLine($"Page {page.PageNumber} — {page.Words.Count} words");
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($" Paragraph at ({paragraph.X}, {paragraph.Y}):");
Console.WriteLine($" {paragraph.Text}");
}
}
using IronOcr;
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("invoice.png");
var result = ocr.Read(input);
// Hierarchical structure: Pages → Paragraphs → Lines → Words → Characters
// All delivered in reading order by the layout engine
foreach (var page in result.Pages)
{
Console.WriteLine($"Page {page.PageNumber} — {page.Words.Count} words");
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($" Paragraph at ({paragraph.X}, {paragraph.Y}):");
Console.WriteLine($" {paragraph.Text}");
}
}
Imports IronOcr
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadImage("invoice.png")
Dim result = ocr.Read(input)
' Hierarchical structure: Pages → Paragraphs → Lines → Words → Characters
' All delivered in reading order by the layout engine
For Each page In result.Pages
Console.WriteLine($"Page {page.PageNumber} — {page.Words.Count} words")
For Each paragraph In page.Paragraphs
Console.WriteLine($" Paragraph at ({paragraph.X}, {paragraph.Y}):")
Console.WriteLine($" {paragraph.Text}")
Next
Next
End Using
A aproximação manual de agrupamento de linhas — arredondamento das coordenadas Y para o tamanho de um bucket de pixels — é substituída pela segmentação de parágrafos integrada ao mecanismo de layout do Tesseract. As coordenadas da caixa delimitadora estão disponíveis em todos os níveis da hierarquia através de paragraph.X, paragraph.Y, paragraph.Width, e paragraph.Height. Consulte o guia de resultados estruturados e o tutorial de leitura de texto em imagens para obter uma cobertura completa da árvore de resultados.
Geração de PDF pesquisável
O PaddleOCR não gera nenhum arquivo PDF como saída. Gerar um PDF pesquisável a partir dos resultados do PaddleOCR requer uma biblioteca PDF separada, mapeamento manual de coordenadas de region.Rect para unidades de página PDF e injeção de uma camada de texto invisível. O IronOCR gera um PDF pesquisável diretamente a partir do resultado do OCR.
Abordagem PaddleOCR:
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models.Online;
using OpenCvSharp;
// Requires additional package: PdfSharp, iTextSharp, or similar
// Manual coordinate remapping from OpenCV pixel space to PDF point space
FullOcrModel models = await OnlineFullModels.EnglishV4.DownloadAsync();
using PaddleOcrAll ocr = new PaddleOcrAll(models);
using Mat mat = Cv2.ImRead("scanned-page.png");
PaddleOcrResult paddleResult = ocr.Run(mat);
// Não built-in searchable PDF output — must build with external library
// region.Rect coordinates are in pixel space, PDF uses points (1 point = 1/72 inch)
// DPI conversion required for coordinate mapping
float dpiScale = 72.0f / 96.0f; // Assuming 96 DPI source image
// ... hundreds of lines of PDF construction code using external library ...
// This is permanent maintenance, not a one-time cost
Console.WriteLine("Searchable PDF output requires external PDF library and coordinate mapping.");
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models.Online;
using OpenCvSharp;
// Requires additional package: PdfSharp, iTextSharp, or similar
// Manual coordinate remapping from OpenCV pixel space to PDF point space
FullOcrModel models = await OnlineFullModels.EnglishV4.DownloadAsync();
using PaddleOcrAll ocr = new PaddleOcrAll(models);
using Mat mat = Cv2.ImRead("scanned-page.png");
PaddleOcrResult paddleResult = ocr.Run(mat);
// Não built-in searchable PDF output — must build with external library
// region.Rect coordinates are in pixel space, PDF uses points (1 point = 1/72 inch)
// DPI conversion required for coordinate mapping
float dpiScale = 72.0f / 96.0f; // Assuming 96 DPI source image
// ... hundreds of lines of PDF construction code using external library ...
// This is permanent maintenance, not a one-time cost
Console.WriteLine("Searchable PDF output requires external PDF library and coordinate mapping.");
Imports Sdcb.PaddleOCR
Imports Sdcb.PaddleOCR.Models.Online
Imports OpenCvSharp
' Requires additional package: PdfSharp, iTextSharp, or similar
' Manual coordinate remapping from OpenCV pixel space to PDF point space
Dim models As FullOcrModel = Await OnlineFullModels.EnglishV4.DownloadAsync()
Using ocr As New PaddleOcrAll(models)
Using mat As Mat = Cv2.ImRead("scanned-page.png")
Dim paddleResult As PaddleOcrResult = ocr.Run(mat)
End Using
End Using
' Não built-in searchable PDF output — must build with external library
' region.Rect coordinates are in pixel space, PDF uses points (1 point = 1/72 inch)
' DPI conversion required for coordinate mapping
Dim dpiScale As Single = 72.0F / 96.0F ' Assuming 96 DPI source image
' ... hundreds of lines of PDF construction code using external library ...
' This is permanent maintenance, not a one-time cost
Console.WriteLine("Searchable PDF output requires external PDF library and coordinate mapping.")
Abordagem IronOCR:
using IronOcr;
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("scanned-page.png");
input.Deskew();
input.DeNoise();
var result = ocr.Read(input);
// Searchable PDF in one line — no external PDF library, no coordinate mapping
result.SaveAsSearchablePdf("searchable-output.pdf");
Console.WriteLine($"Searchable PDF created. Confidence: {result.Confidence}%");
using IronOcr;
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("scanned-page.png");
input.Deskew();
input.DeNoise();
var result = ocr.Read(input);
// Searchable PDF in one line — no external PDF library, no coordinate mapping
result.SaveAsSearchablePdf("searchable-output.pdf");
Console.WriteLine($"Searchable PDF created. Confidence: {result.Confidence}%");
Imports IronOcr
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadImage("scanned-page.png")
input.Deskew()
input.DeNoise()
Dim result = ocr.Read(input)
' Searchable PDF in one line — no external PDF library, no coordinate mapping
result.SaveAsSearchablePdf("searchable-output.pdf")
Console.WriteLine($"Searchable PDF created. Confidence: {result.Confidence}%")
End Using
O problema de mapeamento de coordenadas — converter coordenadas de pixel do OpenCV para o espaço de pontos do PDF na DPI correta — não existe no IronOCR. O guia de PDF pesquisável aborda a saída de várias páginas, PDFs protegidos por senha e configurações de qualidade de saída. Para equipes que digitalizam arquivos digitalizados ou criam fluxos de trabalho de fax para PDF pesquisável, essa chamada de método única substitui o que, de outra forma, seria um projeto de integração substancial.
Processamento em lote de TIFF com múltiplos quadros
Arquivos TIFF com várias páginas aparecem com frequência em fluxos de trabalho de digitalização de documentos. O PaddleOCR não tem suporte direto para TIFF de múltiplos quadros — cada quadro deve ser extraído individualmente usando uma biblioteca de imagem externa e carregado como um Mat separado. O IronOCR processa arquivos TIFF com múltiplos quadros nativamente.
Abordagem PaddleOCR:
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models.Online;
using OpenCvSharp;
using System.Drawing; // For multi-frame TIFF extraction
using System.Drawing.Imaging;
using System.Text;
FullOcrModel models = await OnlineFullModels.EnglishV4.DownloadAsync();
using PaddleOcrAll ocr = new PaddleOcrAll(models);
var fullText = new StringBuilder();
// Must use System.Drawing to extract individual TIFF frames
// OpenCvSharp cannot enumerate TIFF frames directly
using var tiff = Image.FromFile("multipage-scan.tiff");
FrameDimension dimension = new FrameDimension(tiff.FrameDimensionsList[0]);
int frameCount = tiff.GetFrameCount(dimension);
for (int i = 0; i < frameCount; i++)
{
tiff.SelectActiveFrame(dimension, i);
// Save frame to temp file — OpenCvSharp needs a file path
string tempPath = Path.GetTempFileName() + ".png";
tiff.Save(tempPath, ImageFormat.Png);
try
{
using Mat mat = Cv2.ImRead(tempPath);
PaddleOcrResult result = ocr.Run(mat);
fullText.AppendLine($"=== Frame {i + 1} ===");
fullText.AppendLine(result.Text);
}
finally
{
File.Delete(tempPath); // Must clean up temp files
}
}
Console.WriteLine(fullText.ToString());
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models.Online;
using OpenCvSharp;
using System.Drawing; // For multi-frame TIFF extraction
using System.Drawing.Imaging;
using System.Text;
FullOcrModel models = await OnlineFullModels.EnglishV4.DownloadAsync();
using PaddleOcrAll ocr = new PaddleOcrAll(models);
var fullText = new StringBuilder();
// Must use System.Drawing to extract individual TIFF frames
// OpenCvSharp cannot enumerate TIFF frames directly
using var tiff = Image.FromFile("multipage-scan.tiff");
FrameDimension dimension = new FrameDimension(tiff.FrameDimensionsList[0]);
int frameCount = tiff.GetFrameCount(dimension);
for (int i = 0; i < frameCount; i++)
{
tiff.SelectActiveFrame(dimension, i);
// Save frame to temp file — OpenCvSharp needs a file path
string tempPath = Path.GetTempFileName() + ".png";
tiff.Save(tempPath, ImageFormat.Png);
try
{
using Mat mat = Cv2.ImRead(tempPath);
PaddleOcrResult result = ocr.Run(mat);
fullText.AppendLine($"=== Frame {i + 1} ===");
fullText.AppendLine(result.Text);
}
finally
{
File.Delete(tempPath); // Must clean up temp files
}
}
Console.WriteLine(fullText.ToString());
Imports Sdcb.PaddleOCR
Imports Sdcb.PaddleOCR.Models.Online
Imports OpenCvSharp
Imports System.Drawing ' For multi-frame TIFF extraction
Imports System.Drawing.Imaging
Imports System.Text
Dim models As FullOcrModel = Await OnlineFullModels.EnglishV4.DownloadAsync()
Using ocr As New PaddleOcrAll(models)
Dim fullText As New StringBuilder()
' Must use System.Drawing to extract individual TIFF frames
' OpenCvSharp cannot enumerate TIFF frames directly
Using tiff As Image = Image.FromFile("multipage-scan.tiff")
Dim dimension As New FrameDimension(tiff.FrameDimensionsList(0))
Dim frameCount As Integer = tiff.GetFrameCount(dimension)
For i As Integer = 0 To frameCount - 1
tiff.SelectActiveFrame(dimension, i)
' Save frame to temp file — OpenCvSharp needs a file path
Dim tempPath As String = Path.GetTempFileName() & ".png"
tiff.Save(tempPath, ImageFormat.Png)
Try
Using mat As Mat = Cv2.ImRead(tempPath)
Dim result As PaddleOcrResult = ocr.Run(mat)
fullText.AppendLine($"=== Frame {i + 1} ===")
fullText.AppendLine(result.Text)
End Using
Finally
File.Delete(tempPath) ' Must clean up temp files
End Try
Next
End Using
Console.WriteLine(fullText.ToString())
End Using
Abordagem IronOCR:
using IronOcr;
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImageFrames("multipage-scan.tiff"); // All frames in one call
var result = ocr.Read(input);
foreach (var page in result.Pages)
{
Console.WriteLine($"=== Frame {page.PageNumber} ===");
Console.WriteLine(page.Text);
}
// Optionally save the entire multi-frame result as searchable PDF
result.SaveAsSearchablePdf("multipage-searchable.pdf");
using IronOcr;
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImageFrames("multipage-scan.tiff"); // All frames in one call
var result = ocr.Read(input);
foreach (var page in result.Pages)
{
Console.WriteLine($"=== Frame {page.PageNumber} ===");
Console.WriteLine(page.Text);
}
// Optionally save the entire multi-frame result as searchable PDF
result.SaveAsSearchablePdf("multipage-searchable.pdf");
Imports IronOcr
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadImageFrames("multipage-scan.tiff") ' All frames in one call
Dim result = ocr.Read(input)
For Each page In result.Pages
Console.WriteLine($"=== Frame {page.PageNumber} ===")
Console.WriteLine(page.Text)
Next
' Optionally save the entire multi-frame result as searchable PDF
result.SaveAsSearchablePdf("multipage-searchable.pdf")
End Using
O loop de extração de quadros, a dependência System.Drawing, a criação de arquivo temporário e a lógica de limpeza são todas removidas.IronOCR carrega todos os quadros em uma única chamada LoadImageFrames() e expõe cada quadro como um Page no resultado. O guia de entrada TIFF e GIF aborda opções de carregamento de vários quadros, intervalos de quadros selecionáveis e considerações de memória para grandes arquivos TIFF.
Referência de mapeamento da API PaddleOCR (.NET) para IronOCR
| PaddleOCR (Sdcb) | IronOCR | Notas |
|---|---|---|
Sdcb.PaddleOCR |
IronOcr |
Espaço de nomes |
Sdcb.PaddleOCR.Models.Online |
N / D | Não é necessário nenhum namespace para aquisição de modelo. |
Sdcb.PaddleInference |
N / D | Não é necessário nenhum namespace de backend de inferência. |
FullOcrModel |
N / D | Não há equivalente — os modelos são agrupados. |
OnlineFullModels.ChineseV4.DownloadAsync() |
dotnet add package IronOcr.Languages.ChineseSimplified |
A aquisição de modelos foi substituída pelo NuGet. |
LocalDetectionModel.FromDirectory(path) |
N / D | Sem gerenciamento de caminho de modelo |
LocalClassificationModel.FromDirectory(path) |
N / D | Sem gerenciamento de caminho de modelo |
LocalRecognitionModel.FromDirectory(path) |
N / D | Sem gerenciamento de caminho de modelo |
new PaddleOcrAll(models) |
new IronTesseract() |
Instanciação do motor |
new PaddleOcrAll(models, PaddleDevice.Gpu(0)) |
N / D | A seleção de dispositivo GPU foi completamente removida. |
PaddleDevice.Cpu() |
N / D | O único modo é CPU; Nenhuma seleção necessária |
ocr.AllowRotateDetection = true |
input.Deskew() |
Correção de rotação |
ocr.Enable180Classification = true |
Automático | A detecção de posição invertida já está integrada. |
Cv2.ImRead(path) |
input.LoadImage(path) |
Carregamento de imagens — sem necessidade de OpenCV |
ocr.Run(mat) |
ocr.Read(input) |
Executar OCR |
result.Text |
result.Text |
Texto completo do documento |
result.Regions |
result.Pages[0].Lines ou .Words |
Regiões de texto estruturado |
region.Text |
word.Text / line.Text |
Conteúdo textual de uma região |
region.Score (float 0-1) |
word.Confidence (int 0-100) |
Valor de confiança — a escala difere |
region.Rect.Center.X |
word.X |
Posição horizontal |
region.Rect.Center.Y |
word.Y |
Posição vertical |
region.Rect.Size.Width |
word.Width |
Largura da caixa delimitadora |
region.Rect.Size.Height |
word.Height |
Altura da caixa delimitadora |
| N / D | input.LoadPdf(path) |
Entrada de PDF nativa (sem equivalente ao PaddleOCR) |
| N / D | input.LoadImageFrames(path) |
TIFF com múltiplos quadros (sem equivalente no PaddleOCR) |
| N / D | result.SaveAsSearchablePdf(path) |
Saída em PDF pesquisável (sem equivalente ao PaddleOCR) |
Problemas e soluções comuns em migrações
Problema 1: Incompatibilidade na Escala de Confiança
PaddleOCR: A confiança da região é um float de 0.0 a 1.0. Um limiar comum é region.Score >= 0.8 para filtrar detecções de baixa qualidade.
Solução: A confiança do IronOCR é um percentual int de 0 a 100. Multiplique o limiar do PaddleOCR por 100:
// PaddleOCR: filter at 0.8
var highConfidence = result.Regions.Where(r => r.Score >= 0.8);
//IronOCR equivalent: filter at 80
var highConfidence = result.Pages
.SelectMany(p => p.Words)
.Where(w => w.Confidence >= 80);
// PaddleOCR: filter at 0.8
var highConfidence = result.Regions.Where(r => r.Score >= 0.8);
//IronOCR equivalent: filter at 80
var highConfidence = result.Pages
.SelectMany(p => p.Words)
.Where(w => w.Confidence >= 80);
' PaddleOCR: filter at 0.8
Dim highConfidence = result.Regions.Where(Function(r) r.Score >= 0.8)
' IronOCR equivalent: filter at 80
Dim highConfidence = result.Pages _
.SelectMany(Function(p) p.Words) _
.Where(Function(w) w.Confidence >= 80)
A confiança em nível de documento está disponível como result.Confidence para rápida seleção de qualidade. O guia de pontuação de confiança abrange limites por palavra e por documento.
Questão 2: Pressupostos sobre a ordem de leitura
PaddleOCR: result.Regions é ordenado pela sequência de detecção, não pela ordem de leitura. Qualquer código que consuma result.Text esperando saída de cima para baixo, da esquerda para a direita, depende do padrão de ordenação manual usado em todos os exemplos do PaddleOCR.
Solução: O result.Text do IronOCR já está em ordem de leitura. Remova a classificação manual. Para casos em que a ordenação foi usada para construir uma saída linha por linha, use result.Pages[0].Lines diretamente:
// PaddleOCR: manual sort required for reading order
var lines = result.Regions
.OrderBy(r => r.Rect.Center.Y)
.ThenBy(r => r.Rect.Center.X)
.Select(r => r.Text);
// IronOCR: reading order is the default
var lines = result.Pages[0].Lines.Select(l => l.Text);
// PaddleOCR: manual sort required for reading order
var lines = result.Regions
.OrderBy(r => r.Rect.Center.Y)
.ThenBy(r => r.Rect.Center.X)
.Select(r => r.Text);
// IronOCR: reading order is the default
var lines = result.Pages[0].Lines.Select(l => l.Text);
Imports System.Linq
' PaddleOCR: manual sort required for reading order
Dim lines = result.Regions _
.OrderBy(Function(r) r.Rect.Center.Y) _
.ThenBy(Function(r) r.Rect.Center.X) _
.Select(Function(r) r.Text)
' IronOCR: reading order is the default
Dim lines = result.Pages(0).Lines.Select(Function(l) l.Text)
Problema 3: Código de conversão de matriz OpenCV
PaddleOCR: Algumas bases de código contêm métodos auxiliares que carregam imagens de streams ou arrays de bytes escrevendo primeiro em um arquivo temporário e depois chamando Cv2.ImRead(). Esses padrões existem porque Cv2.ImRead() só aceita caminhos de arquivos.
Solução: O OcrInput do IronOCR aceita streams e arrays de bytes diretamente. Exclua o arquivo temporário intermediário:
// PaddleOCR: stream → temp file → Mat → OCR
string tempPath = Path.GetTempFileName() + ".png";
using (var fs = File.Create(tempPath))
await imageStream.CopyToAsync(fs);
using Mat mat = Cv2.ImRead(tempPath);
PaddleOcrResult result = ocr.Run(mat);
File.Delete(tempPath);
// IronOCR: stream → OCR (no temp file)
using var input = new OcrInput();
input.LoadImage(imageStream);
var result = ocr.Read(input);
// PaddleOCR: stream → temp file → Mat → OCR
string tempPath = Path.GetTempFileName() + ".png";
using (var fs = File.Create(tempPath))
await imageStream.CopyToAsync(fs);
using Mat mat = Cv2.ImRead(tempPath);
PaddleOcrResult result = ocr.Run(mat);
File.Delete(tempPath);
// IronOCR: stream → OCR (no temp file)
using var input = new OcrInput();
input.LoadImage(imageStream);
var result = ocr.Read(input);
Imports System.IO
Imports OpenCvSharp
Imports IronOcr
' PaddleOCR: stream → temp file → Mat → OCR
Dim tempPath As String = Path.GetTempFileName() & ".png"
Using fs = File.Create(tempPath)
Await imageStream.CopyToAsync(fs)
End Using
Using mat As Mat = Cv2.ImRead(tempPath)
Dim result As PaddleOcrResult = ocr.Run(mat)
End Using
File.Delete(tempPath)
' IronOCR: stream → OCR (no temp file)
Using input As New OcrInput()
input.LoadImage(imageStream)
Dim result = ocr.Read(input)
End Using
O guia de entrada de fluxo abrange o carregamento de fluxos a partir de respostas HTTP, blobs de banco de dados e fluxos de memória.
Problema 4: Remoção do padrão de inicialização assíncrona
PaddleOCR: A inicialização do mecanismo é assíncrona porque o download do modelo envolve entrada/saída de rede. Isso força a execução assíncrona em toda a cadeia de chamadas, o que pode ser problemático em contextos síncronos, como construtores ou manipuladores de eventos não assíncronos.
Solução: A inicialização do IronOCR é síncrona. new IronTesseract() não executa I/O. Remova o await e o modificador async de qualquer método cuja única operação assíncrona foi o download do modelo:
// PaddleOCR: async forced by model download
public async Task<string> ExtractTextAsync(string imagePath)
{
FullOcrModel models = await OnlineFullModels.EnglishV4.DownloadAsync();
using PaddleOcrAll ocr = new PaddleOcrAll(models);
using Mat mat = Cv2.ImRead(imagePath);
return ocr.Run(mat).Text;
}
// IronOCR: synchronous — no async required unless the caller needs it
public string ExtractText(string imagePath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage(imagePath);
return ocr.Read(input).Text;
}
// PaddleOCR: async forced by model download
public async Task<string> ExtractTextAsync(string imagePath)
{
FullOcrModel models = await OnlineFullModels.EnglishV4.DownloadAsync();
using PaddleOcrAll ocr = new PaddleOcrAll(models);
using Mat mat = Cv2.ImRead(imagePath);
return ocr.Run(mat).Text;
}
// IronOCR: synchronous — no async required unless the caller needs it
public string ExtractText(string imagePath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage(imagePath);
return ocr.Read(input).Text;
}
Imports System.Threading.Tasks
' PaddleOCR: async forced by model download
Public Async Function ExtractTextAsync(imagePath As String) As Task(Of String)
Dim models As FullOcrModel = Await OnlineFullModels.EnglishV4.DownloadAsync()
Using ocr As New PaddleOcrAll(models)
Using mat As Mat = Cv2.ImRead(imagePath)
Return ocr.Run(mat).Text
End Using
End Using
End Function
' IronOCR: synchronous — no async required unless the caller needs it
Public Function ExtractText(imagePath As String) As String
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadImage(imagePath)
Return ocr.Read(input).Text
End Using
End Function
IronOCR também fornece suporte assíncrono nativo via ocr.ReadAsync(input) quando a execução não bloqueante é genuinamente necessária em um contexto assíncrono.
Problema 5: Limpeza da etapa de compilação do Docker
PaddleOCR: O Dockerfile contém apt-get install libopencv-dev, uma instrução COPY models/ /app/models/, e frequentemente um passo RUN de pré-download do modelo. A imagem base é frequentemente a imagem NVIDIA CUDA para implantações em GPU.
Solução: Remova todas as instruções do Dockerfile específicas do PaddleOCR. A imagem Docker do IronOCR não requer nenhuma imagem base especial nem etapa de cópia de modelo:
# PaddleOCR Dockerfile (remove all of this)
FROM nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04
RUN apt-get update && apt-get install -y libopencv-dev libgdiplus
COPY models/ /app/models/
COPY . /app
# IronOCR Dockerfile (clean)
FROM mcr.microsoft.com/dotnet/aspnet:8.0
COPY . /app
WORKDIR /app
ENTRYPOINT ["dotnet", "YourApp.dll"]
A imagem resultante diminui de aproximadamente 1,5 GB para aproximadamente 400 MB. O guia de implantação do Docker aborda os requisitos de biblioteca do Linux e as compilações para múltiplas arquiteturas.
Problema 6: Invalidação do cache do modelo CI/CD
PaddleOCR: Pipelines de CI/CD que armazenam em cache a etapa de restauração do NuGet devem gerenciar separadamente o cache de arquivos de modelo. Um padrão comum é armazenar em cache uma pasta models/ entre execuções. Quando a versão do wrapper é atualizada, a chave do cache muda e os modelos precisam ser baixados novamente dos servidores do Baidu, adicionando de 30 a 60 segundos ao processo.
Solução: O IronOCR não possui um diretório de cache de modelos. O único cache necessário é o cache padrão de pacotes NuGet . Sem etapa de cache separada, sem invalidação de cache em atualizações de wrapper, sem download de servidores de terceiros durante a CI:
# Remove from CI/CD pipeline:
# - name: Cache PaddleOCR models
# uses: actions/cache@v3
# with:
# path: models/
# key: paddleocr-models-${{env.PADDLEOCR_VERSION}}
# IronOCR only needs standard NuGet caching:
- name: Cache NuGet packages
uses: actions/cache@v3
with:
path: ~/.nuget/packages
key: nuget-${{hashFiles('**/*.csproj')}}
# Remove from CI/CD pipeline:
# - name: Cache PaddleOCR models
# uses: actions/cache@v3
# with:
# path: models/
# key: paddleocr-models-${{env.PADDLEOCR_VERSION}}
# IronOCR only needs standard NuGet caching:
- name: Cache NuGet packages
uses: actions/cache@v3
with:
path: ~/.nuget/packages
key: nuget-${{hashFiles('**/*.csproj')}}
Lista de verificação para migração do PaddleOCR (.NET)
Pré-migração
Antes de fazer qualquer alteração, audite o código-fonte para identificar todos os usos do PaddleOCR:
# Find all PaddleOCR namespace imports
grep -rn "using Sdcb.PaddleOCR" --include="*.cs" .
# Find all OpenCvSharp imports (added as PaddleOCR dependency)
grep -rn "using OpenCvSharp" --include="*.cs" .
# Find all Mat usage patterns
grep -rn "Cv2\.ImRead\|new Mat\|Mat mat" --include="*.cs" .
# Find all async model download calls
grep -rn "DownloadAsync\|OnlineFullModels\|LocalDetectionModel" --include="*.cs" .
# Find all GPU device configuration
grep -rn "PaddleDevice\|EnableUseGpu\|cuda" --include="*.cs" .
# Find all result region access patterns
grep -rn "result\.Regions\|region\.Score\|region\.Rect" --include="*.cs" .
# Locate model directory references in configuration files
grep -rn "PP-OCRv4\|cls_infer\|det_infer\|rec_infer" --include="*.cs" --include="*.json" --include="*.yaml" .
# Find all PaddleOCR namespace imports
grep -rn "using Sdcb.PaddleOCR" --include="*.cs" .
# Find all OpenCvSharp imports (added as PaddleOCR dependency)
grep -rn "using OpenCvSharp" --include="*.cs" .
# Find all Mat usage patterns
grep -rn "Cv2\.ImRead\|new Mat\|Mat mat" --include="*.cs" .
# Find all async model download calls
grep -rn "DownloadAsync\|OnlineFullModels\|LocalDetectionModel" --include="*.cs" .
# Find all GPU device configuration
grep -rn "PaddleDevice\|EnableUseGpu\|cuda" --include="*.cs" .
# Find all result region access patterns
grep -rn "result\.Regions\|region\.Score\|region\.Rect" --include="*.cs" .
# Locate model directory references in configuration files
grep -rn "PP-OCRv4\|cls_infer\|det_infer\|rec_infer" --include="*.cs" --include="*.json" --include="*.yaml" .
Inventariar os diretórios de modelos e observar o tamanho total. Identificar quais modelos de linguagem estão em uso (Chinês, Inglês, Japonês, etc.) para determinar quais pacotes IronOcr.Languages.* adicionar. Verifique se há configuração de GPU presente — esses arquivos têm a maior área a ser limpa.
Migração de código
- Remova
Sdcb.PaddleOCR,Sdcb.PaddleOCR.Models.Online,Sdcb.PaddleInference.runtime.*,OpenCvSharp4, eOpenCvSharp4.runtime.*do arquivo.csproj - Adicione
IronOcrao arquivo.csproj - Adicione pacotes
IronOcr.Languages.*para cada idioma não-inglês anteriormente baixado como um modelo PaddleOCR - Substitua todas as diretivas
using Sdcb.PaddleOCR*eusing OpenCvSharpporusing IronOcr - Adicione
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";ao início do aplicativo - Substitua
FullOcrModel models = await OnlineFullModels.*.DownloadAsync()por nada — remova a linha completamente - Substitua
new PaddleOcrAll(models)pornew IronTesseract() - Substitua
new PaddleOcrAll(models, PaddleDevice.Gpu(deviceId: 0))pornew IronTesseract() - Substitua
Mat mat = Cv2.ImRead(path)porvar input = new OcrInput(); input.LoadImage(caminho); - Substitua
ocr.Run(mat)porocr.Read(input) - Substitua o acesso
result.Regionsporresult.Pages[0].Linesouresult.Pages[0].Words - Substitua
region.Score >= thresholdporword.Confidence >= threshold * 100 - Substitua
region.Rect.Center.X / .Center.Yporword.X / word.Y - Remover a lógica de classificação manual — a saída do IronOCR já está em ordem de leitura.
- Remova o diretório
models/e todos os arquivos de modelo do repositório e dos scripts de implantação
Pós-migração
- Verifique se
dotnet buildé bem sucedido sem referências aSdcb.*,OpenCvSharp, ouPaddleInferencena saída de compilação - Execute o OCR no mesmo conjunto de documentos representativos usado para validar a saída do PaddleOCR e compare a precisão do texto.
- Confirme se os valores de confiança estão sendo lidos como números inteiros de 0 a 100 (e não como números de ponto flutuante de 0 a 1) em todos os pontos de filtro.
- Verifique se a ordem de leitura está correta sem classificação manual — verifique especificamente os layouts de várias colunas e de faturas.
- Teste se a construção da imagem Docker completa sem a imagem base CUDA ou
apt-get install libopencv-dev - Confirme se o tamanho da imagem Docker é inferior a 500 MB
- Execute o pipeline de CI/CD de ponta a ponta e verifique se não ocorrem downloads externos durante a compilação.
- Teste de implantação isolada da internet: verifique se o aplicativo inicia e processa documentos sem conexões de rede de saída.
- Para entradas TIFF com múltiplos quadros, verifique se todos os quadros foram processados e se a contagem de quadros corresponde ao arquivo de origem.
- Para qualquer entrada PDF, verifique se
input.LoadPdf()produz a mesma contagem de páginas e conteúdo de texto que a conversão anterior baseada em PdfiumViewer
Principais benefícios da migração para o IronOCR
Artefatos de Implantação Diminuem em 80 Porcento. O impacto de implantação do PaddleOCR — paddle_inference.dll, OpenCV DLLs e três diretórios de modelo — adiciona 300-500 MB a cada alvo de implantação. Após a migração, a instalação do IronOCR tem aproximadamente 80 MB. As imagens do Docker diminuíram de aproximadamente 1,5 GB para cerca de 400 MB. A inicialização de contêineres é mais rápida, os custos de armazenamento são menores e os pipelines de implantação que antes transferiam 500 MB de artefatos agora transferem 80 MB.
O tempo de inicialização a frio cai de segundos para milissegundos. O PaddleOCR carrega três arquivos de modelo de rede neural do disco na primeira inferência, adicionando uma pausa de 3 a 5 segundos antes do retorno da primeira chamada. Em funções sem servidor, cenários de escalonamento automático ou qualquer contexto em que novas instâncias sejam criadas sob demanda, essa inicialização a frio é cobrada repetidamente. O mecanismo do IronOCR é integrado e inicializa em menos de um segundo. O exemplo básico de OCR demonstra o padrão de inicialização.
A cobertura de idiomas expande de 14 para 125 sem necessidade de alterações na infraestrutura. O PaddleOCR agora oferece suporte a 14 idiomas. Adicionar qualquer um dos 111 idiomas suportados IronOCR, além do limite do PaddleOCR, requer apenas a adição de um pacote NuGet por idioma — sem necessidade de baixar modelos, gerenciar diretórios ou sincronizar versões. Equipes cujo volume de documentos se expande para novos mercados não precisam reescrever o documento nem desenvolver uma nova infraestrutura para adicionar suporte a OCR em polonês, vietnamita, grego ou hebraico. O catálogo completo de idiomas mostra todos os mais de 125 pacotes disponíveis.
A saída em PDF pesquisável requer apenas uma linha. O PaddleOCR retorna regiões de texto. Converter essas regiões em uma camada PDF pesquisável requer uma biblioteca PDF separada, conversão de coordenadas de pixel para ponto e código invisível de injeção de texto que se torna uma manutenção permanente. Após a migração, result.SaveAsSearchablePdf("output.pdf") substitui todo esse subsistema. Fluxos de trabalho de arquivamento de documentos digitalizados, conversões de fax para PDF e integrações de gerenciamento de documentos são diretamente beneficiados. O guia passo a passo para PDFs pesquisáveis e a postagem no blog sobre extração de dados de PDFs abordam todas as opções de saída.
Nenhuma Conexão de Rede Externa em Qualquer Etapa. O PaddleOCR conecta-se ao armazenamento bj.bcebos.com da Baidu para downloads de modelo. Em ambientes onde as conexões de saída são restritas — redes governamentais, sistemas isolados da internet, infraestrutura de serviços financeiros — essa conexão requer uma exceção no firewall ou um fluxo de trabalho de pré-download que aumenta a complexidade da CI/CD. O IronOCR não faz conexões externas em tempo de execução. Os modelos são restaurados como parte do dotnet restore do NuGet e estão presentes na saída de implantação. O guia de implantação da AWS e o guia de implantação do Azure abordam a configuração específica da nuvem para ambientes com restrições de rede.
Um Contato de Suporte Comercial para Toda a Pilha OCR. As questões do PaddleOCR abrangem o wrapper Sdcb.PaddleOCR (GitHub da comunidade), o framework PaddlePaddle (Baidu), o OpenCvSharp (comunidade), e o CUDA/cuDNN (NVIDIA). Cada nível de suporte possui um canal diferente, sem garantia de tempo de resposta. O IronOCR é um produto único da Iron Software com suporte comercial por e-mail e níveis de resposta prioritária. O centro de documentação do IronOCR reúne toda a documentação da API, guias práticos e recursos de resolução de problemas em um só lugar.
Perguntas frequentes
Por que devo migrar do PaddleOCR 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 PaddleOCR para o IronOCR?
Substitua as sequências de inicialização do PaddleOCR 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 PaddleOCR 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 PaddleOCR 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 PaddleOCR para o IronOCR exige alterações na infraestrutura de implantação?
O IronOCR requer menos alterações de infraestrutura do que o PaddleOCR. Não há caminhos binários do SDK, instalação de arquivos de licença ou configurações de servidor de licenças. 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 PaddleOCR?
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 PaddleOCR 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 PaddleOCR 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.

