跳至页脚内容
视频

从 Dynamsoft OCR 迁移到 IronOCR

本指南为从 Dynamsoft 标签识别器 迁移到IronOCR 的.NET开发人员提供了完整的迁移路径。 它涵盖了NuGet包替换、命名空间更新、许可证初始化以及与一般功能比较不同的场景的实用代码迁移示例——包括模板配置消除、运行时设置 JSON 删除、区域定义迁移和结果解析简化。

为什么要从 Dynamsoft OCR 迁移?

Dynamsoft 标签识别器是一款专用的专业工具。这种精准度在小范围部署中表现出色,但一旦应用范围超出最初的 MRZ 或 VIN 用例,就会产生阻碍。

运行时设置JSON是维护表面。 每个Dynamsoft识别工作流从AppendSettingsFromString开始,加载一个声明参数数组、字符模型和区域引用的JSON文档。 这些 JSON 模板是单独的部署工件,不会编译到您的二进制文件中。 当 Dynamsoft 在 SDK 版本之间更改模板架构时,它们需要版本控制、部署管道管理和手动更新。 IronOCR无需配置文件:实例化.Read(),引擎会自动选择适当的设置。

年度订阅价格叠加。Dynamsoft标签识别器许可价格为每台设备每年 599 美元起。 没有永久的解决方案。 一个五设备处理集群每年的成本超过 2,995 美元——而且该预算仅涵盖 MRZ 和结构化标签识别。 如果添加 Dynamsoft 条形码阅读器进行条形码读取,或添加 Dynamsoft 文档规范化器进行边缘校正,则年度账单将按产品数量乘以相应倍数。 IronOCR的Lite许可证是$999,一次性支付,涵盖通用OCR、结构化标签、条形码、原生PDF、可搜索PDF输出,以及单个包中的125+种语言。

区域定义需要JSON模板。 在Dynamsoft中定义一个识别区域意味着制作一个LabelRecognizerParameterArray中,并在任何图像处理开始之前加载整个文档。 IronOCR使用一个OcrInput.LoadImage。 更改区域只需修改一个数字,无需重新解析 JSON 文档。

结果解析完全是您的责任。 LineResult数组。 您需要的每个结构——字段偏移量、日期转换、校验位验证、名称分隔符处理——都需要在原始输出的基础上编写自定义解析代码。 IronOCR返回一个.Characters,作为带有边界框坐标、置信度得分和字体元数据的类型化集合。

不支持原生 PDF 会产生隐藏的依赖关系。Dynamsoft标签识别器不具备 PDF 输入功能。 任何到达处理管道的 PDF 文件都需要一个外部库将每一页渲染成位图,一个页面迭代循环,以及在第一次调用 Dynamsoft 之前进行结果聚合步骤。 IronOCR直接读取PDF:new IronTesseract().Read("document.pdf")处理每一页面,无需二次依赖、渲染循环或临时图像文件夹。

多产品初始化模块随着范围的扩大而增加。 处理结构化标签、条形码和文档标准化的Dynamsoft应用程序在启动时会进行三个静态InitLicense调用,三个释放链和三个独立的SDK版本依赖。 升级 SDK 意味着需要协调所有产品。 无论应用程序使用哪些功能, IronOCR都只有一个初始化语句和一个软件包版本。

基本问题

Dynamsoft 需要先在运行时加载 JSON 配置文件,然后才能开始识别:

// Dynamsoft: JSON template required before any image processing
LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();

string settings = @"{
    ""LabelRecognizerParameterArray"": [{
        ""Name"": ""VIN_Label"",
        ""ReferenceRegionNameArray"": [""VinRegion""],
        ""CharacterModelName"": ""VIN""
    }],
    ""ReferenceRegionArray"": [{
        ""Name"": ""VinRegion"",
        ""Localization"": {
            ""SourceType"": ""LST_MANUAL_SPECIFICATION"",
            ""FirstPoint"":  [5, 40],
            ""SecondPoint"": [95, 40],
            ""ThirdPoint"":  [95, 60],
            ""FourthPoint"": [5, 60]
        }
    }]
}";

recognizer.AppendSettingsFromString(settings);  // fails silently if JSON is malformed
var results = recognizer.RecognizeFile(imagePath);
// Dynamsoft: JSON template required before any image processing
LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();

string settings = @"{
    ""LabelRecognizerParameterArray"": [{
        ""Name"": ""VIN_Label"",
        ""ReferenceRegionNameArray"": [""VinRegion""],
        ""CharacterModelName"": ""VIN""
    }],
    ""ReferenceRegionArray"": [{
        ""Name"": ""VinRegion"",
        ""Localization"": {
            ""SourceType"": ""LST_MANUAL_SPECIFICATION"",
            ""FirstPoint"":  [5, 40],
            ""SecondPoint"": [95, 40],
            ""ThirdPoint"":  [95, 60],
            ""FourthPoint"": [5, 60]
        }
    }]
}";

recognizer.AppendSettingsFromString(settings);  // fails silently if JSON is malformed
var results = recognizer.RecognizeFile(imagePath);
' Dynamsoft: JSON template required before any image processing
LabelRecognizer.InitLicense(licenseKey)
Dim recognizer As New LabelRecognizer()

Dim settings As String = "{
    ""LabelRecognizerParameterArray"": [{
        ""Name"": ""VIN_Label"",
        ""ReferenceRegionNameArray"": [""VinRegion""],
        ""CharacterModelName"": ""VIN""
    }],
    ""ReferenceRegionArray"": [{
        ""Name"": ""VinRegion"",
        ""Localization"": {
            ""SourceType"": ""LST_MANUAL_SPECIFICATION"",
            ""FirstPoint"":  [5, 40],
            ""SecondPoint"": [95, 40],
            ""ThirdPoint"":  [95, 60],
            ""FourthPoint"": [5, 60]
        }
    }]
}"

recognizer.AppendSettingsFromString(settings)  ' fails silently if JSON is malformed
Dim results = recognizer.RecognizeFile(imagePath)
$vbLabelText   $csharpLabel
// IronOCR: no JSON, no template files — region is a constructor argument
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";

var region = new CropRectangle(x: 50, y: 400, width: 900, height: 200);
using var input = new OcrInput();
input.LoadImage(imagePath, region);

var result = new IronTesseract().Read(input);
Console.WriteLine(result.Text);
// IronOCR: no JSON, no template files — region is a constructor argument
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";

var region = new CropRectangle(x: 50, y: 400, width: 900, height: 200);
using var input = new OcrInput();
input.LoadImage(imagePath, region);

var result = new IronTesseract().Read(input);
Console.WriteLine(result.Text);
Imports IronOcr

' IronOCR: no JSON, no template files — region is a constructor argument
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"

Dim region As New CropRectangle(x:=50, y:=400, width:=900, height:=200)
Using input As New OcrInput()
    input.LoadImage(imagePath, region)

    Dim result = New IronTesseract().Read(input)
    Console.WriteLine(result.Text)
End Using
$vbLabelText   $csharpLabel

IronOCR与 Dynamsoft OCR:功能对比

下表列出了两个库之间的全部功能差距。

特征 Dynamsoft 标签识别器 IronOCR
通用文档 OCR 不支持 全面支持
MRZ识别 专业化(原始文本输出) 内置结构化字段
车辆识别码 (VIN) 识别 专业版(需要模板) 标准OCR,区域定向
工业标签解读 专业版(需要模板) 是的,通过CropRectangle
运行时设置配置 必需的 (JSON通过AppendSettingsFromString) 不要求
模板部署工件 必需(JSON 文件) None
原生 PDF 输入 不支持
受密码保护的PDF 不支持
多页 TIFF 输入 手动页面迭代 原生(LoadImageFrames)
可搜索的 PDF 输出 不支持 是的(SaveAsSearchablePdf)
条形码读取 独立产品(Dynamsoft 条码阅读器) 内置的(ReadBarCodes = true)
自动预处理 有限的 是的(调整桌面倾斜度、降噪、对比度、二值化、锐化、膨胀、腐蚀)
结构化输出(字数、行数、段落) 原始LineResult文本字符串 完全输入坐标和置信度
语言支持 有限公司(拉丁语 MRZ) 支持 125 多种语言,以NuGet包的形式打包。
多语言同步 不支持 是的(OcrLanguage.French + OcrLanguage.German)
置信度评分 仅按行收费 按字、行、页
hOCR导出 不支持
许可模式 年度订阅费(599美元起/设备/年) 永久($999一次性,Lite层)
需要多种产品 是的(3+ 可获得全面保障) 没有(一个包裹)
.NET Framework支持 .NET Framework 4.x+ .NET Framework 4.6.2+,. .NET 5/6/7/8/9
跨平台部署 是的(Windows、Linux、macOS、Docker、Azure、AWS)
NuGet 软件包 Dynamsoft.LabelRecognizer IronOcr

