如何使用 C# 提升影像品質以獲得更好的 OCR 結果
此指南引導.NET開發人員完成從Sdcb.PaddleOCR套件家族到IronOCR的完整遷移。 它涵蓋了完整的替換路徑:移除多套件PaddlePaddle堆疊,消除模型文件管理和GPU配置,並用單個NuGet安裝替換依賴OpenCV的推理管道。 每個章節都是獨立的——無需先前閱讀比較文章。
為什麼從PaddleOCR遷移
Sdcb.PaddleOCR包裝器是一個由社群維護的橋樑,將PaddlePaddle的Python深度學習生態系統與.NET連接起來。 它可以完成工作,但同時也承受著該橋樑的所有負擔——模型文件、本地推理二進位檔、用於圖像載入的OpenCV,以及可選的CUDA基礎設施。 對於大多數.NET OCR工作負載來說,這是專案不需要的基礎設施。
在讀取單個字元之前,三個模型目錄。PaddleOCR的推理管道連結了三個神經網路:檢測模型、方向分類模型和識別模型。 每個網路都是一個單獨的models/目錄樹到達的,開發人員最終都需要負責模型版本管理。 當Sdcb.PaddleOCR更新時,先前版本的預先下載模型可能需要重新下載。 IronOCR沒有模型文件,沒有模型目錄,也沒有版本同步問題。 引擎被打包在NuGet包中。
OpenCV不是可選項。 沒有任何從文件路徑到PaddleOCR推理的路徑能繞過OpenCvSharp。每張圖像,無論其格式如何,都必須通過ocr.Run(mat)才能接受。 這意味著在部署輸出中有兩個額外的NuGet包(System.Drawing.Bitmap直接。 Mat中介不存在。
GPU配置是個多日項目。 PaddleOCR宣傳的GPU性能指標——每張影像50-100ms,相比之下CPU則是300-500ms——是事實。 要達到這一點需要特定版本的NVIDIA驅動程式、CUDA Toolkit 11.8(而不是12.x)、cuDNN 8.6+正確放置在PATH中,以及一個獨立的GPU運行時NuGet包。 在Docker中,基礎映像必須是nvidia-container-toolkit。 沒有現成GPU基礎設施的團隊在每個環境上需耗費2-8小時進行CUDA配置。 IronOCR設計為CPU推理,提供每張影像150-300ms的標準硬體性能,且完全不需GPU設置。
部署工件比原來小4-6倍。PaddleOCR的部署輸出包含opencv_world*.dll文件(總計約50MB)和模型目錄(約21MB)。 Docker映像大約為1.5GB。 IronOCR部署總共大約80MB; Docker映像大約為400MB。 這種差異在CI/CD中會增長:每個恢復NuGet包的管道運行要麼下載百度的模型,要麼從單獨的快取層拉取。
沒有可搜尋的PDF輸出。PaddleOCR從圖像返回文字區域,無機制將識別的文字作為可搜尋的層嵌入到PDF中。 從PaddleOCR輸出建立一個可搜尋的PDF需要第三方PDF庫、逐頁的文字層嵌入和坐標重映射。 IronOCR通過一行生成完全可搜尋的PDF:result.SaveAsSearchablePdf("output.pdf")。
根本問題
PaddleOCR需要在推理開始前配置三個模型目錄:
// 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
IronOCR沒有模型文件、沒有模型目錄,並且不依賴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):特點比較
下表涵蓋了遷移計劃中最重要的維度。
| 功能 | PaddleOCR (Sdcb) | IronOCR |
|---|---|---|
| 需要 NuGet 套件 | 4-5 | 1 |
| 需要的模型文件 | 是(3個目錄,約21MB) | 否(打包在包中) |
| 模型下載來源 | 百度伺服器(bj.bcebos.com) | NuGet還原(Iron Software) |
| OpenCV 依賴 | 需要(OpenCvSharp4) | None |
| 圖像輸入 | 通過Mat mat = Cv2.ImRead() |
直接文件路徑、流、字節陣列 |
| 本地PDF輸入 | 不是 | 是(input.LoadPdf()) |
| 可搜尋的 PDF 輸出 | 不是 | 是(result.SaveAsSearchablePdf()) |
| 多幀TIFF輸入 | 每幀手動迴圈 | input.LoadImageFrames() |
| GPU 支持 | 是(需要CUDA 11.8 + cuDNN) | CPU優化(不需要GPU) |
| 內建預處理 | 否(神經網路處理偏移/噪聲) | 是的(Deskew, DeNoise, Contrast, Binarize, Sharpen) |
| 支持的語言 | 14 | 125+ |
| 語言安裝方法 | DownloadAsync()每個語言模型 |
dotnet add package IronOcr.Languages.* |
| 同時多語言支持 | 否(每種語言一個單獨模型) | 是(OcrLanguage.English + OcrLanguage.French) |
| 結構化輸出 | result.Regions(空間不排序) |
頁、段落、行、字、字元 |
| 信心得分 | 每個區域浮動(0-1) | 每個單詞百分比(0-100) |
| 條碼讀取 | 不是 | 是(ocr.Configuration.ReadBarCodes = true) |
| hOCR匯出 | 不是 | 是 |
| 部署大小 | 300-500MB | ~80MB |
| Docker映像大小 | ~1.5GB(包含CUDA基礎) | ~400MB |
| 冷啟動時間 | 3-5秒(模型載入) | 低於1秒 |
| 跨平台 | Windows,Linux(部分) | Windows、Linux、macOS、Docker、Azure、AWS |
| .NET相容性 | .NET 6+(社羣包裝器) | .NET Framework 4.6.2+, .NET 5/6/7/8/9 |
| 商業支持 | 社羣/GitHub問題 | 是(Iron Software,具備SLA) |
| 許可證 | Apache 2.0(免費) | 永久($999 Lite / $1,499 Pro / $2,999 Enterprise) |
快速開始:PaddleOCR(.NET)到IronOCR遷移
步驟1:替換NuGet包
移除所有五個與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
如果GPU運行時已安裝,也需要移除它:
dotnet remove package Sdcb.PaddleInference.runtime.win64.cuda118
dotnet remove package Sdcb.PaddleInference.runtime.win64.cuda118
從NuGet包頁面安裝IronOCR:
dotnet add package IronOcr
步驟2:更新命名空間
替換所有PaddleOCR和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
步驟3:初始化許可證
在建立任何IronTesseract實例之前,在應用程式啟動時新增授權初始化:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
免費試用金鑰可從IronOCR授權頁面獲得。 試用版會生成帶有水印的輸出,並允許進行全面功能測試,然後再購買。
程式碼遷移範例
本地模型路徑配置消除
為避免在執行時連接百度伺服器而預先下載PaddleOCR模型文件的項目必須配置三個單獨的目錄路徑。 每當包裝器版本更改時,這個配置都必須更新。
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
IronOCR方法:
//不是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);
//不是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
'不是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
FromDirectory()調用和版本同步線索都消失。 IronOCR引擎是在NuGet包恢復時打包的,不需要運行時路徑解析。 參見IronTesseract設置指南了解初始化選項,包括在appsettings.json中放置授權金鑰。
雙階段檢測和識別管道整合
PaddleOCR的旋轉和方向管道是通過PaddleOcrAll上的屬性配置的。 在IronOCR中複製這種行為使用OcrInput預處理方法,這些方法用更簡單的調用介面處理相同的文件問題。
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
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
IronOCR的Lines集合由Tesseract布局引擎按閱讀順序傳遞,消除了手動排序模式。 圖像方向校正指南記錄了完整的旋轉和去傾選項。
GPU和CPU裝置選擇移除
運行GPU推理的PaddleOCR應用攜帶最大的遷移面:GPU運行時NuGet包、CUDA/cuDNN環境前提條件,以及PaddleDevice.Gpu()配置調用。 遷移期間,所有這些都被去除。
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
IronOCR方法:
IronOCR方法與上述範例相同——IronTesseract用相同的API調用處理這種情況。 不需要GPU包,不需要CUDA前提條件,也不需要選擇裝置。 用new IronTesseract()並移除所有GPU相關配置。
IronOCR在CPU上每張影像提供150-300ms——比PaddleOCR在CPU上快(300-500ms),適合於大多數網路API和文件管道工作量,完全不需要GPU基礎設施。 對於高通量場景,速度優化指南涵蓋了配置選項,包括執行緒管理和頁面分割模式調整。
結構化文件資料提取
PaddleOCR返回按空間排序的PaddleOcrResultRegion物件的平面陣列,而不是按閱讀流。 提取段落級或行級結構需要基於邊界框近似的手動分組邏輯。 IronOCR提供保証閱讀順序的分層結果樹。
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);
//不是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);
//不是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)
'不是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
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
手動行分組近似——將Y坐標捨入至像素桶大小——被Tesseract布局引擎的內建段落分割取代。 邊界框坐標在結構樹的每個層次通過paragraph.Height可用。 參見結構結果指南和從影像讀取文字教程了解完整的結果樹覆蓋。
可搜尋PDF生成
PaddleOCR不產生PDF輸出。 從PaddleOCR結果生成可搜尋的PDF需要分開的PDF庫、從region.Rect到PDF頁面單位的手動坐標映射、以及不可見的文字層注入。 IronOCR直接從OCR結果生成可搜尋的PDF。
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);
//不是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);
//不是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)
'不是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.")
End Using
End Using
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
坐標映射問題——將OpenCV像素坐標轉換為正確DPI的PDF點空間——在IronOCR中不存在。 可搜尋的PDF指南涵蓋多頁輸出、密碼保護的PDF及輸出質量設置。 對於數位化掃描檔案或建構傳真至可搜尋PDF管道的團隊而言,這個單一方法呼叫替換了相當可觀的整合項目。
多幀 TIFF 批次處理
多頁TIFF文件在文件掃描工作流程中經常出現。 PaddleOCR沒有直接的TIFF多幀支援——每幀必須使用外部影像庫單獨提取,並作為單獨的Mat載入。 IronOCR本地處理多幀TIFF。
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
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
幀提取迴圈、System.Drawing依賴、臨時文件建立和清理邏輯都被去除。 IronOCR以單一Page。 TIFF和GIF輸入指南涵蓋了多幀載入選項、選擇性幀範圍和大型TIFF檔案的記憶體考慮。
PaddleOCR(.NET)API到IronOCR映射參考
| PaddleOCR (Sdcb) | IronOCR | 注意事項 |
|---|---|---|
Sdcb.PaddleOCR |
IronOcr |
命名空間 |
Sdcb.PaddleOCR.Models.Online |
不適用 | 不需要模型獲取命名空間 |
Sdcb.PaddleInference |
不適用 | 不需要推理後端命名空間 |
FullOcrModel |
不適用 | 無等效項——模型已打包 |
OnlineFullModels.ChineseV4.DownloadAsync() |
dotnet add package IronOcr.Languages.ChineseSimplified |
模型獲取被NuGet替代 |
LocalDetectionModel.FromDirectory(path) |
不適用 | 無需模型路徑管理 |
LocalClassificationModel.FromDirectory(path) |
不適用 | 無需模型路徑管理 |
LocalRecognitionModel.FromDirectory(path) |
不適用 | 無需模型路徑管理 |
new PaddleOcrAll(models) |
new IronTesseract() |
引擎建立 |
new PaddleOcrAll(models, PaddleDevice.Gpu(0)) |
不適用 | GPU裝置選擇完全去除 |
PaddleDevice.Cpu() |
不適用 | CPU是唯一模式; 不需要選擇 |
ocr.AllowRotateDetection = true |
input.Deskew() |
旋轉校正 |
ocr.Enable180Classification = true |
自動 | 倒置檢測內建 |
Cv2.ImRead(path) |
input.LoadImage(path) |
圖像載入——不需要OpenCV |
ocr.Run(mat) |
ocr.Read(input) |
執行 OCR |
result.Text |
result.Text |
完整文件文字字串 |
result.Regions |
.Words |
結構化文字區域 |
region.Text |
word.Text / line.Text |
區域的文字內容 |
region.Score(浮動0-1) |
word.Confidence(整數0-100) |
信心值——比例不同 |
region.Rect.Center.X |
word.X |
水平位置 |
region.Rect.Center.Y |
word.Y |
垂直位置 |
region.Rect.Size.Width |
word.Width |
邊界框寬度 |
region.Rect.Size.Height |
word.Height |
邊界框高度 |
| 不適用 | input.LoadPdf(path) |
原生PDF輸入(無PaddleOCR等效) |
| 不適用 | input.LoadImageFrames(path) |
多幀TIFF(無PaddleOCR等效) |
| 不適用 | result.SaveAsSearchablePdf(path) |
可搜尋的PDF輸出(無PaddleOCR等效) |
常見的遷移問題与解決方案
問題1:信心值刻度不匹配
PaddleOCR:區域信心值在0.0到1.0之間。常見的閾值是region.Score >= 0.8來過濾低質量檢測。
解決方案:IronOCR信心值是0到100的百分比。將PaddleOCR閾值乘以100:
// PaddleOCR: filter at 0.8
var highConfidence = result.Regions.Where(r => r.Score >= 0.8);
//IronOCRequivalent: 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);
//IronOCRequivalent: 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)
' IronOCRequivalent: filter at 80
Dim highConfidence = result.Pages _
.SelectMany(Function(p) p.Words) _
.Where(Function(w) w.Confidence >= 80)
文件級別信心值可作為result.Confidence進行快速質量篩選。 信心水準指南涵蓋了每個單詞和文件級別的閾值。
問題2:閱讀順序假設
PaddleOCR:result.Regions按檢測順序排序,而不是閱讀順序。 消費result.Text的任何程式碼預期頂部到底部、左到右輸出都依賴於PaddleOCR範例中使用的手動排序模式。
解決方案:IronOCR的result.Text已經在閱讀順序中。 去除手動排序。 對於排序被用來構建逐行輸出時的情況,直接使用result.Pages[0].Lines:
// 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)
問題3:OpenCV Mat轉換程式碼
PaddleOCR:一些程式碼庫中帶有載入流或字節陣列並通過先寫入臨時文件再調用Cv2.ImRead()的助手方法。 這些模式存在是因為Cv2.ImRead()只接受文件路徑。
解決方案:IronOCR的OcrInput直接接受流和字節陣列。 刪除臨時文件中介:
// 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
流輸入指南涵蓋了HTTP響應、資料庫blob和記憶體流的流載入。
問題4:異步初始化模式去除
PaddleOCR:引擎初始化是異步的,因為模型下載涉及到網路I/O。 這強制整個調用鏈保持異步,在同步上下文中如構造函式或非異步事件處理程式中會有問題。
解決方案:IronOCR初始化是同步的。 new IronTesseract()不執行I/O。 去除async修飾符:
// 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也提供通過ocr.ReadAsync(input)的本機異步支援。
問題5:Docker構建步驟清理
PaddleOCR:Dockerfile中包含RUN步驟。基礎映像通常是用於GPU部署的NVIDIA CUDA映像。
解決方案:移除所有特定於PaddleOCR的Dockerfile指令。IronOCRDocker映像不需要特殊的基礎映像,也不需要模型複製步驟:
# 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
#IronOCRDockerfile (clean)
FROM mcr.microsoft.com/dotnet/aspnet:8.0
COPY . /app
WORKDIR /app
ENTRYPOINT ["dotnet", "YourApp.dll"]
生成的映像從大約1.5GB下降至大約400MB。 Docker部署指南涵蓋了Linux庫要求和跨架構構建。
問題6:CI/CD模型快取失效
PaddleOCR:快取重置步驟的CI/CD管道必須單獨管理模型文件快取。 常見模式是在運行間快取models/文件夾。 當包裝器版本更新時,快取鍵會更改,模型必須從百度伺服器重新下載,為管道增加30-60秒。
解決方案:IronOCR沒有模型快取目錄。 唯一需要的快取是標準的NuGet包快取。 沒有單獨的快取步驟,沒有包裝器更新時的快取失效,沒有在CI期間從第三方伺服器下載:
# Remove from CI/CD pipeline:
# - name: Cache PaddleOCR models
# uses: actions/cache@v3
# with:
# path: models/
# key: paddleocr-models-${{env.PADDLEOCR_VERSION}}
#IronOCRonly 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}}
#IronOCRonly needs standard NuGet caching:
- name: Cache NuGet packages
uses: actions/cache@v3
with:
path: ~/.nuget/packages
key: nuget-${{hashFiles('**/*.csproj')}}
PaddleOCR(.NET)遷移檢查表
遷移前
在進行任何更改之前,審核程式碼庫以識別所有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" .
盤點模型目錄並註記總大小。識別使用的語言模型(中文、英文、日文等),以確定需要新增哪些IronOcr.Languages.*包。 註明是否存在GPU配置——這些文件有最多的清理面。
程式碼遷移
- 從
OpenCvSharp4.runtime.</em> - 新增
.csproj文件 - 為每個先前下載作為PaddleOCR模型的非英語語言新增
IronOcr.Languages.*包 - 用
using OpenCvSharp指令 - 在應用程式啟動時新增
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; - 用nothing替換
FullOcrModel models = await OnlineFullModels.*.DownloadAsync()——完全移除該行 - 用
new PaddleOcrAll(models) - 用
new PaddleOcrAll(models, PaddleDevice.Gpu(deviceId: 0)) - 用
var input = new OcrInput(); input.LoadImage(path);替換Mat mat = Cv2.ImRead(path) - 用
ocr.Run(mat) - 用
result.Regions存取 - 用
region.Score >= threshold - 用
region.Rect.Center.X / .Center.Y - 刪除手動排序邏輯——IronOCR輸出已經在閱讀順序中
- 移除
models/目錄及所有模型文件從資料庫和部署腳本
遷移後
- 驗證
PaddleInference的情況下成功 - 在用於驗證PaddleOCR輸出的相同代表性文件集上運行OCR並比較文字準確性
- 確認在所有篩選點信心值被作為整數0-100讀取(而非浮動0-1)
- 驗證閱讀順序在沒有手動排序情況下正確——具體檢查多列和發票佈局
- 測試完成Docker映像構建而無需CUDA基礎映像或
apt-get install libopencv-dev - 確認Docker映像大小低於500MB
- 完整運行CI/CD管道並驗證在構建過程中沒有外部下載發生
- 測試氣隙部署:驗證應用程式啟動並處理文件時不進行出站網路連接
- 對於多幀TIFF輸入,驗證所有幀已處理且幀計數與源文件匹配
- 對於PDF輸入,驗證
input.LoadPdf()產生的頁數與之前基於PdfiumViewer的轉換相同
遷移至IronOCR的主要好處
部署工件縮小80%。 PaddleOCR的部署佈署——paddle_inference.dll、OpenCV DLL和三個模型目錄——每個部署目標增加300-500MB。 遷移後,IronOCR的部署大約為80MB。 Docker映像從約1.5GB減少到約400MB。 容器啟動速度更快,儲存成本更低,並且以前轉移500MB工件的部署管道現在轉移80MB。
冷啟動從秒降至毫秒。 PaddleOCR在首次推理時從磁碟載入三個神經網路模型文件,新增了3-5秒的暫停,然後第一個調用返回。 在無伺服器功能、自動縮放場景或任何新實例按需啟動的上下文中,那次冷啟動需要反复支付。 IronOCR的引擎捆綁並初始化在一秒內。 基本OCR範例展示了初始化模式。
語言覆蓋從14擴展到125,無需基礎設施工作。 PaddleOCR支援14種語言。 新增IronOCR支援的111種超過PaddleOCR限制的語言中的任何一種,只需要每語言新增一個NuGet包——無需模型下載、無需目錄管理、無需版本同步。 其文件量擴張到新市場的團隊不會面臨重寫或新的基礎設施項目來新增波蘭語、越南語、希臘語或希伯來語OCR支援。 完整語言目錄顯示了所有125+個可用包。
可搜尋的PDF輸出僅需一行。 PaddleOCR返回文字區域。 將這些區域轉換為可搜尋的PDF層需要分開的PDF庫、像素到點坐標轉換和不可見的文字注入程式碼,這變成永久維護。 遷移后,result.SaveAsSearchablePdf("output.pdf")取代了整個子系統。 掃描文件檔案工作流程、傳真到PDF流水線和文件管理整合都直接受益。 可搜尋的PDF操作指南和PDF資料提取部落格文章涵蓋了完整的輸出選項。
在任何階段都沒有外部網路連接。 PaddleOCR連接百度的bj.bcebos.com儲存下載模型。 在出站連接受限的環境中——政府網路、氣隙系統、金融服務基礎設施——那次連接需要防火牆例外或新增CI/CD複雜度的預下載工作流。 IronOCR在運行時沒有外部連接。模型作為dotnet restore的一部分從NuGet還原,並存在於部署輸出中。 AWS部署指南和Azure部署指南涵蓋了有網路限制的環境的雲端特定配置。
整個OCR堆棧的一個商業支援聯絡點。 PaddleOCR問題跨越Sdcb.PaddleOCR包裝器(社羣GitHub)、PaddlePaddle框架(百度)、OpenCvSharp(社羣)和CUDA/cuDNN(NVIDIA)。 每一層都有不同的支援渠道,沒有保證的回應時間。IronOCR是Iron Software的單一產品,提供商業郵件支援和優先響應等級。 IronOCR文件中心將所有API文件、操作指南和故障排除資源集中在一處。
常見問題
為什麼我應該從PaddleOCR遷移到IronOCR?
常見的驅動因素包括消除COM互操作複雜性、更換基於文件的許可證管理、避免按頁收費、啟用Docker/容器部署,以及採取與標準.NET工具整合的NuGet本地工作流。
從PaddleOCR遷移到IronOCR時,主要的程式碼改變是什麼?
使用IronTesseract實例替換PaddleOCR初始化序列,移除COM生命週期管理(明確的Create/Load/Close模式),並更新結果屬性名稱。結果是顯著減少樣板行數。
如何安裝IronOCR以開始遷移?
在套件管理器控制台中運行 'Install-Package IronOcr' 或在CLI中運行 'dotnet add package IronOcr'。語言包是單獨的包:例如 'dotnet add package IronOcr.Languages.French' 安裝法語支持。
IronOCR的OCR準確度是否能與PaddleOCR對標準商業文件的準確度相匹配?
IronOCR在包括發票、合同、收據和鍵入的表單等標準業務內容上達到高準確性。圖像預處理過濾器(糾偏、噪音去除、對比度增強)進一步提高了變質輸入的識別。
IronOCR如何處理PaddleOCR單獨安裝的語言資料?
IronOCR中的語言資料分發為NuGet包。'dotnet add package IronOcr.Languages.German' 安裝德語支持。不涉及手動文件放置或目錄路徑。
從PaddleOCR遷移到IronOCR是否需要更改部署基礎設施?
IronOCR所需的基礎設施變更比PaddleOCR少。沒有SDK二進位路徑、授權文件放置或授權伺服器配置。NuGet包中包含了完整的OCR引擎,授權金鑰為設置在應用程式程式碼中的字串。
遷移後如何配置IronOCR許可證?
在應用啟動程式碼中分配IronOcr.License.LicenseKey = "YOUR-KEY"。在Docker或Kubernetes中,將金鑰儲存為環境變數並在啟動時讀取。在接受流量之前,使用License.IsValidLicense驗證。
IronOCR能像PaddleOCR一樣處理PDF嗎?
是的。IronOCR能讀取本地和掃描的PDF。實例化IronTesseract,調用ocr.Read(input),其中input是PDF路徑或OcrPdfInput,並迭代OcrResult頁面。不需要單獨的PDF渲染管道。
IronOCR如何處理大批量處理中的多執行緒?
IronTesseract可以安全地在每個執行緒中實例化。在Parallel.ForEach或Task池中為每個執行緒建立一個實例,並發併運行OCR,當完成時處置每個實例。不需要全域狀態或鎖定。
IronOCR在文字提取後支持哪些輸出格式?
IronOCR返回結構化結果,包括文字、單詞坐標、信心分數和頁面結構。導出選項包括純文字、可搜索PDF以及下游處理的結構化結果物件。
IronOCR在擴展工作負載時價格是否比PaddleOCR更可預測?
IronOCR使用固定費率的永久性許可證,沒有按頁或按量收費。無論您處理10000頁還是1000萬頁,許可證費用保持不變。團隊和批量許可證選項可在IronOCR定價頁面上找到。
從PaddleOCR遷移到IronOCR後,我現有的測試會發生什麼變化?
遷移後,斷言提取的文字內容的測試應繼續通過。需要更新的測試是驗證API調用模式或COM物件生命週期,這需要反映IronOCR較簡單的初始化和結果模型。

