如何使用OCR工具从图像中提取阿拉伯文本
本指南将引导.NET开发人员完成从 GdPicture .NET OCR 到IronOCR的完整迁移。 它涵盖了每个主要 OCR 工作流程的包交换、命名空间更改和实际的前后代码模式,尤其侧重于消除定义 GdPicture 资源管理模型的整数图像 ID 生命周期。 无需事先阅读对比文章。
为什么要从 GdPicture.NET 迁移?
GdPicture .NET是一个文档图像处理平台,专为需要从单一供应商处获得扫描仪集成、DICOM 支持、PDF 编辑、注释和 OCR 的团队而构建。 当 OCR 是唯一需求时,平台的定价和 API 架构会造成摩擦,而这种摩擦会随着时间的推移而累积。
**基本 OCR 工作流程的插件成本。**从扫描的 PDF 文件中提取文本并生成可搜索的输出需要三个独立的许可组件:核心 SDK(约 4,000 美元)、OCR 插件(约 2,000 美元)和 PDF 插件(约 2,000 美元)。这意味着 8,000 美元的入门成本,Plus20% 的年度维护费用。 只需要 OCR 功能的团队需要承担文档影像平台的全部定价结构。 IronOCR覆盖相同的工作流程——图像OCR,PDF OCR,预处理、可搜索的PDF输出——从一个单一的包在$999到$2,399的永久版本,无需按功能许可决策。 请参阅IronOCR许可页面,了解完整的分级细则。
整数图像ID生命周期。 通过GdPicture加载的每个图像返回一个int。 您将该整数传递给OCR操作,然后在完成时使用它调用ReleaseGdPictureImage。 此模式在IDisposable之前就已存在。 只要正确执行,它就能奏效; 如果漏掉,就会造成内存泄漏。 在每天处理数百份文档的生产服务中,一个错误分支上的发布调用被遗漏会导致内存增长,而这种增长确实很难诊断。 IronOCR的using语句消除了整个清理负担。
特定版本的命名空间。 GdPicture在其命名空间中嵌入了主要版本号:using GdPicture14。 升级到下一个主要版本需要在引用GdPicture类的每个源文件中更新该using指令。 对于一个包含数十个服务中的 OCR 的大型应用程序来说,查找和替换工作需要花费数小时,而且不会带来任何功能上的改进。 IronOCR的命名空间在所有主要版本中都保持为IronOcr。
外部资源文件夹要求。 GdPicture OCR需要在运行时指向.traineddata语言文件。该路径在开发机器上有效,但在没有目录结构的Linux服务器、Docker容器和Azure App Service部署上会中断。IronOCR将英语语言支持包内在NuGet包中; 其他语言将作为NuGet包安装,并随构建输出一起传输。
三组件初始化。 GdPicture中的PDF OCR工作流程需要实例化GdPicturePDF——三个独立的组件,各自具有单独的处理要求——加上许可管理器的注册和资源文件夹的分配。 IronOCR启动时需要一行代码,调用时需要一个类。
不支持原生线程安全。GdPicture OCR 实例不是线程安全的。 并行文档处理需要仔细的实例管理和同步,以避免状态损坏。 IronOCR设计用于并发使用:每个线程创建一个IronTesseract,或者在负载下共享一个实例——这两种模式都无需额外的同步代码使用。
基本问题
GdPicture 会为每个图像分配内存,作为运行时管理的整数句柄。 TIFF处理中循环中的每个RenderPageToGdPictureImage调用创建一个必须手动释放的新分配:
// GdPicture: every frame = new integer ID = manual release required
using var pdf = new GdPicturePDF();
pdf.LoadFromFile("multi-page.tiff", false);
var frameIds = new List<int>();
try
{
for (int i = 1; i <= pdf.GetPageCount(); i++)
{
pdf.SelectPage(i);
int frameId = pdf.RenderPageToGdPictureImage(200, false); // new allocation
if (frameId != 0) frameIds.Add(frameId);
// ... OCR call here ...
}
}
finally
{
foreach (var id in frameIds) _imaging.ReleaseGdPictureImage(id); // manual per-frame release
}
IronOCR完全消除了生命周期。 OcrInput处理帧枚举和清理:
// IronOCR: the using statement handles everything
using var input = new OcrInput();
input.LoadImageFrames("multi-page.tiff");
var result = new IronTesseract().Read(input);
IronOCR与 GdPicture .NET:功能对比
下表列出了两个图书馆在以 OCR 为中心的工作流程中的功能覆盖范围。
| 特征 | GdPicture.NET | IronOCR |
|---|---|---|
| 安装 | 多个NuGet包 | dotnet add package IronOcr |
| 许可证激活 | LicenseManager.RegisterKEY() | IronOcr.License.LicenseKey = "..." |
| 命名空间正在进行重大升级 | 需要查找和替换 (GdPicture14 → next) | 未变更 (IronOcr) |
| 组件初始化 | GdPictureImaging + GdPictureOCR + GdPicturePDF | new IronTesseract() |
| 资源内存模型 | 手动整数 ID 跟踪和发布 | IDisposable / using 语句 |
| 内存泄漏风险 | 高 (缺少ReleaseGdPictureImage) | 无 (编译器通过using强制) |
| 外部资源文件夹 | 必需 (_ocr.ResourceFolder = path) | 无需购买——已包含在软件包中 |
| 图像OCR | 是 | 是 |
| PDF OCR | 是的(需要PDF插件) | 内置,无需额外许可 |
| 多页 TIFF 文件 | 手动帧循环 + 逐帧 ID 清理 | input.LoadImageFrames() |
| 流输入 | 通过GdPictureImaging流重载 | input.LoadImage(stream) |
| 可搜索的 PDF 输出 | pdf.OcrPage() + pdf.SaveToFile() | result.SaveAsSearchablePdf() |
| 去斜预处理 | 需要文档影像插件 | input.Deskew()——内建 |
| 降噪 | 需要文档影像插件 | input.DeNoise()——内建 |
| 语言 | 文件夹中包含基于 Tesseract 的训练数据文件 | 通过NuGet包提供 125 多个包 |
| 多语言OCR | 是 | OcrLanguage.French + OcrLanguage.German |
| 线程安全 | 手动实例管理 | 线程安全设计 |
| 异步OCR | 非内置 | ReadAsync() |
| 条形码读取 | 独立的条形码插件 | ocr.Configuration.ReadBarCodes = true |
| 结构化输出 | 基于索引的块/行/词访问 | 类型化.Characters |
| 置信度评分 | GetOCRResultConfidence(resultId) | result.Confidence |
| 跨平台 | Windows、Linux、macOS | Windows、Linux、macOS、Docker、AWS、Azure |
| 入口费用(PDF OCR识别) | 约 8,000 美元(核心功能 + OCR + PDF 插件) | $999–$2,399 |
| 定价模式 | 插件式永久授权 + 每年 20% 的维护费 | 永久免费,可选年度更新 |
| 商业支持 | 是 | 是 |
快速入门:GdPicture .NET到IronOCR 的迁移
步骤 1:替换 NuGet 软件包
移除 GdPicture 软件包:
dotnet remove package GdPicture.NET
dotnet remove package GdPicture.NET.OCR
dotnet remove package GdPicture.NET.PDF
从NuGet包页面安装IronOCR :
对于英语以外的语言,请安装相应的语言包:
步骤 2:更新命名空间
将 GdPicture 命名空间替换为IronOCR命名空间:
// Before (GdPicture)
using GdPicture14;
// After (IronOCR)
using IronOcr;
步骤 3:初始化许可证
将此行放置在Startup.cs,在任何OCR调用之前:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"完全移除 GdPicture 许可证注册限制:
// Remove this
LicenseManager lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
IronOCR产品页面提供免费试用密钥,供您在购买前进行评估。
代码迁移示例
多页 TIFF 帧处理
在 GdPicture 中处理多页 TIFF 文件需要通过 PDF/图像 API 选择每一帧,将其渲染为新的图像 ID,运行 OCR,然后释放该 ID。 一个在finally块中未释放的单帧会泄露10至50 MB,具体取决于DPI。
GdPicture .NET方法:
using GdPicture14;
public class GdPictureTiffProcessor
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
public string ExtractTextFromTiff(string tiffPath)
{
var text = new StringBuilder();
// Load TIFF through the imaging component
int tiffId = _imaging.CreateGdPictureImageFromFile(tiffPath);
if (tiffId == 0)
throw new Exception($"TIFF load failed: {_imaging.GetStat()}");
// Outer try: release the original TIFF handle
try
{
int frameCount = _imaging.GetPageCount(tiffId);
for (int i = 1; i <= frameCount; i++)
{
// Switch to frame — modifies the existing ID in place
_imaging.SelectPage(tiffId, i);
// Clone frame to a new image ID for OCR
int frameId = _imaging.CloneImage(tiffId);
if (frameId == 0) continue;
// Inner try: release each cloned frame ID
try
{
_ocr.SetImage(frameId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (!string.IsNullOrEmpty(resultId))
{
text.AppendLine($"[Frame {i}] {_ocr.GetOCRResultText(resultId)}");
}
}
finally
{
// Release cloned frame — critical for each iteration
_imaging.ReleaseGdPictureImage(frameId);
}
}
}
finally
{
// Release original TIFF handle
_imaging.ReleaseGdPictureImage(tiffId);
}
return text.ToString();
}
}
IronOCR方法:
using IronOcr;
public class IronOcrTiffProcessor
{
public string ExtractTextFromTiff(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // all frames loaded, all cleanup automatic
var result = new IronTesseract().Read(input);
// Per-frame text is available on result.Pages
foreach (var page in result.Pages)
Console.WriteLine($"[Frame {page.PageNumber}] {page.Text}");
return result.Text;
}
}
using块在范围结束时处理所有与每个帧相关的内存。 无需维护try/finally块。 TIFF 和 GIF 输入指南详细介绍了帧选择和多格式处理。
基于流的输入替换插件初始化
服务器应用程序经常以流的形式接收文档,而不是文件路径——例如通过 HTTP 上传、消息队列或数据库 blob。 GdPicture需要通过GdPictureImaging加载流,而该流程本身需要组件初始化序列和在每个操作之前的资源文件夹配置。
GdPicture .NET方法:
using GdPicture14;
public class GdPictureStreamOcr : IDisposable
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
public GdPictureStreamOcr()
{
// Plugin initialization required before stream loading is possible
var lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
_imaging = new GdPictureImaging();
_ocr = new GdPictureOCR();
_ocr.ResourceFolder = @"C:\GdPicture\Resources\OCR"; // path must exist at runtime
}
public string ExtractTextFromStream(Stream documentStream)
{
// Load stream into imaging component to get an image ID
int imageId = _imaging.CreateGdPictureImageFromStream(documentStream);
if (imageId == 0)
throw new Exception($"Stream load failed: {_imaging.GetStat()}");
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId))
throw new Exception($"OCR failed: {_ocr.GetStat()}");
return _ocr.GetOCRResultText(resultId);
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
}
public void Dispose()
{
_ocr?.Dispose();
_imaging?.Dispose();
}
}
IronOCR方法:
using IronOcr;
public class IronOcrStreamOcr
{
public string ExtractTextFromStream(Stream documentStream)
{
using var input = new OcrInput();
input.LoadImage(documentStream); // stream accepted directly — no imaging component
return new IronTesseract().Read(input).Text;
}
}
GdPicture 方法需要构建三个对象并配置文件系统路径,然后才能使用第一个流。 IronOCR直接接受OcrInput的流,无需事先设置。流输入指南涵盖了字节数组、FileStream模式,用于不需要临时文件写入的管道架构。
异步OCR取代状态码轮询
GdPicture OCR是同步的。 在ASP.NET Core端点或后台服务中处理文档的应用程序必须将Task.Run中,以避免阻塞请求线程——然后跨线程边界管理图像ID生命周期。 IronOCR通过ReadAsync提供一流的异步支持。
GdPicture .NET方法:
using GdPicture14;
using System.Threading.Tasks;
public class GdPictureAsyncWrapper
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);
public async Task<string> ExtractTextAsync(string imagePath)
{
// Must acquire lock: GdPictureOCR is not thread-safe
await _lock.WaitAsync();
try
{
return await Task.Run(() =>
{
int imageId = _imaging.CreateGdPictureImageFromFile(imagePath);
if (imageId == 0)
throw new Exception($"Load failed: {_imaging.GetStat()}");
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId))
throw new Exception($"OCR failed: {_ocr.GetStat()}");
return _ocr.GetOCRResultText(resultId);
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
});
}
finally
{
_lock.Release();
}
}
}
IronOCR方法:
using IronOcr;
public class IronOcrAsyncService
{
private readonly IronTesseract _ocr = new IronTesseract();
public async Task<string> ExtractTextAsync(string imagePath)
{
// ReadAsync is natively async — no Task.Run wrapper, no lock required
var result = await _ocr.ReadAsync(imagePath);
return result.Text;
}
public async Task<string> ExtractFromStreamAsync(Stream stream)
{
using var input = new OcrInput();
input.LoadImage(stream);
var result = await _ocr.ReadAsync(input);
return result.Text;
}
}
GdPicture方法需要一个Task.Run以将同步阻塞工作移到调用线程之外,并在该lambda中保持完整的图像ID生命周期。 IronOCR的ReadAsync是真正的不阻塞且线程安全的。 异步 OCR 指南涵盖了与ASP.NET Core中间件和托管后台服务的集成。
并行批处理与线程安全
要尽快处理扫描文档文件夹,需要并行执行。 GdPicture需要每个线程一个GdPictureOCR实例——共享一个实例会导致非确定性的失效。 每个线程的实例还需要自己的GdPictureImaging组件和资源文件夹配置,使线程池方法不使用工厂模式就变得不切实际。
GdPicture .NET方法:
using GdPicture14;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class GdPictureParallelBatch
{
private readonly string _resourceFolder = @"C:\GdPicture\Resources\OCR";
public ConcurrentDictionary<string, string> ProcessBatch(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// Each thread must have its own component instances
Parallel.ForEach(imagePaths, imagePath =>
{
// Create per-thread instances — shared instances cause failures
using var threadImaging = new GdPictureImaging();
using var threadOcr = new GdPictureOCR();
threadOcr.ResourceFolder = _resourceFolder;
// Re-register license per thread (may be required depending on SDK version)
var lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
int imageId = threadImaging.CreateGdPictureImageFromFile(imagePath);
if (imageId == 0)
{
results[imagePath] = $"ERROR: {threadImaging.GetStat()}";
return;
}
try
{
threadOcr.SetImage(imageId);
threadOcr.Language = "eng";
string resultId = threadOcr.RunOCR();
results[imagePath] = string.IsNullOrEmpty(resultId)
? $"ERROR: {threadOcr.GetStat()}"
: threadOcr.GetOCRResultText(resultId);
}
finally
{
threadImaging.ReleaseGdPictureImage(imageId);
}
});
return results;
}
}
IronOCR方法:
using IronOcr;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class IronOcrParallelBatch
{
public ConcurrentDictionary<string, string> ProcessBatch(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// IronTesseract is thread-safe — one instance handles all threads
var ocr = new IronTesseract();
Parallel.ForEach(imagePaths, imagePath =>
{
try
{
results[imagePath] = ocr.Read(imagePath).Text;
}
catch (Exception ex)
{
results[imagePath] = $"ERROR: {ex.Message}";
}
});
return results;
}
}
GdPicture 并行实现为每个线程创建三个对象,并且需要按线程注册许可证。 IronOCR可以从单个IronTesseract实例同时读取,无需同步开销。 多线程示例显示了高吞吐量文档处理工作负载的基准和Parallel.ForEach模式。
字节数组输入和结构化段落提取
从数据库或对象存储中检索文档的应用程序通常使用字节数组而不是文件路径。 GdPicture需要将字节数组转换为流并通过GdPictureImaging加载。 从结果中提取结构化的段落级数据需要浏览块/行索引层次结构。
GdPicture .NET方法:
using GdPicture14;
public class GdPictureByteArrayOcr
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
public List<string> ExtractParagraphsFromBytes(byte[] imageBytes)
{
var paragraphs = new List<string>();
// Byte array must go through MemoryStream to reach CreateGdPictureImageFromStream
using var ms = new MemoryStream(imageBytes);
int imageId = _imaging.CreateGdPictureImageFromStream(ms);
if (imageId == 0)
throw new Exception($"Byte array load failed: {_imaging.GetStat()}");
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId))
throw new Exception($"OCR failed: {_ocr.GetStat()}");
// Paragraph-level data requires iterating block structure
int blockCount = _ocr.GetOCRResultBlockCount(resultId);
for (int b = 0; b < blockCount; b++)
{
var blockText = new StringBuilder();
int lineCount = _ocr.GetOCRResultBlockLineCount(resultId, b);
for (int l = 0; l < lineCount; l++)
{
int wordCount = _ocr.GetOCRResultBlockLineWordCount(resultId, b, l);
for (int w = 0; w < wordCount; w++)
{
blockText.Append(_ocr.GetOCRResultBlockLineWordText(resultId, b, l, w));
blockText.Append(" ");
}
}
string text = blockText.ToString().Trim();
if (!string.IsNullOrEmpty(text))
paragraphs.Add(text);
}
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
return paragraphs;
}
}
IronOCR方法:
using IronOcr;
public class IronOcrByteArrayOcr
{
public List<string> ExtractParagraphsFromBytes(byte[] imageBytes)
{
using var input = new OcrInput();
input.LoadImage(imageBytes); // byte array accepted directly
var result = new IronTesseract().Read(input);
// Paragraphs are a first-class typed collection
return result.Paragraphs
.Select(p => p.Text)
.Where(t => !string.IsNullOrWhiteSpace(t))
.ToList();
}
}
IronOCR直接在MemoryStream。 结果将.Paragraphs暴露为可类型化的LINQ查询集合——无需块/行/词三重循环。 读取结果指南涵盖发票和表单处理工作流程的坐标访问、置信度过滤和结构化输出模式。 要扫描文档的特定区域,请参阅基于区域的 OCR 。
GdPicture .NET API 到IronOCR映射参考
| GdPicture.NET | IronOCR当量 |
|---|---|
using GdPicture14; | using IronOcr; |
LicenseManager.RegisterKEY("key") | IronOcr.License.LicenseKey = "key" |
new GdPictureImaging() | 不需要——内部OcrInput |
new GdPictureOCR() | new IronTesseract() |
new GdPicturePDF() | 不需要——OcrInput.LoadPdf()负责此事 |
_ocr.ResourceFolder = path | 不需要 — NuGet中已包含这些资源。 |
imaging.CreateGdPictureImageFromFile(path) | input.LoadImage(path) |
imaging.CreateGdPictureImageFromStream(stream) | input.LoadImage(stream) |
imaging.CreateGdPictureImageFromBytes(bytes) | input.LoadImage(bytes) |
imaging.CloneImage(tiffId) 每帧 | input.LoadImageFrames(tiffPath) |
imaging.ReleaseGdPictureImage(imageId) | using var input = new OcrInput()——自动 |
ocr.SetImage(imageId) | 不需要——OcrInput保持图像 |
ocr.Language = "eng" | ocr.Language = OcrLanguage.English |
ocr.RunOCR() → resultId 字符串 | ocr.Read(input) → 类型化OcrResult |
ocr.GetOCRResultText(resultId) | result.Text |
ocr.GetOCRResultConfidence(resultId) | result.Confidence |
ocr.GetOCRResultBlockCount(resultId) | result.Pages[i].Paragraphs.Count |
ocr.GetOCRResultBlockLineWordText(resultId, b, l, w) | result.Words[i].Text |
imaging.GetStat() / ocr.GetStat() | .NET Standard例外情况 |
每次调用后的GdPictureStatus.OK检查 | 不需要——异常会传播 |
pdf.OcrPage("eng", resourcePath, "", 200) | result.SaveAsSearchablePdf(outputPath) |
pdf.RenderPageToGdPictureImage(200, false) | 无需手动渲染 — IronOCR内部即可完成渲染 |
pdf.SelectPage(i) | 无需手动处理——所有页面默认都会处理。 |
pdf.GetPageCount() | result.Pages.Count |
Task.Run(() => ocr.RunOCR()) + SemaphoreSlim | await ocr.ReadAsync(input) |
常见迁移问题和解决方案
问题 1:重构后代码中仍残留图像 ID 变量
GdPicture.NET: 现有代码在方法顶部声明int imageId,通过try/finally跟踪,并传递给多个GdPicture调用。 在替换GdPicture调用后,这些变量及其ReleaseGdPictureImage调用将成为孤立的无效代码。
**解决方法:**删除整个图像 ID 模式。 用finally和释放调用。 使用Grep查找每个必须去除的清理调用的ReleaseGdPictureImage:
grep -rn "ReleaseGdPictureImage\|imageId\|resultId" --include="*.cs" .
// Remove all of this
int imageId = _imaging.CreateGdPictureImageFromFile(path);
try
{
_ocr.SetImage(imageId);
string resultId = _ocr.RunOCR();
return _ocr.GetOCRResultText(resultId);
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
// Replace with
using var input = new OcrInput();
input.LoadImage(path);
return new IronTesseract().Read(input).Text;
问题 2:已部署环境中找不到资源文件夹路径
GdPicture.NET: 在_ocr.ResourceFolder中设置的路径在开发机器上解析,但在生产中失败。 常见症状是无声的OCR故障返回空结果,或不命名缺失文件的通用GdPictureStatus错误。
解决方案: 完全去除ResourceFolder分配。IronOCRNuGet包中已内置英文支持。 其他语言将以NuGet包的形式安装。 没有需要配置或部署的文件系统路径:
问题 3:GdPictureStatus 错误处理已替换为异常处理
GdPicture.NET: 每个操作返回或设置一个GdPictureStatus枚举值,必须立即检查。 代码密集了if (status != GdPictureStatus.OK)守卫。 有些状态码是通用的(例如,InvalidParameter),需要查阅文档以确定根本原因。
解决方案: IronOCR会抛出类型化的.NET异常。 用 try/catch 代码块替换状态检查。 标准FileNotFoundException覆盖输入故障; IronOcr.Exceptions.OcrException覆盖OCR特定错误。 请参阅IronTesseract 设置指南,了解推荐的错误处理模式:
try
{
var result = new IronTesseract().Read(imagePath);
return result.Text;
}
catch (FileNotFoundException ex)
{
_logger.LogError("Input file missing: {Path}", ex.FileName);
throw;
}
catch (IronOcr.Exceptions.OcrException ex)
{
_logger.LogError("OCR processing failed: {Message}", ex.Message);
throw;
}
问题4: 多文件中的GdPicture14命名空间
GdPicture.NET: 命名空间中的版本号意味着全项目的using GdPicture14;指令存在于几十个文件中。 迁移后,这些必须全部替换为using IronOcr;。
解决方案: 在所有.cs文件上使用全局查找和替换,然后验证没有GdPicture引用残留:
# Find all files with GdPicture namespace
grep -rln "using GdPicture" --include="*.cs" .
# After replacing, verify nothing remains
grep -rn "GdPicture14\|GdPictureOCR\|GdPictureImaging\|GdPicturePDF" --include="*.cs" .
问题 5:TIFF 帧计数逻辑
GdPicture.NET: 迭代TIFF帧的代码通常混合了跨GdPicturePDF.GetPageCount()的调用,具体取决于文件的加载方式。 帧索引从 1 开始。
解决方案: input.LoadImageFrames(path)自动处理所有帧。 如果现有代码只处理特定帧,请使用input.LoadImageFrames(path, frameNumbers)和一个基于零的索引数组。 通过result.Pages访问每帧结果,后者也是零索引。 TIFF 输入指南明确记录了索引行为。
问题 6:预处理需要文档图像处理插件
GdPicture.NET: Deskew和Despeckle操作属于GdPictureDocumentImaging插件,需要单独购买许可证。 跳过插件的团队在扫描页面倾斜或有噪点的文档时,经常会遇到 OCR 准确性问题。
解决方案: IronOCR预处理方法是基础软件包的一部分。 在DeNoise()。 图像质量校正指南涵盖了所有可用的滤镜,包括DeepCleanBackgroundNoise(),用于严重退化的扫描件:
using var input = new OcrInput();
input.LoadImage("scanned-document.tiff");
input.Deskew(); // no separate plugin license
input.DeNoise();
input.Contrast();
var result = new IronTesseract().Read(input);
GdPicture .NET迁移清单
迁移前
在进行任何更改之前,请审核代码库,找出所有 GdPicture 依赖项:
# Find all GdPicture namespace imports
grep -rn "using GdPicture" --include="*.cs" .
# Find all image ID creation points
grep -rn "CreateGdPictureImageFromFile\|CreateGdPictureImageFromStream\|RenderPageToGdPictureImage\|CloneImage" --include="*.cs" .
# Find all release calls — these map to using block boundaries
grep -rn "ReleaseGdPictureImage" --include="*.cs" .
# Find all resource folder assignments
grep -rn "ResourceFolder" --include="*.cs" .
# Find all OCR result ID accesses
grep -rn "RunOCR\|GetOCRResult\|resultId" --include="*.cs" .
# Find all GdPictureStatus checks
grep -rn "GdPictureStatus\|GetStat()" --include="*.cs" .
# Count GdPicture-dependent files
grep -rln "GdPicture14" --include="*.cs" . | wc -l
修改代码前,请记录以下内容:
哪个文件包含GdPicturePDF引用
- 存在多少个不同的图像 ID 创建/发布对?
- 文档图像插件(倾斜校正、去斑点)是否正在使用
资源文件夹中的哪个语言
traineddata文件 - 哪些部署脚本或 Docker 文件引用了资源文件夹路径
代码迁移
- 从项目文件中移除所有 GdPicture NuGet包
- 通过NuGet安装
IronOcr - 为之前在资源文件夹中的每种语言安装
IronOcr.Languages.*包 - 添加
Startup.cs - 去除
LicenseManager.RegisterKEY()调用和许可证管理器对象 - 去除所有
GdPictureImaging字段声明和构造函数初始化 - 去除所有
ResourceFolder分配 - 去除用于OCR工作流程的所有
GdPicturePDF字段声明 - 用
using var input = new OcrInput();替换每个int imageId = _imaging.CreateGdPictureImage*(...)`块; input.Load*(...) - 替换每个
_ocr.SetImage(imageId); _ocr.Language = "..."; string resultId = _ocr.RunOCR();withvar result = new IronTesseract().Read(input); - 用
_ocr.GetOCRResultText(resultId) - 删除所有
_imaging.ReleaseGdPictureImage(imageId)调用 - 用try/catch块替换
GdPictureStatus检查 - 用
input.LoadImageFrames(path)替换TIFF帧循环 - 用
pdf.OcrPage(...)+pdf.SaveToFile(...) - 从部署脚本和 Docker 镜像中移除资源文件夹路径
- 在所有受影响的文件中将
using IronOcr;
后迁移
完成所有代码更改后,在部署到生产环境之前,请验证以下内容:
单图片OCR返回预期的文本,没有从已删除组件中出现的NullReferenceException
多页TIFF处理覆盖所有帧,并通过result.Pages产生每页文本
- PDF OCR 可处理所有页面,即使批量处理 50 多份文档也不会增加内存占用。
流输入接受
FileStream而无需中间文件写入 - 异步 OCR 与ASP.NET Core请求处理程序集成,不会阻塞线程池。
并行批处理处理使用
Parallel.ForEach在所有线程中产生准确的结果 所有语言包(法语、德语等)均可通过NuGet包正确激活。 - 预处理滤波器(去倾斜、去噪)可提高扫描文档的准确性
- 可搜索的 PDF 输出可被 PDF 查看器和文本搜索应用程序读取
迁移后,任何
.cs文件中没有GdPicture命名空间引用残留 - 内存分析显示,在持续负载下使用情况稳定(无内存泄漏)。
- 在 Linux 和 Docker 目标上部署成功,未出现文件系统路径错误
迁移到IronOCR的主要优势
编译器强制执行内存安全。GdPicture要求开发者手动维护的图像 ID 生命周期完全消失。 using块在每个作用域结束时强制执行清理——包括异常路径。 无需维护finally块,由于遗漏的释放调用而没有生产内存问题。
适用于每个OCR场景的单一包。 图像OCR,PDF OCR,多页TIFF处理,可搜索的PDF生成,预处理滤镜,条形码读取和125+种语言包都可以在IronOcr NuGet包及其语言伴随包中获得。 没有需要购买第二或第三个许可证才能实现常用工作流程的功能门槛。 请参阅IronOCR功能概述以获取完整的功能列表。
本地异步和线程安全。 Task.Run包装器。 IronTesseract可以在不进行同步的情况下跨线程使用。 以前需要每线程组件初始化和信号量序列化的文档处理服务简化为使用Parallel.ForEach的单一共享实例。 异步 OCR 指南涵盖了ASP.NET Core和托管服务模式。
零部署配置。IronOCR不需要文件系统路径、外部IronOCR文件、本地二进制文件或部署脚本。NuGetNuGet步骤会提供应用程序所需的一切。 Docker 镜像、Azure 应用服务部署和 Linux 服务器的工作方式与开发机器完全相同。Docker部署指南和Azure 部署指南演示了可用于生产环境的配置。
版本稳定的命名空间。 using IronOcr 在主要版本发布间没有改变。 NuGet版本号通过标准包管理模型处理版本控制。 未来的重大升级不需要对整个代码库中的命名空间导入进行查找和替换。
可预测的永久许可。 IronOCR定价为$999 (Lite)、$1,499 (Plus)、$2,399 (Professional)和$4,799 (Unlimited)-一次性永久购买包括一年更新,可选择每年续订。 所有功能在所有级别中均可使用。 没有按功能收费的插件,没有按页面收费,第一年之后也没有维护义务。 之前在仅用于 OCR 工作流程的 GdPicture 插件许可证上花费超过 8,000 美元的团队,可以在第一个许可证周期内弥补这一缺口。 完整的定价详情请参见IronOCR许可页面。