快速入门:Dynamsoft OCR 到IronOCR 的迁移

步骤 1:替换 NuGet 软件包

移除 Dynamsoft 软件包:

dotnet remove package Dynamsoft.LabelRecognizer
dotnet remove package Dynamsoft.LabelRecognizer
SHELL

NuGet库安装IronOCR :

dotnet add package IronOcr

步骤 2:更新命名空间

// Before (Dynamsoft)
using Dynamsoft.LabelRecognizer;
using Dynamsoft.Core;

// After (IronOCR)
using IronOcr;
// Before (Dynamsoft)
using Dynamsoft.LabelRecognizer;
using Dynamsoft.Core;

// After (IronOCR)
using IronOcr;
Imports IronOcr
$vbLabelText   $csharpLabel

步骤 3:初始化许可证

在应用程序启动时,用单个IronOCR许可证分配替换每个Dynamsoft静态InitLicense调用:

// Before (Dynamsoft — one call per product)
LabelRecognizer.InitLicense("DYNAMSOFT-LICENSE-KEY");

// After (IronOCR — one line, all features)
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
// Before (Dynamsoft — one call per product)
LabelRecognizer.InitLicense("DYNAMSOFT-LICENSE-KEY");

// After (IronOCR — one line, all features)
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
' Before (Dynamsoft — one call per product)
LabelRecognizer.InitLicense("DYNAMSOFT-LICENSE-KEY")

' After (IronOCR — one line, all features)
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
$vbLabelText   $csharpLabel

代码迁移示例

基于模板的识别到零配置引擎

Dynamsoft 的 JSON 模板系统在处理任何图像之前,会向 SDK 提供有关字符模型、区域规范和识别参数的详细说明。 对于大多数现实世界的 OCR 工作负载而言,这种配置会增加设置的复杂性,而不会带来识别方面的好处,因为底层 Tesseract 引擎会自动处理布局检测。

Dynamsoft 方法:

using Dynamsoft.LabelRecognizer;
using Dynamsoft.Core;

// JSON template required before recognition
LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();

string invoiceTemplate = @"{
    ""LabelRecognizerParameterArray"": [{
        ""Name"": ""Invoice_Header"",
        ""ReferenceRegionNameArray"": [""HeaderRegion""],
        ""CharacterModelName"": ""NumberLetter""
    }],
    ""ReferenceRegionArray"": [{
        ""Name"": ""HeaderRegion"",
        ""Localization"": {
            ""SourceType"": ""LST_MANUAL_SPECIFICATION"",
            ""FirstPoint"":  [0, 0],
            ""SecondPoint"": [100, 0],
            ""ThirdPoint"":  [100, 20],
            ""FourthPoint"": [0, 20]
        }
    }]
}";

recognizer.AppendSettingsFromString(invoiceTemplate);

var results = recognizer.RecognizeFile("invoice.jpg");
var headerText = new StringBuilder();
foreach (var result in results)
    foreach (var line in result.LineResults)
        headerText.AppendLine(line.Text);

Console.WriteLine(headerText.ToString());
recognizer.Dispose();
using Dynamsoft.LabelRecognizer;
using Dynamsoft.Core;

// JSON template required before recognition
LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();

string invoiceTemplate = @"{
    ""LabelRecognizerParameterArray"": [{
        ""Name"": ""Invoice_Header"",
        ""ReferenceRegionNameArray"": [""HeaderRegion""],
        ""CharacterModelName"": ""NumberLetter""
    }],
    ""ReferenceRegionArray"": [{
        ""Name"": ""HeaderRegion"",
        ""Localization"": {
            ""SourceType"": ""LST_MANUAL_SPECIFICATION"",
            ""FirstPoint"":  [0, 0],
            ""SecondPoint"": [100, 0],
            ""ThirdPoint"":  [100, 20],
            ""FourthPoint"": [0, 20]
        }
    }]
}";

recognizer.AppendSettingsFromString(invoiceTemplate);

var results = recognizer.RecognizeFile("invoice.jpg");
var headerText = new StringBuilder();
foreach (var result in results)
    foreach (var line in result.LineResults)
        headerText.AppendLine(line.Text);

Console.WriteLine(headerText.ToString());
recognizer.Dispose();
Imports Dynamsoft.LabelRecognizer
Imports Dynamsoft.Core
Imports System.Text

' JSON template required before recognition
LabelRecognizer.InitLicense(licenseKey)
Dim recognizer As New LabelRecognizer()

Dim invoiceTemplate As String = "{
    ""LabelRecognizerParameterArray"": [{
        ""Name"": ""Invoice_Header"",
        ""ReferenceRegionNameArray"": [""HeaderRegion""],
        ""CharacterModelName"": ""NumberLetter""
    }],
    ""ReferenceRegionArray"": [{
        ""Name"": ""HeaderRegion"",
        ""Localization"": {
            ""SourceType"": ""LST_MANUAL_SPECIFICATION"",
            ""FirstPoint"":  [0, 0],
            ""SecondPoint"": [100, 0],
            ""ThirdPoint"":  [100, 20],
            ""FourthPoint"": [0, 20]
        }
    }]
}"

recognizer.AppendSettingsFromString(invoiceTemplate)

Dim results = recognizer.RecognizeFile("invoice.jpg")
Dim headerText As New StringBuilder()
For Each result In results
    For Each line In result.LineResults
        headerText.AppendLine(line.Text)
    Next
Next

Console.WriteLine(headerText.ToString())
recognizer.Dispose()
$vbLabelText   $csharpLabel

IronOCR方法:

using IronOcr;

// No template file — region specified inline, engine auto-configures
var region = new CropRectangle(x: 0, y: 0, width: 1200, height: 200);
using var input = new OcrInput();
input.LoadImage("invoice.jpg", region);
input.Deskew();

var result = new IronTesseract().Read(input);
Console.WriteLine(result.Text);
Console.WriteLine($"Confidence: {result.Confidence}%");
using IronOcr;

// No template file — region specified inline, engine auto-configures
var region = new CropRectangle(x: 0, y: 0, width: 1200, height: 200);
using var input = new OcrInput();
input.LoadImage("invoice.jpg", region);
input.Deskew();

var result = new IronTesseract().Read(input);
Console.WriteLine(result.Text);
Console.WriteLine($"Confidence: {result.Confidence}%");
Imports IronOcr

' No template file — region specified inline, engine auto-configures
Dim region As New CropRectangle(x:=0, y:=0, width:=1200, height:=200)
Using input As New OcrInput()
    input.LoadImage("invoice.jpg", region)
    input.Deskew()

    Dim result = New IronTesseract().Read(input)
    Console.WriteLine(result.Text)
    Console.WriteLine($"Confidence: {result.Confidence}%")
End Using
$vbLabelText   $csharpLabel

Dynamsoft 的方法要求在处理任何图像之前,先编写、验证和部署 JSON 模板。 在AppendSettingsFromString中的拼写错误在运行时返回空结果——没有编译时验证。 IronOCR的CropRectangle是一个简单的构造函数:编译时类型检查,无外部文件,无部署工件。 有关所有目标模式,请参阅基于区域的 OCR 指南

VIN码扫描及区域迁移

车辆识别码提取是 Dynamsoft 标签识别器 的一项专长。 其 VIN 模板模型针对特定的 17 位字母数字格式进行了优化。 迁移到IronOCR会将模板模型替换为预处理流程和区域目标定位,这样无需字符模型配置即可处理相同的 VIN 车牌条件。

Dynamsoft 方法:

using Dynamsoft.LabelRecognizer;

LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();

