IronOCR与 AWS Textract:面向初创公司的 OCR 解决方案
在您为OCR插件支付一美元之前,GdPicture.NET需要一个核心许可证——如果您想读取PDF或生成可搜索的输出,您还需要额外的PDF插件。 联系GdPicture获取最新价格。 对于需要从图像和PDF中提取文本的团队,多插件的综合成本加上年度维护费用可能会显著增加。 计费方式是 GdPicture 架构的直接结果:它是一个完整的文档图像处理平台,涵盖扫描、DICOM、注释、条形码处理和 PDF 编辑,而 OCR 只是众多插件之一。 仅需 OCR 功能的团队需要承担整个文档图像处理 SDK 的成本,而他们永远无法充分利用这些 SDK。
API也体现了同样的理念。 GdPicture使用整数图像ID来追踪内存中的资源——您加载的每个图像都会返回一个int,您对该整数句柄调用方法,然后您必须明确释放它或接受内存泄漏。 这种模式比using语句出现得更早。 它确实有效,但每次清理工作的决策都由开发者一人承担。本文对比分析了插件定价和图像 ID 生命周期这两个矛盾点,以及 API 复杂性、预处理和 PDF 处理等问题,并以IronOCR作为对比。
了解.NET
GdPicture .NET是由 ORPALIS 开发的商业文档图像处理 SDK。 经过多年的发展,它已成为一个涵盖图像处理、PDF 创建和编辑、条形码读取和生成、TWAIN/WIA 扫描仪集成、DICOM 医学成像、注释和审查工作流程以及符合 PDF/A 标准的文档归档的广泛平台。 OCR是该生态系统中的一项功能,通过专用插件实现。
SDK 将其功能组织成不同的模块,每个模块都有自己的主类:
- GdPictureImaging — 图像加载、处理、格式转换以及所有整数图像 ID 的来源
- GdPicturePDF — PDF 创建、编辑、渲染和页面级 OCR 操作(需要单独的插件许可证)
- GdPictureOCR — 文本识别和提取; 使用 GdPictureImaging 生成的图像 ID
- GdPicture1DBarcode / GdPicture2DBarcode — 一维和二维条形码的读取和写入(单独的插件许可)
- GdPictureDocumentImaging — 高级文档清理过滤器,包括倾斜校正、去斑点、去除边框和去除打孔(单独的插件)
- GdPictureAnnotations — 文档标记、审阅和编辑
即便是基础的OCR也需要实例化两个组件——GdPictureOCR用于识别文本,并管理它们的生命周期。 从PDF进行OCR需要第三个组件:GdPicturePDF。 命名空间本身包括一个版本号(GdPicture14),这意味着每次主要的SDK升级都会迫使全代码库的查找和替换。
常见 OCR 场景的插件依赖关系树如下所示:
| 如果您需要 | 您必须获得许可 |
|---|---|
| 从图像中识别光学字符 | 基础许可 + OCR 插件 |
| PDF文件的光学字符识别 | 基础版 + OCR 插件 + PDF 插件 |
| 可搜索的 PDF 输出 | 基础版 + OCR 插件 + PDF 插件 |
| OCR识别前的文档清理 | 基础版 + OCR 插件 + 文档图像处理插件 |
图像 ID 生命周期
GdPicture 的主要架构特点是整数图像 ID 管理。 加载任何图像会在GdPicture运行时内部分配内存并返回一个int句柄。 在所有后续调用中,您都将使用该整数来引用图像。 完成后,您用那个整数调用ReleaseGdPictureImage。 否则,内存将一直被占用,直到进程结束。
using GdPicture14;
// Every image load returns an integer handle
int imageId = _imaging.CreateGdPictureImageFromFile(imagePath);
if (imageId == 0)
{
throw new Exception($"Failed to load image: {_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
{
// CRITICAL: omitting this line causes a memory leak
_imaging.ReleaseGdPictureImage(imageId);
}
using GdPicture14;
// Every image load returns an integer handle
int imageId = _imaging.CreateGdPictureImageFromFile(imagePath);
if (imageId == 0)
{
throw new Exception($"Failed to load image: {_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
{
// CRITICAL: omitting this line causes a memory leak
_imaging.ReleaseGdPictureImage(imageId);
}
Imports GdPicture14
' Every image load returns an integer handle
Dim imageId As Integer = _imaging.CreateGdPictureImageFromFile(imagePath)
If imageId = 0 Then
Throw New Exception($"Failed to load image: {_imaging.GetStat()}")
End If
Try
_ocr.SetImage(imageId)
_ocr.Language = "eng"
Dim resultId As String = _ocr.RunOCR()
If String.IsNullOrEmpty(resultId) Then
Throw New Exception($"OCR failed: {_ocr.GetStat()}")
End If
Return _ocr.GetOCRResultText(resultId)
Finally
' CRITICAL: omitting this line causes a memory leak
_imaging.ReleaseGdPictureImage(imageId)
End Try
处理多页 PDF 时,每个渲染后的页面都会生成一个单独的图像 ID。 如果未进行清理,一份 100 页、200 DPI 的文档可能会占用 1-5 GB 的内存。 正确的模式要求收集所有ID并在finally块中释放它们——但这种纪律完全由开发人员负责。
了解IronOCR
IronOCR是一个面向.NET的商业 OCR 库,以单个NuGet包的形式构建。 其设计目标是使安装包、设置许可证密钥、调用Read的配置开销最小化,以保证准确性。 该库封装了一个优化的 Tesseract 5 引擎,具有自动预处理、原生 PDF 处理和结构化结果访问等功能,所有这些都通过标准的.NET模式公开。
主要特点:
- 单一NuGet包——
dotnet add package IronOcr安装所有内容,包括英语语言支持; 没有外部资源文件夹,也没有原生二进制文件管理 - 标准.NET资源管理——
IDisposable; 一个using语句处理所有清理,无论是否发生异常都一样。 - 自动预处理——Deskew, DeNoise, Contrast, Binarize和EnhanceResolution都是对
OcrInput的单行方法调用; 该引擎还会在没有明确指令的情况下进行自动纠错。 - 本地PDF输入——扫描的PDF和本地PDF都可直接输入到
IronTesseract.Read,无需单独的渲染步骤。 - 设计为线程安全——一个
IronTesseract实例处理并发请求;Parallel.ForEach无需额外同步即可运行。 - 125+种语言——作为独立的NuGet包分发(
IronOcr.Languages.French等),没有需要管理的tessdata文件夹。 - 结构化结果访问——
.Pages,.Lines,.Words,.Barcodes公开为带坐标数据的类型化集合。
定价为永久性:$999 Lite, $1,499 Plus, $2,999 Professional, $5,999 Unlimited。 所有功能在所有级别均可使用; 区别在于所覆盖的开发者数量和项目数量。
功能对比
| 特征 | GdPicture.NET | IronOCR |
|---|---|---|
| 定价模式 | 插件式永久授权 + 每年 20% 的维护费 | 永久免费,可选年度更新 |
| 入口费用(PDF OCR识别) | 需要多个插件(联系GdPicture获取价格) | $2,999 (一个包,所有功能) |
| 资源管理 | 手动整数 ID 跟踪和发布 | IDisposable / using 声明 |
| 命名空间稳定性 | 命名空间中的版本号(GdPicture14) |
无版本限制(IronOcr) |
| PDF OCR | 需要单独的 PDF 插件 | 内置,无需额外许可 |
| 安装 | 多个NuGet包 + 外部资源文件夹 | 单个NuGet,无外部文件 |
| 跨平台 | Windows、Linux、macOS | Windows、Linux、macOS、Docker、AWS、Azure |
详细功能对比
| 特征 | GdPicture.NET | IronOCR |
|---|---|---|
| 安装和设置 | ||
| NuGet安装 | 需要多个软件包 | dotnet add package IronOcr |
| 外部资源文件夹 | 必需(tessdata 风格的语言文件) | 非必需(已捆绑) |
| 许可证激活 | LicenseManager.RegisterKEY() |
IronOcr.License.LicenseKey = "..." |
| 组件初始化 | PDF OCR工作流程的3-4个类别 | new IronTesseract() |
| 命名空间正在进行重大升级 | 必须更新每个源文件 | 无需更改 |
| OCR功能 | ||
| 图像OCR | 是 | 是 |
| PDF OCR | 是的(需要PDF插件) | 是的(内置) |
| 受密码保护的PDF | 是 | 是 |
| 多页PDF | 手动页面循环 + ID 清理 | 自动翻译 |
| 可搜索的 PDF 输出 | OcrPage + SaveToFile |
result.SaveAsSearchablePdf() |
| hOCR导出 | 否 | 是 |
| 预处理 | ||
| 德斯丘 | 通过 GdPictureDocumentImaging 插件 | input.Deskew() |
| 降噪 | 通过 GdPictureDocumentImaging 插件 | input.DeNoise() |
| 对比度增强 | 通过 GdPictureImaging | input.Contrast() |
| 二值化 | 通过 GdPictureImaging | input.Binarize() |
| DPI增强 | 通过 GdPictureImaging | input.EnhanceResolution(300) |
| 自动纠错(无需代码) | 否 | 是的(内置于 Read 中) |
| 资源管理 | ||
| 内存管理模型 | 手动整数 ID 跟踪 | IDisposable / GC |
| 内存泄漏风险 | 高(缺少 ReleaseGdPictureImage) | 无(使用语句) |
| 螺纹安全 | 手动实例管理 | 线程安全设计 |
| 结果和数据 | ||
| 纯文本 | GetOCRResultText(resultId) |
result.Text |
| 置信度得分 | GetOCRResultConfidence(resultId) |
result.Confidence |
| 词级数据 | 嵌套块/行/词迭代 | result.Words 集合 |
| 坐标访问 | 通过块/行/词索引调用 | 直接.X, .Y, .Width, .Height |
| OCR过程中的条形码读取 | 通过单独的条形码插件 | ocr.Configuration.ReadBarCodes = true |
| 语言 | ||
| 语言数量 | 基于 Tesseract 的类似覆盖范围 | 通过NuGet包提供 125 多个包 |
| 语言分布 | 资源文件夹文件 | NuGet包 |
| 一次完成多语言输入 | 是 | 是的 (AddSecondaryLanguage) |
| 部署 | ||
| 多克 | 支持 | 已支持(有文档记录) |
| Linux | 支持 | 支持 |
| AWS Lambda | 可能的 | 文档指南 |
| Azure 应用服务 | 可能的 | 文档指南 |
图像 ID 生命周期与一次性 OCR 输入
GdPicture 和IronOCR之间最重要的架构差异不是功能上的差距,而是每个库如何处理它所处理的图像的内存。
GdPicture 方法
GdPicture 中的每个图像都以整数句柄的形式存在于运行时管理的池中。 您可以通过加载图像来获取句柄,将该整数传递给 OCR 和其他操作,然后在完成后释放它。 按照这个模式,结果清晰明了,而且是正确的。 问题在于很容易出错,其后果是内存无限增长。
PDF OCR 工作流程最清楚地展现了这种风险。 每次调用pdf.RenderPageToGdPictureImage()都会在内存中分配一个新的光栅图像并返回一个新的整数ID。 一份100页的文件会产生100个独立的分配方案。 漏掉一个ReleaseGdPictureImage调用,进程便会积累泄漏的内存:
using GdPicture14;
public string ExtractTextFromPdf(string pdfPath)
{
var text = new StringBuilder();
using var pdf = new GdPicturePDF();
GdPictureStatus status = pdf.LoadFromFile(pdfPath, false);
if (status != GdPictureStatus.OK)
throw new Exception($"Failed to load PDF: {status}");
int pageCount = pdf.GetPageCount();
for (int i = 1; i <= pageCount; i++)
{
pdf.SelectPage(i);
// Each page render = new memory allocation + new integer ID
int imageId = pdf.RenderPageToGdPictureImage(200, false);
if (imageId == 0) continue;
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (!string.IsNullOrEmpty(resultId))
text.AppendLine(_ocr.GetOCRResultText(resultId));
}
finally
{
// Without this: ~10–50MB leaked per page
_imaging.ReleaseGdPictureImage(imageId);
}
}
return text.ToString();
}
using GdPicture14;
public string ExtractTextFromPdf(string pdfPath)
{
var text = new StringBuilder();
using var pdf = new GdPicturePDF();
GdPictureStatus status = pdf.LoadFromFile(pdfPath, false);
if (status != GdPictureStatus.OK)
throw new Exception($"Failed to load PDF: {status}");
int pageCount = pdf.GetPageCount();
for (int i = 1; i <= pageCount; i++)
{
pdf.SelectPage(i);
// Each page render = new memory allocation + new integer ID
int imageId = pdf.RenderPageToGdPictureImage(200, false);
if (imageId == 0) continue;
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (!string.IsNullOrEmpty(resultId))
text.AppendLine(_ocr.GetOCRResultText(resultId));
}
finally
{
// Without this: ~10–50MB leaked per page
_imaging.ReleaseGdPictureImage(imageId);
}
}
return text.ToString();
}
Imports GdPicture14
Imports System.Text
Public Function ExtractTextFromPdf(ByVal pdfPath As String) As String
Dim text As New StringBuilder()
Using pdf As New GdPicturePDF()
Dim status As GdPictureStatus = pdf.LoadFromFile(pdfPath, False)
If status <> GdPictureStatus.OK Then
Throw New Exception($"Failed to load PDF: {status}")
End If
Dim pageCount As Integer = pdf.GetPageCount()
For i As Integer = 1 To pageCount
pdf.SelectPage(i)
' Each page render = new memory allocation + new integer ID
Dim imageId As Integer = pdf.RenderPageToGdPictureImage(200, False)
If imageId = 0 Then Continue For
Try
_ocr.SetImage(imageId)
_ocr.Language = "eng"
Dim resultId As String = _ocr.RunOCR()
If Not String.IsNullOrEmpty(resultId) Then
text.AppendLine(_ocr.GetOCRResultText(resultId))
End If
Finally
' Without this: ~10–50MB leaked per page
_imaging.ReleaseGdPictureImage(imageId)
End Try
Next
End Using
Return text.ToString()
End Function
批量处理多个PDF文件会加剧这个问题。 每个finally中释放它们。 那是基础设施建设工作,不是OCR识别工作。
IronOCR方法
IDisposable。 一个using语句保证无论方法是正常返回还是抛出异常都能清理。 无需跟踪整数 ID,无需初始化单独的图像组件,也无需对 PDF 页面进行外部 DPI 渲染步骤。
using IronOcr;
public string ExtractTextFromPdf(string pdfPath)
{
// LoadPdf handles page rendering internally
using var input = new OcrInput();
input.LoadPdf(pdfPath);
var result = new IronTesseract().Read(input);
foreach (var page in result.Pages)
Console.WriteLine($"Page {page.PageNumber}: {page.Text}");
return result.Text;
// using block exits — all internal resources released automatically
}
using IronOcr;
public string ExtractTextFromPdf(string pdfPath)
{
// LoadPdf handles page rendering internally
using var input = new OcrInput();
input.LoadPdf(pdfPath);
var result = new IronTesseract().Read(input);
foreach (var page in result.Pages)
Console.WriteLine($"Page {page.PageNumber}: {page.Text}");
return result.Text;
// using block exits — all internal resources released automatically
}
Imports IronOcr
Public Function ExtractTextFromPdf(pdfPath As String) As String
' LoadPdf handles page rendering internally
Using input As New OcrInput()
input.LoadPdf(pdfPath)
Dim result = New IronTesseract().Read(input)
For Each page In result.Pages
Console.WriteLine($"Page {page.PageNumber}: {page.Text}")
Next
Return result.Text
' using block exits — all internal resources released automatically
End Using
End Function
对于受密码保护的 PDF 文件,该参数是一个命名参数,而不是需要额外状态检查的单独重载:
using var input = new OcrInput();
input.LoadPdf("encrypted.pdf", Password: "secret");
var result = new IronTesseract().Read(input);
result.SaveAsSearchablePdf("searchable-output.pdf");
using var input = new OcrInput();
input.LoadPdf("encrypted.pdf", Password: "secret");
var result = new IronTesseract().Read(input);
result.SaveAsSearchablePdf("searchable-output.pdf");
Imports IronOcr
Using input As New OcrInput()
input.LoadPdf("encrypted.pdf", Password:="secret")
Dim result = New IronTesseract().Read(input)
result.SaveAsSearchablePdf("searchable-output.pdf")
End Using
IronOCR PDF 输入指南涵盖了其他选项,包括页面范围选择。 对于已经熟悉跨Entity Framework、HttpClient和流处理的OcrInput不需要新的心智模型。
插件捆绑费用
对于从图像和PDF中提取文本并生成可搜索输出的服务器应用,GdPicture许可证堆栈需要多个插件。 联系GdPicture获取Professional核心许可证、OCR插件和PDF插件的最新价格。 年度维护费用(通常是总许可证费用的一个百分比)随着时间的推移而累积。
IronOCR Professional在一个软件包中涵盖了相同的工作流程:
IronOCR Professional: $professionalLicense (一次性)
可选年度更新: $plusLicense/年
3年总计(永久,无更新):$professionalLicense
多年的成本差异可能很大,具体取决于更新偏好。 之所以存在这种差距,是因为 GdPicture 的插件模式是针对完整的文档图像处理平台定价的,而不是针对以 OCR 为中心的工作负载。 在使用最常见的OCR输入格式生成其可用之前,需要OCR插件和PDF插件。
评估 GdPicture 的 OCR 功能的团队需要为一些在以 OCR 为中心的待办事项清单中很少出现的功能付费:TWAIN/WIA 扫描仪集成、DICOM 医学成像、注释和编辑工作流程以及除文本提取之外的高级 PDF 编辑。 请参见IronOCR许可页面以获取所有功能在每个层级都可用的定价模型——没有插件,没有附加组件,没有按功能许可决策。
API复杂性和预处理
GdPicture通过GdPictureDocumentImaging插件(去斜、去污点、去除边框)公开预处理功能。 在 OCR 之前应用倾斜校正需要文档图像插件许可证,这会在处理质量参差不齐的扫描文档的任何工作流程中增加第三项成本。
GdPicture 方法
低质量扫描 PDF 的完整工作流程——加载、预处理、逐页 OCR 识别、发布——涵盖三个组件类别,并且需要在每个步骤中仔细检查状态:
using GdPicture14;
public void CreateSearchablePdf(string inputPdf, string outputPdf)
{
using var pdf = new GdPicturePDF();
GdPictureStatus status = pdf.LoadFromFile(inputPdf, false);
if (status != GdPictureStatus.OK)
throw new Exception($"Load failed: {status}");
int pageCount = pdf.GetPageCount();
for (int i = 1; i <= pageCount; i++)
{
pdf.SelectPage(i);
// OcrPage adds text layer to each page
// Requires: OCR plugin + PDF plugin + resource folder path
GdPictureStatus ocrStatus = pdf.OcrPage(
"eng",
@"C:\GdPicture\Resources\OCR", // 外部资源文件夹 required
"",
200
);
if (ocrStatus != GdPictureStatus.OK)
Console.WriteLine($"Warning: Page {i} OCR failed: {ocrStatus}");
}
pdf.SaveToFile(outputPdf, true);
}
using GdPicture14;
public void CreateSearchablePdf(string inputPdf, string outputPdf)
{
using var pdf = new GdPicturePDF();
GdPictureStatus status = pdf.LoadFromFile(inputPdf, false);
if (status != GdPictureStatus.OK)
throw new Exception($"Load failed: {status}");
int pageCount = pdf.GetPageCount();
for (int i = 1; i <= pageCount; i++)
{
pdf.SelectPage(i);
// OcrPage adds text layer to each page
// Requires: OCR plugin + PDF plugin + resource folder path
GdPictureStatus ocrStatus = pdf.OcrPage(
"eng",
@"C:\GdPicture\Resources\OCR", // 外部资源文件夹 required
"",
200
);
if (ocrStatus != GdPictureStatus.OK)
Console.WriteLine($"Warning: Page {i} OCR failed: {ocrStatus}");
}
pdf.SaveToFile(outputPdf, true);
}
Imports GdPicture14
Public Sub CreateSearchablePdf(inputPdf As String, outputPdf As String)
Using pdf As New GdPicturePDF()
Dim status As GdPictureStatus = pdf.LoadFromFile(inputPdf, False)
If status <> GdPictureStatus.OK Then
Throw New Exception($"Load failed: {status}")
End If
Dim pageCount As Integer = pdf.GetPageCount()
For i As Integer = 1 To pageCount
pdf.SelectPage(i)
' OcrPage adds text layer to each page
' Requires: OCR plugin + PDF plugin + resource folder path
Dim ocrStatus As GdPictureStatus = pdf.OcrPage(
"eng",
"C:\GdPicture\Resources\OCR", ' 外部资源文件夹 required
"",
200
)
If ocrStatus <> GdPictureStatus.OK Then
Console.WriteLine($"Warning: Page {i} OCR failed: {ocrStatus}")
End If
Next
pdf.SaveToFile(outputPdf, True)
End Using
End Sub
ResourceFolder路径是不可选的。 它必须在运行时解析到一个包含C:\GdPicture\Resources\OCR时、或应用程序部署到Linux时、或Docker镜像不包含该目录结构时,在生产中失败。 调试这些失败常常意味着追踪通用GdPictureStatus错误代码,而这些错误代码极少说明根本原因。
IronOCR方法
IronOCR中等效的工作流程(包括去斜校正和去噪等预处理步骤)只有五行代码:
using IronOcr;
public void CreateSearchablePdf(string inputPdf, string outputPdf)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf(inputPdf);
input.Deskew(); // 否 separate plugin required
input.DeNoise();
var result = ocr.Read(input);
result.SaveAsSearchablePdf(outputPdf);
}
using IronOcr;
public void CreateSearchablePdf(string inputPdf, string outputPdf)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf(inputPdf);
input.Deskew(); // 否 separate plugin required
input.DeNoise();
var result = ocr.Read(input);
result.SaveAsSearchablePdf(outputPdf);
}
Imports IronOcr
Public Sub CreateSearchablePdf(inputPdf As String, outputPdf As String)
Dim ocr = New IronTesseract()
Using input = New OcrInput()
input.LoadPdf(inputPdf)
input.Deskew() ' 否 separate plugin required
input.DeNoise()
Dim result = ocr.Read(input)
result.SaveAsSearchablePdf(outputPdf)
End Using
End Sub
NuGet包中已嵌入英语语言支持。 额外的语言作为NuGet包安装(.traineddata文件。 图像质量校正指南和图像方向校正指南涵盖了OcrInput上的所有预处理选项。
对于置信度感知处理(例如,在写入数据库之前过滤掉低于阈值的结果),结果对象直接公开置信度:
var result = new IronTesseract().Read("scanned.jpg");
Console.WriteLine($"Confidence: {result.Confidence}%");
// Per-word confidence for fine-grained filtering
var highConfidenceWords = result.Words
.Where(w => w.Confidence > 85)
.Select(w => w.Text);
var result = new IronTesseract().Read("scanned.jpg");
Console.WriteLine($"Confidence: {result.Confidence}%");
// Per-word confidence for fine-grained filtering
var highConfidenceWords = result.Words
.Where(w => w.Confidence > 85)
.Select(w => w.Text);
Imports System
Imports System.Linq
Dim result = (New IronTesseract()).Read("scanned.jpg")
Console.WriteLine($"Confidence: {result.Confidence}%")
' Per-word confidence for fine-grained filtering
Dim highConfidenceWords = result.Words _
.Where(Function(w) w.Confidence > 85) _
.Select(Function(w) w.Text)
GdPicture需要三个嵌套循环——块、行、词——才能达到相同的数据,每层都有基于索引的方法调用(GetOCRResultBlockLineWordConfidence(resultId, b, l, w))。 置信度评分操作指南演示了常见用例的结构化访问模型。
结构化数据和结果访问
提取每个单词的坐标和置信度值是文档理解工作流程的常见要求——发票字段提取、表单处理和布局分析都需要它。
GdPicture 方法
GdPicture从GdPictureOCR实例上通过方法调用查询。 要达到逐词置信度,需要迭代三个嵌套层级:
using GdPicture14;
public List<(string word, float confidence)> GetWordConfidences(int imageId)
{
var words = new List<(string, float)>();
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
int blockCount = _ocr.GetOCRResultBlockCount(resultId);
for (int b = 0; b < blockCount; b++)
{
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++)
{
string word = _ocr.GetOCRResultBlockLineWordText(resultId, b, l, w);
float conf = _ocr.GetOCRResultBlockLineWordConfidence(resultId, b, l, w);
words.Add((word, conf));
}
}
}
return words;
}
using GdPicture14;
public List<(string word, float confidence)> GetWordConfidences(int imageId)
{
var words = new List<(string, float)>();
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
int blockCount = _ocr.GetOCRResultBlockCount(resultId);
for (int b = 0; b < blockCount; b++)
{
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++)
{
string word = _ocr.GetOCRResultBlockLineWordText(resultId, b, l, w);
float conf = _ocr.GetOCRResultBlockLineWordConfidence(resultId, b, l, w);
words.Add((word, conf));
}
}
}
return words;
}
Imports GdPicture14
Public Function GetWordConfidences(imageId As Integer) As List(Of (word As String, confidence As Single))
Dim words As New List(Of (String, Single))()
_ocr.SetImage(imageId)
_ocr.Language = "eng"
Dim resultId As String = _ocr.RunOCR()
Dim blockCount As Integer = _ocr.GetOCRResultBlockCount(resultId)
For b As Integer = 0 To blockCount - 1
Dim lineCount As Integer = _ocr.GetOCRResultBlockLineCount(resultId, b)
For l As Integer = 0 To lineCount - 1
Dim wordCount As Integer = _ocr.GetOCRResultBlockLineWordCount(resultId, b, l)
For w As Integer = 0 To wordCount - 1
Dim word As String = _ocr.GetOCRResultBlockLineWordText(resultId, b, l, w)
Dim conf As Single = _ocr.GetOCRResultBlockLineWordConfidence(resultId, b, l, w)
words.Add((word, conf))
Next
Next
Next
Return words
End Function
这并没有错——数据是可以获取的。 但这需要理解和维护每个需要词级数据的项目的三维结果结构索引。
IronOCR方法
.Words公开为一个类型化集合。 LINQ 正好与之相反:
using IronOcr;
var result = new IronTesseract().Read("invoice.jpg");
// All words with position and confidence
foreach (var word in result.Words)
{
Console.WriteLine($"'{word.Text}' at ({word.X},{word.Y}) — {word.Confidence:P0}");
}
// Lines, paragraphs, and pages follow the same pattern
foreach (var line in result.Lines)
Console.WriteLine(line.Text);
foreach (var page in result.Pages)
Console.WriteLine($"Page {page.PageNumber}: {page.Text.Length} characters");
using IronOcr;
var result = new IronTesseract().Read("invoice.jpg");
// All words with position and confidence
foreach (var word in result.Words)
{
Console.WriteLine($"'{word.Text}' at ({word.X},{word.Y}) — {word.Confidence:P0}");
}
// Lines, paragraphs, and pages follow the same pattern
foreach (var line in result.Lines)
Console.WriteLine(line.Text);
foreach (var page in result.Pages)
Console.WriteLine($"Page {page.PageNumber}: {page.Text.Length} characters");
Imports IronOcr
Dim result = New IronTesseract().Read("invoice.jpg")
' All words with position and confidence
For Each word In result.Words
Console.WriteLine($"'{word.Text}' at ({word.X},{word.Y}) — {word.Confidence:P0}")
Next
' Lines, paragraphs, and pages follow the same pattern
For Each line In result.Lines
Console.WriteLine(line.Text)
Next
For Each page In result.Pages
Console.WriteLine($"Page {page.PageNumber}: {page.Text.Length} characters")
Next
基于区域的 OCR(从文档的特定矩形区域提取文本)同样直接:
var region = new CropRectangle(0, 0, 600, 100); // header area
using var input = new OcrInput();
input.LoadImage("invoice.jpg", region);
var headerText = new IronTesseract().Read(input).Text;
var region = new CropRectangle(0, 0, 600, 100); // header area
using var input = new OcrInput();
input.LoadImage("invoice.jpg", region);
var headerText = new IronTesseract().Read(input).Text;
Imports IronOcr
Dim region As New CropRectangle(0, 0, 600, 100) ' header area
Using input As New OcrInput()
input.LoadImage("invoice.jpg", region)
Dim headerText As String = New IronTesseract().Read(input).Text
End Using
读取结果指南和基于区域的 OCR 指南展示了这些模式如何扩展到多页文档和结构化字段提取工作流程。
API 映射参考
| GdPicture.NET | IronOCR当量 |
|---|---|
LicenseManager.RegisterKEY("key") |
IronOcr.License.LicenseKey = "key" |
new GdPictureImaging() |
无需处理——内部处理 |
new GdPictureOCR() |
new IronTesseract() |
new GdPicturePDF() |
不需要——OcrInput.LoadPdf()处理这个 |
ocr.ResourceFolder = path |
不需要 — NuGet中已包含这些资源。 |
imaging.CreateGdPictureImageFromFile(path) |
input.LoadImage(path) 在 OcrInput |
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.GetOCRResultBlockLineWordText(resultId, b, l, w) |
result.Words[i].Text |
ocr.GetOCRResultBlockLineWordConfidence(resultId, b, l, w) |
result.Words[i].Confidence |
pdf.LoadFromFile(path, false) → GdPictureStatus |
input.LoadPdf(path)——失败时抛出 |
pdf.LoadFromFile(path, password) |
input.LoadPdf(path, Password: password) |
pdf.RenderPageToGdPictureImage(200, false) |
无需手动渲染 — IronOCR内部即可完成渲染 |
pdf.SelectPage(i) |
无需手动处理——所有页面默认都会处理。 |
pdf.GetPageCount() |
不需要——或result.Pages.Count |
pdf.OcrPage("eng", resourcePath, "", 200) |
result.SaveAsSearchablePdf(outputPath) |
pdf.SaveToFile(outputPath, true) |
result.SaveAsSearchablePdf(outputPath) |
imaging.GetStat() / ocr.GetStat() |
.NET Standard例外情况 |
每次调用后检查GdPictureStatus.OK |
无需处理——异常会正常传播。 |
using GdPicture14; |
using IronOcr; |
当团队考虑从 GdPicture .NET迁移到IronOCR时
OCR是唯一要求
实施文档导入管道(扫描发票导入、医疗表格处理、合同文本提取)的团队经常会评估 GdPicture,因为它与 LEADTOOLS 和 Kofax 一起出现在Enterprise软件目录中。 一旦插件依赖关系表变得清晰,情况就发生了变化。 如果需要输入 PDF 文件,则 PDF 插件是必不可少的。 如果需要进行预处理,则文档图像处理插件是必需的。 需要具有去斜校正的扫描PDF的OCR的团队最终在专业入门成本上获得三个插件许可证。当需求是文本提取时,而没有超出GdPicture功能列表的需求时,该定价就代表了纯粹的开销。 在$2,999 Professional的IronOCR覆盖了相同的工作流——图像输入、PDF输入、预处理、可搜索PDF输出——一个包中没有插件决策的提供。
生产环境中的内存泄漏事件
在高吞吐量文档处理服务中,整数 ID 生命周期会成为随时可能发生的生产事故。 在负载较高的情况下处理上传的 PDF 的 Web API 端点最终会出现一个代码路径,其中图像释放调用不会被执行——错误分支中的提前返回、清理块之前未处理的异常,或者仅仅是不熟悉该模式的新开发人员添加了一个以整数图像句柄为参数的辅助方法,但忘记释放它。 内存占用不断增长,服务运行速度变慢,而堆转储文件中却难以找到根本原因。曾经调试过此类问题的团队通常希望采用运行时自动清理的模型。IronOCR的标准处置模式正是如此——由编译器强制执行结构化清理,而不是由开发人员在每个调用点手动清理。
版本升级摩擦
GdPicture 将主版本号嵌入到其命名空间中。 从版本 14 升级到版本 15 需要更新每个引用 GdPicture 类的源文件中的命名空间指令。 对于一个包含 OCR、PDF 处理和图像处理等功能,且分布在数十个服务和实用程序类中的大型应用程序来说,这是一个非同寻常的迁移任务,而且没有任何功能上的好处。 从 Microsoft.EntityFrameworkCore 到 Newtonsoft.Json,所有现代.NET库都使用与版本无关的命名空间,而是通过NuGet包版本号来管理版本控制。 IronOCR遵循该约定:从版本 1 到当前版本,相同的命名空间导入都适用。
微服务和容器部署
GdPicture OCR 要求运行时必须存在一个位于特定路径的外部资源文件夹,该文件夹包含语言数据文件。在容器化部署中,这意味着需要将这些文件打包到多克镜像中、挂载一个卷,或者配置一个启动脚本来下载它们。 典型的结果是多克镜像比实际需要的大几百兆字节,部署清单中的一项被遗漏,以及在干净的容器重启时,由于卷未挂载而导致生产环境失败。 IronOCR软件包本身就包含了英语支持; 其他语言将作为NuGet包安装,这些包是构建输出的一部分。 容器从恢复步骤中获取所需内容,无需配置任何路径。 Docker部署指南和Linux部署指南分别涵盖了每个目标环境的具体配置。
缺乏文档影像专业知识的开发团队
GdPicture 的表面积很大,这是必然的——它覆盖了整个文档成像平台。 对于加入团队维护 OCR 微服务的开发人员来说,了解图像组件、OCR 组件、PDF 组件、整数 ID 生命周期、状态返回代码处理和资源文件夹配置是一项巨大的初始投资。IronOCR的 OCR 表面区域分为三类:引擎、输入容器和结果对象。 这就是90%使用场景的完整思维模型。 对于新开发人员来说,入门意味着引导他们进行IronTesseract 设置,而不是指导他们使用多模块 SDK。
常见迁移注意事项
替换组件初始化
GdPicture需要一个四步初始化序列:通过GdPictureOCR,并设置资源文件夹路径。 IronOCR将其减少为一项单独的属性赋值,该属性应在Startup.cs中。
// Remove
LicenseManager lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
_imaging = new GdPictureImaging();
_ocr = new GdPictureOCR();
_ocr.ResourceFolder = @"C:\GdPicture\Resources\OCR";
// Replace with
IronOcr.License.LicenseKey = "IRONOCR-LICENSE-KEY";
// IronTesseract instances are instantiated per-use or as a singleton
// Remove
LicenseManager lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
_imaging = new GdPictureImaging();
_ocr = new GdPictureOCR();
_ocr.ResourceFolder = @"C:\GdPicture\Resources\OCR";
// Replace with
IronOcr.License.LicenseKey = "IRONOCR-LICENSE-KEY";
// IronTesseract instances are instantiated per-use or as a singleton
' Remove
Dim lm As New LicenseManager()
lm.RegisterKEY("GDPICTURE-LICENSE-KEY")
_imaging = New GdPictureImaging()
_ocr = New GdPictureOCR()
_ocr.ResourceFolder = "C:\GdPicture\Resources\OCR"
' Replace with
IronOcr.License.LicenseKey = "IRONOCR-LICENSE-KEY"
' IronTesseract instances are instantiated per-use or as a singleton
消除图像ID跟踪
每个ReleaseGdPictureImage调用。 这些清理调用在IronOCR中完全移除——OcrInput处理释放。 迁移是机械的:找出每个using var input = new OcrInput()中,并用相应的IronOCR方法替换GdPicture方法调用。
// Remove
int imageId = _imaging.CreateGdPictureImageFromFile(path);
if (imageId == 0) throw new Exception(_imaging.GetStat().ToString());
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId)) throw new Exception(_ocr.GetStat().ToString());
string text = _ocr.GetOCRResultText(resultId);
_imaging.ReleaseGdPictureImage(imageId);
// Replace with
using var input = new OcrInput();
input.LoadImage(path);
var result = new IronTesseract().Read(input);
string text = result.Text;
// Remove
int imageId = _imaging.CreateGdPictureImageFromFile(path);
if (imageId == 0) throw new Exception(_imaging.GetStat().ToString());
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId)) throw new Exception(_ocr.GetStat().ToString());
string text = _ocr.GetOCRResultText(resultId);
_imaging.ReleaseGdPictureImage(imageId);
// Replace with
using var input = new OcrInput();
input.LoadImage(path);
var result = new IronTesseract().Read(input);
string text = result.Text;
Imports System
' Remove
Dim imageId As Integer = _imaging.CreateGdPictureImageFromFile(path)
If imageId = 0 Then Throw New Exception(_imaging.GetStat().ToString())
_ocr.SetImage(imageId)
_ocr.Language = "eng"
Dim resultId As String = _ocr.RunOCR()
If String.IsNullOrEmpty(resultId) Then Throw New Exception(_ocr.GetStat().ToString())
Dim text As String = _ocr.GetOCRResultText(resultId)
_imaging.ReleaseGdPictureImage(imageId)
' Replace with
Using input As New OcrInput()
input.LoadImage(path)
Dim result = New IronTesseract().Read(input)
Dim text As String = result.Text
End Using
适应错误处理
GdPicture在每个重要操作后检查GdPictureStatus返回代码。 IronOCR会抛出标准的.NET异常。 迁移用FileNotFoundException。 从图像中读取文本的教程涵盖了常见场景中的错误处理模式。
语言包部署
GdPicture语言文件是文件系统文件夹中的.traineddata文件。 IronOCR语言包是NuGet包。 迁移操作会从部署清单中移除资源文件夹,并为每种所需的语言添加NuGet包引用:
# Remove filesystem dependency for each language
# Add NuGet package instead
dotnet add package IronOcr.Languages.French
dotnet add package IronOcr.Languages.German
dotnet add package IronOcr.Languages.ChineseSimplified
# Remove filesystem dependency for each language
# Add NuGet package instead
dotnet add package IronOcr.Languages.French
dotnet add package IronOcr.Languages.German
dotnet add package IronOcr.Languages.ChineseSimplified
多语言指南涵盖了关于在IronTesseract实例上指定主语言和次语言的多语言文档的配置。
其他IronOCR功能
除了本次比较涵盖的领域之外, IronOCR还包含一些在纯 OCR 环境下 GdPicture 没有直接对应功能的特性:
— 异步OCR— 为ASP.NET Core请求处理程序和后台服务中的非阻塞操作提供ReadAsync
-页面旋转检测——无需人工预处理即可自动检测和校正旋转页面
-进度跟踪— 针对长时间运行的批处理作业,通过回调函数触发进度事件,并在用户界面上显示进度条。
.NET兼容性和未来准备情况
IronOCR 的目标平台为.NET 8、 .NET 9、 .NET Standard 2.0 和.NET Framework 4.6.2 及更高版本。 单个NuGet包在 Windows x64、Windows x86、Linux x64 和 macOS 上的部署方式完全相同。 GdPicture .NET也支持跨平台目标,但其插件架构意味着Linux和容器部署必须单独考虑每个插件的本地依赖项。 IronOCR的命名空间在所有主要版本更改中保持IronOcr,这意味着升级路径不需要代码库范围内的查找和替换操作。 随着.NET 10 于 2026 年底发布,IronOCR 在连续的.NET版本之间保持兼容性而不破坏 API 更改方面有着良好的记录,这为具有较长维护周期的应用程序提供了稳定的基础。
结论
对于需要统一的文档图像处理平台的团队来说,GdPicture .NET是一个合理的选择——在一个 SDK 中,来自同一供应商,集成了扫描仪集成、PDF 编辑、注释、DICOM 支持和 OCR 功能。 这种使用场景确实存在。 当大多数功能都实际包含在功能范围内时,插件模式就很有意义了。
当要求是 OCR 而答案是 GdPicture 时,就会出现不匹配的情况。 PDF 插件与从 PDF 进行 OCR 识别的使用场景密不可分。 文档图像处理插件与预处理密不可分。 整数 ID 生命周期不是可选项——它是整个 SDK 的基本资源管理模型。 需要从扫描的 PDF 文件中提取文本的团队需要承担所有这些复杂性和成本,但额外的功能对于他们的工作量来说并不值得。
单开发者的专业工作流程在多年的定价差距可能非常大。 代码复杂度差距是可以衡量的:基本 OCR 加清理需要 25 行以上代码,而清理只需要 1 行。 操作上的差距——外部资源文件夹、版本特定的命名空间、多组件初始化——表现为部署摩擦和上手时间,而不是代码行数,但它是真实存在的。
IronOCR在$2,999 永久性提供了相同的核心OCR成果——图像文字提取、扫描PDF处理、可搜索PDF输出、125+种语言、置信度评分、结构化数据访问——通过标准.NET模式,无需特殊的SDK知识即可维护。 对于那些以 OCR 为目标,而不是将其作为更广泛的文档管理平台的一个组件的团队来说,这才是更合适的选择。
常见问题解答
什么是 GdPicture.NET OCR?
GdPicture.NET OCR 是一款 OCR 解决方案,开发者和企业可以使用它从图像和文档中提取文本。它是与 IronOCR 一起评估的几种适用于 .NET 应用程序开发的 OCR 方案之一。
对于 .NET 开发人员来说,IronOCR 与 GdPicture.NET OCR 相比如何?
IronOCR 是一个基于 NuGet 的 .NET OCR 库,它使用 IronTesseract 作为核心引擎。与 GdPicture.NET OCR 相比,它提供更简单的部署方式(无需 SDK 安装程序)、统一的定价模式以及简洁的 C# API,无需 COM 互操作或云依赖。
IronOCR 比 GdPicture.NET OCR 更容易设置吗?
IronOCR 通过单个 NuGet 包进行安装。无需 SDK 安装程序、复制许可证文件、注册 COM 组件或管理单独的运行时二进制文件。整个 OCR 引擎都打包在包中。
GdPicture.NET OCR 和 IronOCR 在准确率方面存在哪些差异?
IronOCR 对标准商务文档、发票、收据和扫描表格的识别准确率很高。对于严重损坏的文档或不常见的文字,识别准确率会因源文件质量而异。IronOCR 包含图像预处理滤镜,可提高低质量输入文件的识别率。
IronOCR是否支持PDF文本提取?
是的。IronOCR只需一次调用即可从原生PDF和扫描的PDF图像中提取文本。它还支持多页TIFF文件、图像和流。对于扫描的PDF,OCR逐页进行处理,并为每个页面生成一个结果对象。
GdPicture.NET OCR 的许可方式与 IronOCR 相比如何?
IronOCR采用永久统一费率许可,不按页或扫描次数收费。处理大量文档的机构无论处理量多少,都只需支付相同的许可费用。详情及批量定价请访问IronOCR许可页面。
IronOCR支持哪些语言?
IronOCR 通过独立的 NuGet 语言包支持 127 种语言。添加语言只需一条命令“dotnet add package IronOcr.Languages.{Language}”。无需手动放置文件或配置路径。
如何在.NET项目中安装IronOCR ?
通过 NuGet 安装:在程序包管理器控制台中运行“Install-Package IronOcr”命令,或在命令行界面 (CLI) 中运行“dotnet add package IronOcr”命令。其他语言包的安装方式相同。无需使用原生 SDK 安装程序。
与 GdPicture.NET 不同,IronOCR 是否适用于 Docker 和容器化部署?
是的。IronOCR 通过 NuGet 包在 Docker 容器中运行。许可证密钥通过环境变量设置。OCR 引擎本身不需要任何许可证文件、SDK 路径或卷挂载。
我可以在购买前试用 IronOCR,并将其与 GdPicture.NET 进行比较吗?
是的。IronOCR 试用模式可以处理文档,并在输出结果上添加水印,从而生成 OCR 结果。您可以在购买许可证之前,先在自己的文档上验证其准确性。
IronOCR是否支持条形码读取和文本提取?
IronOCR专注于文本提取和OCR识别。对于条形码读取,Iron Software提供了配套库IronBarcode。两者都可单独购买,也可作为Iron Suite套装的一部分购买。
从 GdPicture.NET OCR 迁移到 IronOCR 容易吗?
从 GdPicture.NET OCR 迁移到 IronOCR 通常涉及将初始化序列替换为 IronTesseract 实例化、移除 COM 生命周期管理以及更新 API 调用。大多数迁移都能显著降低代码复杂度。