// VIN requires a specific character model and region configuration
string vinTemplate = @"{
    ""LabelRecognizerParameterArray"": [{
        ""Name"": ""VIN_Scan"",
        ""ReferenceRegionNameArray"": [""VIN_Zone""],
        ""CharacterModelName"": ""VIN""
    }],
    ""ReferenceRegionArray"": [{
        ""Name"": ""VIN_Zone"",
        ""Localization"": {
            ""SourceType"": ""LST_MANUAL_SPECIFICATION"",
            ""FirstPoint"":  [10, 35],
            ""SecondPoint"": [90, 35],
            ""ThirdPoint"":  [90, 65],
            ""FourthPoint"": [10, 65]
        }
    }]
}";
recognizer.AppendSettingsFromString(vinTemplate);

var results = recognizer.RecognizeFile("vehicle-vin.jpg");
string rawVin = results
    .SelectMany(r => r.LineResults)
    .Select(l => l.Text)
    .FirstOrDefault() ?? string.Empty;

Console.WriteLine($"Raw VIN text: {rawVin}");  // still requires validation
recognizer.Dispose();
using Dynamsoft.LabelRecognizer;

LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();

// VIN requires a specific character model and region configuration
string vinTemplate = @"{
    ""LabelRecognizerParameterArray"": [{
        ""Name"": ""VIN_Scan"",
        ""ReferenceRegionNameArray"": [""VIN_Zone""],
        ""CharacterModelName"": ""VIN""
    }],
    ""ReferenceRegionArray"": [{
        ""Name"": ""VIN_Zone"",
        ""Localization"": {
            ""SourceType"": ""LST_MANUAL_SPECIFICATION"",
            ""FirstPoint"":  [10, 35],
            ""SecondPoint"": [90, 35],
            ""ThirdPoint"":  [90, 65],
            ""FourthPoint"": [10, 65]
        }
    }]
}";
recognizer.AppendSettingsFromString(vinTemplate);

var results = recognizer.RecognizeFile("vehicle-vin.jpg");
string rawVin = results
    .SelectMany(r => r.LineResults)
    .Select(l => l.Text)
    .FirstOrDefault() ?? string.Empty;

Console.WriteLine($"Raw VIN text: {rawVin}");  // still requires validation
recognizer.Dispose();
Imports Dynamsoft.LabelRecognizer

LabelRecognizer.InitLicense(licenseKey)
Dim recognizer As New LabelRecognizer()

' VIN requires a specific character model and region configuration
Dim vinTemplate As String = "{
    ""LabelRecognizerParameterArray"": [{
        ""Name"": ""VIN_Scan"",
        ""ReferenceRegionNameArray"": [""VIN_Zone""],
        ""CharacterModelName"": ""VIN""
    }],
    ""ReferenceRegionArray"": [{
        ""Name"": ""VIN_Zone"",
        ""Localization"": {
            ""SourceType"": ""LST_MANUAL_SPECIFICATION"",
            ""FirstPoint"":  [10, 35],
            ""SecondPoint"": [90, 35],
            ""ThirdPoint"":  [90, 65],
            ""FourthPoint"": [10, 65]
        }
    }]
}"
recognizer.AppendSettingsFromString(vinTemplate)

Dim results = recognizer.RecognizeFile("vehicle-vin.jpg")
Dim rawVin As String = results _
    .SelectMany(Function(r) r.LineResults) _
    .Select(Function(l) l.Text) _
    .FirstOrDefault() OrElse String.Empty

Console.WriteLine($"Raw VIN text: {rawVin}")  ' still requires validation
recognizer.Dispose()
$vbLabelText   $csharpLabel

IronOCR方法:

using IronOcr;
using System.Text.RegularExpressions;

// Target the VIN plate region directly — no template file
var vinRegion = new CropRectangle(x: 100, y: 350, width: 800, height: 300);

using var input = new OcrInput();
input.LoadImage("vehicle-vin.jpg", vinRegion);
input.Contrast();   // recover faded stamped digits
input.Sharpen();    // clarify character boundaries on embossed plates

var result = new IronTesseract().Read(input);

// VIN: 17 chars, no I/O/Q
var vinMatch = Regex.Match(result.Text, @"\b[A-HJ-NPR-Z0-9]{17}\b");
string vin = vinMatch.Success ? vinMatch.Value : result.Text.Trim();

Console.WriteLine($"VIN: {vin}");
Console.WriteLine($"Confidence: {result.Confidence}%");
using IronOcr;
using System.Text.RegularExpressions;

// Target the VIN plate region directly — no template file
var vinRegion = new CropRectangle(x: 100, y: 350, width: 800, height: 300);

using var input = new OcrInput();
input.LoadImage("vehicle-vin.jpg", vinRegion);
input.Contrast();   // recover faded stamped digits
input.Sharpen();    // clarify character boundaries on embossed plates

var result = new IronTesseract().Read(input);

// VIN: 17 chars, no I/O/Q
var vinMatch = Regex.Match(result.Text, @"\b[A-HJ-NPR-Z0-9]{17}\b");
string vin = vinMatch.Success ? vinMatch.Value : result.Text.Trim();

Console.WriteLine($"VIN: {vin}");
Console.WriteLine($"Confidence: {result.Confidence}%");
Imports IronOcr
Imports System.Text.RegularExpressions

' Target the VIN plate region directly — no template file
Dim vinRegion As New CropRectangle(x:=100, y:=350, width:=800, height:=300)

Using input As New OcrInput()
    input.LoadImage("vehicle-vin.jpg", vinRegion)
    input.Contrast()   ' recover faded stamped digits
    input.Sharpen()    ' clarify character boundaries on embossed plates

    Dim result = New IronTesseract().Read(input)

    ' VIN: 17 chars, no I/O/Q
    Dim vinMatch = Regex.Match(result.Text, "\b[A-HJ-NPR-Z0-9]{17}\b")
    Dim vin As String = If(vinMatch.Success, vinMatch.Value, result.Text.Trim())

    Console.WriteLine($"VIN: {vin}")
    Console.WriteLine($"Confidence: {result.Confidence}%")
End Using
$vbLabelText   $csharpLabel

SecondPoint规范相同的像素参考框架——通过将Dynamsoft的百分比值乘以您的图像尺寸进行转换。 预处理管道 (Contrast, Sharpen) 替代了Dynamsoft在其VIN模板中烘焙的字符模型优化。 图像质量校正指南涵盖了浮雕和低对比度表面的滤镜选择。

多页 TIFF 批量处理

Dynamsoft 标签识别器可处理单张图像。 多页 TIFF 文档(常见于文档管理系统、医学影像档案和传真流程)需要外部迭代:加载每一帧,将其逐一传递给识别器,然后汇总结果。 IronOCR通过LoadImageFrames原生支持多帧TIFF。

Dynamsoft 方法:

using Dynamsoft.LabelRecognizer;
// Requires an external TIFF library to extract frames

LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();
recognizer.AppendSettingsFromString(genericTemplate);

// External library needed to iterate TIFF frames
var tiffImage = System.Drawing.Image.FromFile("scanned-batch.tiff");
int frameCount = tiffImage.GetFrameCount(
    System.Drawing.Imaging.FrameDimension.Page);

var allText = new StringBuilder();
for (int i = 0; i < frameCount; i++)
{
    tiffImage.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, i);
    string tempPath = $"frame_{i}.jpg";
    tiffImage.Save(tempPath, System.Drawing.Imaging.ImageFormat.Jpeg);

    var results = recognizer.RecognizeFile(tempPath);
    foreach (var r in results)
        foreach (var line in r.LineResults)
            allText.AppendLine(line.Text);

    System.IO.File.Delete(tempPath);  // clean up temp files
}

Console.WriteLine(allText.ToString());
recognizer.Dispose();
using Dynamsoft.LabelRecognizer;
// Requires an external TIFF library to extract frames

LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();
recognizer.AppendSettingsFromString(genericTemplate);

// External library needed to iterate TIFF frames
var tiffImage = System.Drawing.Image.FromFile("scanned-batch.tiff");
int frameCount = tiffImage.GetFrameCount(
    System.Drawing.Imaging.FrameDimension.Page);

var allText = new StringBuilder();
for (int i = 0; i < frameCount; i++)
{
    tiffImage.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, i);
    string tempPath = $"frame_{i}.jpg";
    tiffImage.Save(tempPath, System.Drawing.Imaging.ImageFormat.Jpeg);

    var results = recognizer.RecognizeFile(tempPath);
    foreach (var r in results)
        foreach (var line in r.LineResults)
            allText.AppendLine(line.Text);

    System.IO.File.Delete(tempPath);  // clean up temp files
}

Console.WriteLine(allText.ToString());
recognizer.Dispose();
Imports Dynamsoft.LabelRecognizer
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.IO
Imports System.Text

' Requires an external TIFF library to extract frames

LabelRecognizer.InitLicense(licenseKey)
Dim recognizer As New LabelRecognizer()
recognizer.AppendSettingsFromString(genericTemplate)

' External library needed to iterate TIFF frames
Dim tiffImage As Image = Image.FromFile("scanned-batch.tiff")
Dim frameCount As Integer = tiffImage.GetFrameCount(FrameDimension.Page)

Dim allText As New StringBuilder()
For i As Integer = 0 To frameCount - 1
    tiffImage.SelectActiveFrame(FrameDimension.Page, i)
    Dim tempPath As String = $"frame_{i}.jpg"
    tiffImage.Save(tempPath, Imaging.ImageFormat.Jpeg)

    Dim results = recognizer.RecognizeFile(tempPath)
    For Each r In results
        For Each line In r.LineResults
            allText.AppendLine(line.Text)
        Next
    Next

    File.Delete(tempPath)  ' clean up temp files
Next

Console.WriteLine(allText.ToString())
recognizer.Dispose()
$vbLabelText   $csharpLabel

IronOCR方法:

using IronOcr;

// No frame iteration, no temp files, no external TIFF library
using var input = new OcrInput();
input.LoadImageFrames("scanned-batch.tiff");  // loads all frames natively
input.Deskew();
input.DeNoise();

var ocr = new IronTesseract();
var result = ocr.Read(input);

// Results organized per page
foreach (var page in result.Pages)
{
    Console.WriteLine($"Frame {page.PageNumber}: {page.Lines.Count} lines");
    Console.WriteLine(page.Text);
}

// Save entire batch as a single searchable PDF
result.SaveAsSearchablePdf("batch-searchable.pdf");
using IronOcr;

// No frame iteration, no temp files, no external TIFF library
using var input = new OcrInput();
input.LoadImageFrames("scanned-batch.tiff");  // loads all frames natively
input.Deskew();
input.DeNoise();

var ocr = new IronTesseract();
var result = ocr.Read(input);

// Results organized per page
foreach (var page in result.Pages)
{
    Console.WriteLine($"Frame {page.PageNumber}: {page.Lines.Count} lines");
    Console.WriteLine(page.Text);
}

// Save entire batch as a single searchable PDF
result.SaveAsSearchablePdf("batch-searchable.pdf");
Imports IronOcr

' No frame iteration, no temp files, no external TIFF library
Using input As New OcrInput()
    input.LoadImageFrames("scanned-batch.tiff") ' loads all frames natively
    input.Deskew()
    input.DeNoise()

    Dim ocr As New IronTesseract()
    Dim result = ocr.Read(input)

    ' Results organized per page
    For Each page In result.Pages
        Console.WriteLine($"Frame {page.PageNumber}: {page.Lines.Count} lines")
        Console.WriteLine(page.Text)
    Next

    ' Save entire batch as a single searchable PDF
    result.SaveAsSearchablePdf("batch-searchable.pdf")
End Using
$vbLabelText   $csharpLabel

Dynamsoft方法需要System.Drawing用于帧提取,每帧在磁盘上创建临时文件,并在每次通过后手动清理。 IronOCR的LoadImageFrames在一次调用中读取所有帧——无需临时文件,无需帧索引跟踪。 在OCR之后,SaveAsSearchablePdf将整个批次归档为可搜索文档,仅需一次方法调用。 多帧 TIFF 指南可搜索 PDF 输出指南详细介绍了这两种功能。

结构化词级结果解析

Dynamsoft RecognizeFile 返回一个DLRResult数组。 每个Location属性。 提取单词级位置需要将行文本按空格分割,并按比例分割行边界框——这种近似方法在比例字体上会失效。 IronOCR将单词级边界框呈现为每个OcrResult.Word对象上的类型化属性。

Dynamsoft 方法:

using Dynamsoft.LabelRecognizer;

LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();
recognizer.AppendSettingsFromString(settingsJson);

var results = recognizer.RecognizeFile("form-scan.jpg");

foreach (var dlrResult in results)
{
    foreach (var lineResult in dlrResult.LineResults)
    {
        // Line-level data only — word boundaries are not provided
        Console.WriteLine($"Text: {lineResult.Text}");
        Console.WriteLine($"Confidence: {lineResult.Confidence}");

        // Location is a quadrilateral — four corner points
        var loc = lineResult.Location;
        Console.WriteLine($"Top-left: ({loc.Points[0].X}, {loc.Points[0].Y})");

        // To get word positions: split text and divide bounding box manually
        var words = lineResult.Text.Split(' ');
        int approxWidth = (loc.Points[1].X - loc.Points[0].X) / words.Length;
        for (int i = 0; i < words.Length; i++)
        {
            int wordX = loc.Points[0].X + (i * approxWidth);
            Console.WriteLine($"  Word '{words[i]}' approx at x={wordX}");
        }
    }
}
recognizer.Dispose();
using Dynamsoft.LabelRecognizer;

LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();
recognizer.AppendSettingsFromString(settingsJson);

var results = recognizer.RecognizeFile("form-scan.jpg");

foreach (var dlrResult in results)
{
    foreach (var lineResult in dlrResult.LineResults)
    {
        // Line-level data only — word boundaries are not provided
        Console.WriteLine($"Text: {lineResult.Text}");
        Console.WriteLine($"Confidence: {lineResult.Confidence}");

        // Location is a quadrilateral — four corner points
        var loc = lineResult.Location;
        Console.WriteLine($"Top-left: ({loc.Points[0].X}, {loc.Points[0].Y})");

        // To get word positions: split text and divide bounding box manually
        var words = lineResult.Text.Split(' ');
        int approxWidth = (loc.Points[1].X - loc.Points[0].X) / words.Length;
        for (int i = 0; i < words.Length; i++)
        {
            int wordX = loc.Points[0].X + (i * approxWidth);
            Console.WriteLine($"  Word '{words[i]}' approx at x={wordX}");
        }
    }
}
recognizer.Dispose();
Imports Dynamsoft.LabelRecognizer

LabelRecognizer.InitLicense(licenseKey)
Dim recognizer As New LabelRecognizer()
recognizer.AppendSettingsFromString(settingsJson)

Dim results = recognizer.RecognizeFile("form-scan.jpg")

For Each dlrResult In results
    For Each lineResult In dlrResult.LineResults
        ' Line-level data only — word boundaries are not provided
        Console.WriteLine($"Text: {lineResult.Text}")
        Console.WriteLine($"Confidence: {lineResult.Confidence}")

        ' Location is a quadrilateral — four corner points
        Dim loc = lineResult.Location
        Console.WriteLine($"Top-left: ({loc.Points(0).X}, {loc.Points(0).Y})")

        ' To get word positions: split text and divide bounding box manually
        Dim words = lineResult.Text.Split(" "c)
        Dim approxWidth As Integer = (loc.Points(1).X - loc.Points(0).X) \ words.Length
        For i As Integer = 0 To words.Length - 1
            Dim wordX As Integer = loc.Points(0).X + (i * approxWidth)
            Console.WriteLine($"  Word '{words(i)}' approx at x={wordX}")
        Next
    Next
Next
recognizer.Dispose()
$vbLabelText   $csharpLabel

IronOCR方法:

using IronOcr;

var result = new IronTesseract().Read("form-scan.jpg");

// Word-level positions are first-class properties — no manual division
foreach (var page in result.Pages)
{
    foreach (var paragraph in page.Paragraphs)
    {
        Console.WriteLine($"Paragraph at ({paragraph.X}, {paragraph.Y}): {paragraph.Text}");

        foreach (var line in paragraph.Lines)
        {
            foreach (var word in line.Words)
            {
                Console.WriteLine(
                    $"  Word: '{word.Text}' " +
                    $"at ({word.X},{word.Y}) " +
                    $"size {word.Width}x{word.Height} " +
                    $"confidence {word.Confidence}%");
            }
        }
    }
}
using IronOcr;

var result = new IronTesseract().Read("form-scan.jpg");

// Word-level positions are first-class properties — no manual division
foreach (var page in result.Pages)
{
    foreach (var paragraph in page.Paragraphs)
    {
        Console.WriteLine($"Paragraph at ({paragraph.X}, {paragraph.Y}): {paragraph.Text}");

        foreach (var line in paragraph.Lines)
        {
            foreach (var word in line.Words)
            {
                Console.WriteLine(
                    $"  Word: '{word.Text}' " +
                    $"at ({word.X},{word.Y}) " +
                    $"size {word.Width}x{word.Height} " +
                    $"confidence {word.Confidence}%");
            }
        }
    }
}
Imports IronOcr

Dim result = New IronTesseract().Read("form-scan.jpg")

' Word-level positions are first-class properties — no manual division
For Each page In result.Pages
    For Each paragraph In page.Paragraphs
        Console.WriteLine($"Paragraph at ({paragraph.X}, {paragraph.Y}): {paragraph.Text}")

        For Each line In paragraph.Lines
            For Each word In line.Words
                Console.WriteLine(
                    $"  Word: '{word.Text}' " &
                    $"at ({word.X},{word.Y}) " &
                    $"size {word.Width}x{word.Height} " &
                    $"confidence {word.Confidence}%")
            Next
        Next
    Next
Next
$vbLabelText   $csharpLabel

IronOCR提供了真正的层次结构:页面包含段落,段落包含行,行包含单词,单词包含字符。 每个级别暴露出.Confidence,作为类型化的整数和双精度属性——不需要四边形坐标算术。 结构化结果指南记录了每个可访问的属性,置信度评分指南解释了自动化文档审查流程的置信度阈值。

用于高容量标签扫描的异步并行处理

Dynamsoft标签识别器是线程安全的,但其RecognizeFile调用是同步的。 将其封装在AppendSettingsFromString共享配置状态——在多个线程上加载模板需要仔细的初始化顺序。 IronOCR的Read,线程模型简单明了。

Dynamsoft 方法:

using Dynamsoft.LabelRecognizer;
using System.Threading.Tasks;

// InitLicense must be called once before parallel work begins
LabelRecognizer.InitLicense(licenseKey);

// Each thread needs its own recognizer instance with its own template load
var results = new System.Collections.Concurrent.ConcurrentBag<string>();

await Task.WhenAll(imagePaths.Select(async path =>
{
    // Cannot share recognizer instances safely across tasks
    var recognizer = new LabelRecognizer();
    recognizer.AppendSettingsFromString(templateJson);  // reload JSON per instance

    await Task.Run(() =>
    {
        var dlrResults = recognizer.RecognizeFile(path);
        var text = string.Join("\n",
            dlrResults.SelectMany(r => r.LineResults).Select(l => l.Text));
        results.Add(text);
    });

    recognizer.Dispose();
}));

Console.WriteLine($"Processed {results.Count} images");
using Dynamsoft.LabelRecognizer;
using System.Threading.Tasks;

// InitLicense must be called once before parallel work begins
LabelRecognizer.InitLicense(licenseKey);

// Each thread needs its own recognizer instance with its own template load
var results = new System.Collections.Concurrent.ConcurrentBag<string>();

await Task.WhenAll(imagePaths.Select(async path =>
{
    // Cannot share recognizer instances safely across tasks
    var recognizer = new LabelRecognizer();
    recognizer.AppendSettingsFromString(templateJson);  // reload JSON per instance

    await Task.Run(() =>
    {
        var dlrResults = recognizer.RecognizeFile(path);
        var text = string.Join("\n",
            dlrResults.SelectMany(r => r.LineResults).Select(l => l.Text));
        results.Add(text);
    });

    recognizer.Dispose();
}));

Console.WriteLine($"Processed {results.Count} images");
Imports Dynamsoft.LabelRecognizer
Imports System.Threading.Tasks
Imports System.Collections.Concurrent

' InitLicense must be called once before parallel work begins
LabelRecognizer.InitLicense(licenseKey)

' Each thread needs its own recognizer instance with its own template load
Dim results As New ConcurrentBag(Of String)()

Await Task.WhenAll(imagePaths.Select(Async Function(path)
    ' Cannot share recognizer instances safely across tasks
    Dim recognizer As New LabelRecognizer()
    recognizer.AppendSettingsFromString(templateJson)  ' reload JSON per instance

    Await Task.Run(Sub()
        Dim dlrResults = recognizer.RecognizeFile(path)
        Dim text = String.Join(vbLf, dlrResults.SelectMany(Function(r) r.LineResults).Select(Function(l) l.Text))
        results.Add(text)
    End Sub)

    recognizer.Dispose()
End Function))

Console.WriteLine($"Processed {results.Count} images")
$vbLabelText   $csharpLabel

IronOCR方法:

using IronOcr;
using System.Collections.Concurrent;
using System.Threading.Tasks;

var extractedTexts = new ConcurrentDictionary<string, string>();

// Each IronTesseract instance is independent — no shared state
Parallel.ForEach(imagePaths, imagePath =>
{
    var ocr = new IronTesseract();
    var result = ocr.Read(imagePath);
    extractedTexts[imagePath] = result.Text;
});

foreach (var (path, text) in extractedTexts)
    Console.WriteLine($"{System.IO.Path.GetFileName(path)}: {text.Length} characters");
using IronOcr;
using System.Collections.Concurrent;
using System.Threading.Tasks;

var extractedTexts = new ConcurrentDictionary<string, string>();

// Each IronTesseract instance is independent — no shared state
Parallel.ForEach(imagePaths, imagePath =>
{
    var ocr = new IronTesseract();
    var result = ocr.Read(imagePath);
    extractedTexts[imagePath] = result.Text;
});

foreach (var (path, text) in extractedTexts)
    Console.WriteLine($"{System.IO.Path.GetFileName(path)}: {text.Length} characters");
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Threading.Tasks

Dim extractedTexts As New ConcurrentDictionary(Of String, String)()

' Each IronTesseract instance is independent — no shared state
Parallel.ForEach(imagePaths, Sub(imagePath)
    Dim ocr As New IronTesseract()
    Dim result = ocr.Read(imagePath)
    extractedTexts(imagePath) = result.Text
End Sub)

For Each kvp In extractedTexts
    Dim path = kvp.Key
    Dim text = kvp.Value
    Console.WriteLine($"{System.IO.Path.GetFileName(path)}: {text.Length} characters")
Next
$vbLabelText   $csharpLabel

IronOCR在并行工作开始前不需要任何初始化序列。 在应用程序启动时的一次IronOcr.License.LicenseKey分配激活所有实例。 每个线程不重新加载模板,每个实例不进行JSON验证。 对于非阻塞服务器端场景,异步OCR指南涵盖基于await的ASP.NET Core和Azure Functions模式。

从扫描标签存档输出可搜索的 PDF 文件

Dynamsoft 没有任何形式的 PDF 输出功能。 通过 Dynamsoft 处理的扫描标签档案仍然是仅图像文件——无法搜索,无法被文档管理系统索引,也不符合 PDF/A 归档标准。 IronOCR通过对OcrResult对象进行一次方法调用,添加可搜索的PDF输出。

Dynamsoft 方法:

using Dynamsoft.LabelRecognizer;

// Process scanned label — extract text into a string
LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();
recognizer.AppendSettingsFromString(labelTemplate);

var results = recognizer.RecognizeFile("scanned-labels.jpg");
var extractedText = new StringBuilder();
foreach (var r in results)
    foreach (var line in r.LineResults)
        extractedText.AppendLine(line.Text);

// Cannot create a searchable PDF — save text to a sidecar .txt file instead
System.IO.File.WriteAllText("scanned-labels.txt", extractedText.ToString());

// The original image remains unsearchable
Console.WriteLine("No PDF output available in Dynamsoft Label Recognizer.");
recognizer.Dispose();
using Dynamsoft.LabelRecognizer;

// Process scanned label — extract text into a string
LabelRecognizer.InitLicense(licenseKey);
var recognizer = new LabelRecognizer();
recognizer.AppendSettingsFromString(labelTemplate);

var results = recognizer.RecognizeFile("scanned-labels.jpg");
var extractedText = new StringBuilder();
foreach (var r in results)
    foreach (var line in r.LineResults)
        extractedText.AppendLine(line.Text);

// Cannot create a searchable PDF — save text to a sidecar .txt file instead
System.IO.File.WriteAllText("scanned-labels.txt", extractedText.ToString());

// The original image remains unsearchable
Console.WriteLine("No PDF output available in Dynamsoft Label Recognizer.");
recognizer.Dispose();
Imports Dynamsoft.LabelRecognizer
Imports System.IO
Imports System.Text

' Process scanned label — extract text into a string
LabelRecognizer.InitLicense(licenseKey)
Dim recognizer = New LabelRecognizer()
recognizer.AppendSettingsFromString(labelTemplate)

Dim results = recognizer.RecognizeFile("scanned-labels.jpg")
Dim extractedText = New StringBuilder()
For Each r In results
    For Each line In r.LineResults
        extractedText.AppendLine(line.Text)
    Next
Next

' Cannot create a searchable PDF — save text to a sidecar .txt file instead
File.WriteAllText("scanned-labels.txt", extractedText.ToString())

' The original image remains unsearchable
Console.WriteLine("No PDF output available in Dynamsoft Label Recognizer.")
recognizer.Dispose()
$vbLabelText   $csharpLabel

IronOCR方法:

using IronOcr;

// Read, preprocess, and produce a searchable PDF archive in one pipeline
using var input = new OcrInput();
input.LoadImage("scanned-labels.jpg");
input.Deskew();
input.Contrast();

var ocr = new IronTesseract();
var result = ocr.Read(input);

// Searchable PDF: original image layer preserved, invisible text layer added
result.SaveAsSearchablePdf("scanned-labels-searchable.pdf");

Console.WriteLine($"Searchable PDF created. Confidence: {result.Confidence}%");
Console.WriteLine($"Extracted text preview: {result.Text.Substring(0, 100)}");
using IronOcr;

// Read, preprocess, and produce a searchable PDF archive in one pipeline
using var input = new OcrInput();
input.LoadImage("scanned-labels.jpg");
input.Deskew();
input.Contrast();

var ocr = new IronTesseract();
var result = ocr.Read(input);

// Searchable PDF: original image layer preserved, invisible text layer added
result.SaveAsSearchablePdf("scanned-labels-searchable.pdf");

Console.WriteLine($"Searchable PDF created. Confidence: {result.Confidence}%");
Console.WriteLine($"Extracted text preview: {result.Text.Substring(0, 100)}");
Imports IronOcr

' Read, preprocess, and produce a searchable PDF archive in one pipeline
Using input As New OcrInput()
    input.LoadImage("scanned-labels.jpg")
    input.Deskew()
    input.Contrast()

    Dim ocr As New IronTesseract()
    Dim result = ocr.Read(input)

    ' Searchable PDF: original image layer preserved, invisible text layer added
    result.SaveAsSearchablePdf("scanned-labels-searchable.pdf")

    Console.WriteLine($"Searchable PDF created. Confidence: {result.Confidence}%")
    Console.WriteLine($"Extracted text preview: {result.Text.Substring(0, 100)}")
End Using
$vbLabelText   $csharpLabel

可搜索的PDF输出保留了原始扫描图像的全分辨率,并在其上添加了一层不可见的OCR文本层。文档管理系统、搜索索引和PDF查看器现在可以搜索标签内容。 可搜索的 PDF 操作指南扫描文档处理指南涵盖多页存档和批量转换工作流程。

Dynamsoft OCR API 到IronOCR映射参考

Dynamsoft 标签识别器 IronOCR当量
LabelRecognizer.InitLicense(key) IronOcr.License.LicenseKey = key
new LabelRecognizer() new IronTesseract()
recognizer.AppendSettingsFromString(json) 无需手动配置 — 自动配置
recognizer.RecognizeFile(path) ocr.Read(path)
recognizer.RecognizeByFile(path, templateName) ocr.Read(path)
recognizer.RecognizeBuffer(buffer, width, height, ...) ocr.Read(input)
DLRResult[](结果数组) OcrResult(单一结果对象)
DLRResult.LineResults OcrResult.Lines
DLRLineResult.Text OcrResult.Line.Text
DLRLineResult.Confidence OcrResult.Line.Words[i].Confidence
DLRLineResult.Location.Points[i] OcrResult.Line.X, .Y, .Width, .Height
ReferenceRegionArray(JSON) new CropRectangle(x, y, w, h)
LabelRecognizerParameterArray(JSON) 不要求
模板中的CharacterModelName 不要求
recognizer.Dispose() using var ocr = new IronTesseract()
不支持 PDF 输入 input.LoadPdf(path)
没有可搜索的 PDF 输出 result.SaveAsSearchablePdf(path)
不支持多页 TIFF 输入 input.LoadImageFrames(path)
条形码读取(单独产品) ocr.Configuration.ReadBarCodes = true
多个InitLicense调用(每个产品) 单一IronOcr.License.LicenseKey分配

常见迁移问题和解决方案

问题 1:迁移后 AppendSettingsFromString 返回空结果

Dynamsoft:识别在DLRResult数组。 不会抛出异常。 从 Dynamsoft 迁移过来的团队有时会在结果解析代码中添加防御性空值检查,以防止这种静默故障。

解决方案:完全移除AppendSettingsFromString。 IronOCR无需模板配置。 如果需要特定区域目标,请用ReferenceRegionArray JSON:

// Remove all AppendSettingsFromString calls
// Replace region JSON with a CropRectangle constructor
var region = new CropRectangle(x: 0, y: 400, width: 1200, height: 300);
using var input = new OcrInput();
input.LoadImage(imagePath, region);
var result = new IronTesseract().Read(input);
// Remove all AppendSettingsFromString calls
// Replace region JSON with a CropRectangle constructor
var region = new CropRectangle(x: 0, y: 400, width: 1200, height: 300);
using var input = new OcrInput();
input.LoadImage(imagePath, region);
var result = new IronTesseract().Read(input);
Imports System

' Remove all AppendSettingsFromString calls
' Replace region JSON with a CropRectangle constructor
Dim region As New CropRectangle(x:=0, y:=400, width:=1200, height:=300)
Using input As New OcrInput()
    input.LoadImage(imagePath, region)
    Dim result = New IronTesseract().Read(input)
End Using
$vbLabelText   $csharpLabel

IronTesseract 设置指南记录了所有可以设置为属性而不是 JSON 字符串的引擎配置选项。

问题 2:应用程序启动时多次调用 InitLicense

Dynamsoft:使用标签识别器、条形码阅读器和文档标准化器的应用程序在启动时调用三个独立的InitLicense方法,每个都需要其自己的许可证密钥。 团队存储三个环境变量,独立轮换三个密钥,并调试三个独立的初始化失败。

解决方案:移除所有Dynamsoft InitLicense调用。 替换为一条IronOCR线:

// Remove:
// LabelRecognizer.InitLicense(mrzKey);
// BarcodeReader.InitLicense(barcodeKey);
// DocumentNormalizer.InitLicense(docKey);

// Add once, at application startup:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
// Remove:
// LabelRecognizer.InitLicense(mrzKey);
// BarcodeReader.InitLicense(barcodeKey);
// DocumentNormalizer.InitLicense(docKey);

// Add once, at application startup:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
' Remove:
' LabelRecognizer.InitLicense(mrzKey)
' BarcodeReader.InitLicense(barcodeKey)
' DocumentNormalizer.InitLicense(docKey)

' Add once, at application startup:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
$vbLabelText   $csharpLabel

一个环境变量即可激活IronOCR 的所有功能——OCR、条形码、PDF 处理以及 125 多种语言——无需对每个功能进行初始化。

问题 3:LineResult 文本聚合产生乱码输出

Dynamsoft:DLRResult。 将DLRResult对象的顺序是区域检测顺序,而不是读取顺序。

解决方案:IronOCR的结果层次结构按阅读顺序组织输出。 使用PagesParagraphsLines结构以自然文档顺序访问文本:

var result = new IronTesseract().Read(imagePath);

// Reading-order text — no manual aggregation
Console.WriteLine(result.Text);

// Or access paragraph-level groupings
foreach (var paragraph in result.Pages[0].Paragraphs)
    Console.WriteLine(paragraph.Text);
var result = new IronTesseract().Read(imagePath);

// Reading-order text — no manual aggregation
Console.WriteLine(result.Text);

// Or access paragraph-level groupings
foreach (var paragraph in result.Pages[0].Paragraphs)
    Console.WriteLine(paragraph.Text);
Imports IronOcr

Dim result = New IronTesseract().Read(imagePath)

' Reading-order text — no manual aggregation
Console.WriteLine(result.Text)

' Or access paragraph-level groupings
For Each paragraph In result.Pages(0).Paragraphs
    Console.WriteLine(paragraph.Text)
Next
$vbLabelText   $csharpLabel

问题 4:部署服务器上缺少模板 JSON 文件

Dynamsoft:模板 JSON 文件是单独的部署工件。 当模板文件丢失或在新服务器上路径不正确时,AppendSettingsFromString失败——有时默默地,有时伴随运行时异常——而且部署破坏了识别,而无需接触应用程序代码。

解决方法: IronOCR没有模板文件。 替换NuGet包并更新命名空间后,部署工件列表缩小到应用程序二进制文件和一个环境变量。 如有需要,添加启动验证检查:

// Validate license at startup — no template files to check
if (!IronOcr.License.IsValidLicense)
    throw new InvalidOperationException("IronOCR license key is invalid or missing.");

var ocr = new IronTesseract();
// Ready to process — no file dependencies
// Validate license at startup — no template files to check
if (!IronOcr.License.IsValidLicense)
    throw new InvalidOperationException("IronOCR license key is invalid or missing.");

var ocr = new IronTesseract();
// Ready to process — no file dependencies
Imports IronOcr

' Validate license at startup — no template files to check
If Not IronOcr.License.IsValidLicense Then
    Throw New InvalidOperationException("IronOCR license key is invalid or missing.")
End If

Dim ocr As New IronTesseract()
' Ready to process — no file dependencies
$vbLabelText   $csharpLabel

问题五:跨多个产品的处置链管理

Dynamsoft:每个Dynamsoft产品实例(LabelRecognizer, BarcodeReader, DocumentNormalizer) 实现IDisposable。 聚合多个产品的服务类带有多层次的Dispose调用会导致本地资源泄漏,表现为负载下的内存增长。

解决方案:IronOCR的IDisposable,但处理模型更简单——一个类,一个模式。 在DI容器中注册一个using块来获取短生命周期的实例:

// Short-lived pattern
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = new IronTesseract().Read(input);

// Long-lived singleton (register in Program.cs)
builder.Services.AddSingleton<IronTesseract>();
// Short-lived pattern
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = new IronTesseract().Read(input);

// Long-lived singleton (register in Program.cs)
builder.Services.AddSingleton<IronTesseract>();
Imports IronOcr

' Short-lived pattern
Using input As New OcrInput()
    input.LoadImage(imagePath)
    Dim result = (New IronTesseract()).Read(input)
End Using

' Long-lived singleton (register in Program.vb)
builder.Services.AddSingleton(Of IronTesseract)()
$vbLabelText   $csharpLabel

进度跟踪指南涵盖了长时间运行的批处理作业的生命周期管理,异步 OCR 指南展示了ASP.NET Core中的单例模式。

问题 6:自定义标签格式不支持角色模型

Dynamsoft:模板中的CharacterModelName字段引用一个必须存在于Dynamsoft SDK安装目录中的模型文件。 自定义角色模型需要从 Dynamsoft 的门户网站下载额外的模型文件,并将其放置在正确的 SDK 路径中。 模型缺失会导致识别失败,并出现晦涩难懂的运行时错误。

解决方法: IronOCR不使用字符模型文件。 对于自定义或特殊字体,请使用自定义语言培训:

// No character model files needed
// For specialized fonts, use a custom language pack
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.English;  // or load a custom trained pack

using var input = new OcrInput();
input.LoadImage(specializedFontImage);
input.Binarize();  // helps with specialized font clarity

var result = ocr.Read(input);
// No character model files needed
// For specialized fonts, use a custom language pack
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.English;  // or load a custom trained pack

using var input = new OcrInput();
input.LoadImage(specializedFontImage);
input.Binarize();  // helps with specialized font clarity

var result = ocr.Read(input);
Imports IronTesseract

' No character model files needed
' For specialized fonts, use a custom language pack
Dim ocr As New IronTesseract()
ocr.Language = OcrLanguage.English  ' or load a custom trained pack

Using input As New OcrInput()
    input.LoadImage(specializedFontImage)
    input.Binarize()  ' helps with specialized font clarity

    Dim result = ocr.Read(input)
End Using
$vbLabelText   $csharpLabel

自定义字体训练指南涵盖了在不进行任何文件路径配置的情况下,为专有标签字体训练自定义语言包。

Dynamsoft OCR 迁移检查清单

迁移前

审核代码库中所有与 Dynamsoft 相关的引用:

# Find all Dynamsoft using statements
grep -r "using Dynamsoft" --include="*.cs" .

# Find InitLicense calls (one per product)
grep -r "InitLicense" --include="*.cs" .

# Find AppendSettingsFromString calls (template loads)
grep -r "AppendSettingsFromString" --include="*.cs" .

# Find template JSON strings (inline or file references)
grep -r "LabelRecognizerParameterArray\|ReferenceRegionArray\|CharacterModelName" --include="*.cs" .

# Find RecognizeFile and RecognizeByFile calls
grep -r "RecognizeFile\|RecognizeByFile\|RecognizeBuffer" --include="*.cs" .

# Find LineResult result parsing patterns
grep -r "LineResults\|DLRResult\|DLRLineResult" --include="*.cs" .

# Identify any JSON template files in the project
find . -name "*.json" | xargs grep -l "LabelRecognizerParameterArray" 2>/dev/null
# Find all Dynamsoft using statements
grep -r "using Dynamsoft" --include="*.cs" .

# Find InitLicense calls (one per product)
grep -r "InitLicense" --include="*.cs" .

# Find AppendSettingsFromString calls (template loads)
grep -r "AppendSettingsFromString" --include="*.cs" .

# Find template JSON strings (inline or file references)
grep -r "LabelRecognizerParameterArray\|ReferenceRegionArray\|CharacterModelName" --include="*.cs" .

# Find RecognizeFile and RecognizeByFile calls
grep -r "RecognizeFile\|RecognizeByFile\|RecognizeBuffer" --include="*.cs" .

# Find LineResult result parsing patterns
grep -r "LineResults\|DLRResult\|DLRLineResult" --include="*.cs" .

# Identify any JSON template files in the project
find . -name "*.json" | xargs grep -l "LabelRecognizerParameterArray" 2>/dev/null
SHELL

调查结果:

  • 计算InitLicense调用点数量(每个使用的Dynamsoft产品一个)
  • 计算AppendSettingsFromString调用点数量(每个识别模板一个)
  • 列出所有将在迁移后删除的 JSON 模板文件
  • 识别任何CropRectangle
  • 注意哪些功能由单独的产品涵盖(条形码、文档规范化)

代码迁移

  1. 移除Dynamsoft.LabelRecognizer NuGet包(以及任何其他Dynamsoft包)
  2. 安装IronOcr NuGet包(dotnet add package IronOcr)
  3. 将所有using IronOcr;
  4. 在应用程序启动时用一个LabelRecognizer.InitLicense(key)调用
  5. 移除所有AppendSettingsFromString(json)调用——无必须的等效品
  6. ReferenceRegionArray JSON坐标
  7. new LabelRecognizer()实例化
  8. recognizer.RecognizeFile(path)
  9. DLRLineResult结果迭代
  10. lineResult.Text字符串聚合
  11. BarcodeReader.InitLicense + BarcodeReader.DecodeFile
  12. 移除多产品using var ocr = new IronTesseract()替换
  13. 从项目和部署工件中删除 JSON 模板文件
  14. input.LoadImageFrames(path)替换TIFF文件的帧迭代循环
  15. 在需要存档扫描输出的地方添加result.SaveAsSearchablePdf(outputPath)

后迁移

  • 在应用程序启动时验证true
  • 确认所有识别结果为之前工作的输入返回非空result.Text
  • 在代表性样本上检查result.Confidence—值高于80%表示识别质量良好
  • 通过将CropRectangle输出与之前的Dynamsoft区域结果对比来测试区域定位精度
  • 通过在以前需要Dynamsoft条形码阅读器的输入上运行ocr.Configuration.ReadBarCodes = true验证条形码阅读
  • 验证多页TIFF处理产生每帧一个OcrResult.Page
  • 确认可搜索的 PDF 输出在 PDF 查看器中支持文本搜索。
  • 使用Parallel.ForEach运行并行处理测试,并验证没有线程争用
  • 测试所有部署目标(Linux、Docker、Azure),以确认单包部署无需 JSON 模板文件即可正常工作
  • 确认部署配置中不存在 Dynamsoft 许可证密钥环境变量

迁移到IronOCR的主要优势

一个包消除了多产品协调税。 每种能力——通用OCR、MRZ提取、条形码阅读、原生PDF输入、可搜索PDF输出、预处理和125+种语言——作为一个单一IronOcr NuGet包提供。 版本升级影响一个.csproj文件中的一个包条目。许可证密钥旋转是一个环境变量。 部署工件清单缩减为应用程序二进制文件,不再包含 JSON 模板文件和角色模型目录。

零配置文件依赖。Dynamsoft模板系统要求 JSON 文件在运行时存在且路径正确。IronOCR 除了NuGet包本身之外,没有任何其他运行时文件依赖。 Docker 镜像变得具有确定性:通过 CI 测试的镜像将完全在生产环境中运行。 Docker部署指南展示了一个完整的Dockerfile,只需要一个apt-get install行用于Linux。

结构化输出可减少集成代码。 Dynamsoft返回原始LineResult文本字符串。 结构化数据提取——字段位置、单词边界、每个标记的置信度——需要对您的代码进行后处理和测试。IronOCR的结果层次结构将页面、段落、行、单词和字符作为类型集合呈现,每个级别都有坐标和置信度分数。 从 Dynamsoft 迁移过来的团队通常会删除 30-50% 的结果处理代码,因为IronOCR提供了他们之前手动构建的结构。 OCR 结果功能页面记录了完整的输出模型。

永久授权消除了年度预算的不确定性。Dynamsoft标签识别器没有永久授权选项。 仅标签识别一项,运行五年的处理集群每年就要花费 2995 美元以上,还不包括条形码或文档规范化产品。 IronOCR的$999 Lite许可证是一次性购买,涵盖所有功能无期限。 对于政府、Enterprise和长期生产部署而言,从核心文档处理组件中移除年度续订依赖性,可以降低采购复杂性,并消除项目进行中订阅失效的风险。 IronOCR许可页面列出了所有级别以及每个级别涵盖的内容。

无需平台特定配置即可跨平台使用。IronOCRIronOCRNuGet 的运行时标识符图谱,为 Windows x64、Linux x64、macOS x64 和 macOS ARM 解析正确的运行时二进制文件。 无条件<PackageReference>块,无平台检测代码,无本地二进制路径设置。在WindowsLinuxAWS LambdaAzure App Service上编译和运行相同的using IronOcr;代码,无需修改。

语言覆盖范围可根据应用需求进行扩展。Dynamsoft标签识别器专为拉丁字符 MRZ 格式而设计。 非拉丁文字——阿拉伯语、中文、日语、韩语、希伯来语——不在其设计范围之内。 IronOCR支持以单独的NuGet包安装125+种语言:dotnet add package IronOcr.Languages.Arabic无需在识别管道中更改代码即可添加阿拉伯语支持。语言索引列出了每个可用的语言包。 对于处理国际身份证件、多语言货运标签或跨境发票的应用程序而言,这种广度意味着一个库就能涵盖不断增长的应用程序遇到的每一种文档类型。

请注意Dynamsoft和Tesseract是其各自所有者的注册商标。 此网站与Dynamsoft或Google没有关联,也未获得支持或赞助。 所有产品名称、徽标和品牌均为各自所有者的财产。 比较仅供参考,反映撰写时公开可用的信息。

常见问题解答

我为什么要从 Dynamsoft OCR SDK 迁移到 IronOCR?

常见的驱动因素包括消除 COM 互操作的复杂性、取代基于文件的许可证管理、避免按页计费、启用 Docker/容器部署以及采用与标准 .NET 工具集成的 NuGet 原生工作流程。

从 Dynamsoft OCR SDK 迁移到 IronOCR 时,主要的代码变更有哪些?

将 Dynamsoft OCR 初始化序列替换为 IronTesseract 实例化,移除 COM 生命周期管理(显式的创建/加载/关闭模式),并更新结果属性名称。这样可以显著减少样板代码行数。

我该如何安装 IronOCR 以开始迁移?

在软件包管理器控制台中运行“Install-Package IronOcr”,或在命令行界面中运行“dotnet add package IronOcr”。语言包是单独的软件包:例如,法语语言包需要运行“dotnet add package IronOcr.Languages.French”。

IronOCR 在标准商业文档的 OCR 识别准确率方面是否能与 Dynamsoft OCR SDK 相媲美?

IronOCR 对标准商业内容(包括发票、合同、收据和打印表格)的识别准确率很高。图像预处理滤镜(例如去倾斜、去噪、对比度增强)可进一步提高对质量较差输入文件的识别率。

IronOCR 如何处理 Dynamsoft OCR SDK 单独安装的语言数据?

IronOCR 中的语言数据以 NuGet 包的形式分发。“dotnet add package IronOcr.Languages.German”命令用于安装德语支持。无需手动放置文件或指定目录路径。

从 Dynamsoft OCR SDK 迁移到 IronOCR 是否需要更改部署基础架构?

IronOCR 所需的架构变更比 Dynamsoft OCR SDK 少。它无需修改 SDK 二进制文件路径、许可证文件位置或进行许可证服务器配置。NuGet 包包含完整的 OCR 引擎,许可证密钥是应用程序代码中设置的一个字符串。

迁移后如何配置 IronOCR 许可?

在应用程序启动代码中,将 IronOcr.License.LicenseKey 赋值为 "YOUR-KEY"。在 Docker 或 Kubernetes 中,将此密钥存储为环境变量,并在启动时读取。使用 License.IsValidLicense 在接受流量之前进行验证。

IronOCR 能否像 Dynamsoft OCR 那样处理 PDF 文件?

是的。IronOCR 可以读取原生 PDF 和扫描版 PDF。实例化 IronTesseract,调用 ocr.Read(input) 方法(其中 input 可以是 PDF 路径或 OcrPdfInput),然后遍历 OcrResult 页面。无需单独的 PDF 渲染流程。

IronOCR 如何处理大批量处理中的线程问题?

IronTesseract 可以安全地按线程实例化。在 Parallel.ForEach 或 Task 池中为每个线程启动一个实例,并发运行 OCR,并在完成后释放每个实例。无需全局状态或锁定。

IronOCR在提取文本后支持哪些输出格式?

IronOCR 返回结构化结果,包括文本、词坐标、置信度评分和页面结构。导出选项包括纯文本、可搜索 PDF 和用于下游处理的结构化结果对象。

对于可扩展的工作负载而言,IronOCR 的定价是否比 Dynamsoft OCR SDK 更具可预测性?

IronOCR采用永久统一费率许可模式,不收取任何页数或数量费用。无论您处理1万页还是1000万页,许可费用都保持不变。批量许可和团队许可选项请参见IronOCR定价页面。

从 Dynamsoft OCR SDK 迁移到 IronOCR 后,我现有的测试会发生什么变化?

迁移后,对提取的文本内容进行断言的测试应该继续通过。验证 API 调用模式或 COM 对象生命周期的测试需要更新,以反映 IronOCR 更简化的初始化和结果模型。

Kannaopat Udonpant
软件工程师
在成为软件工程师之前,Kannapat 在日本北海道大学完成了环境资源博士学位。在攻读学位期间,Kannapat 还成为了车辆机器人实验室的成员,隶属于生物生产工程系。2022 年,他利用自己的 C# 技能加入 Iron Software 的工程团队,专注于 IronPDF。Kannapat 珍视他的工作,因为他可以直接从编写大多数 IronPDF 代码的开发者那里学习。除了同行学习外,Kannapat 还喜欢在 Iron Software 工作的社交方面。不撰写代码或文档时,Kannapat 通常可以在他的 PS5 上玩游戏或重温《最后生还者》。

钢铁支援团队

我们每周 5 天,每天 24 小时在线。
聊天
电子邮件
打电话给我