跳至頁尾內容
影片

從 Dynamsoft OCR 遷移到 IronOCR

這個指南為從Dynamsoft Label Recognizer遷移到IronOCR的.NET開發者提供了一個完整的遷移路徑。 它涵蓋了NuGet套件替換、命名空間更新、授權初始化,以及針對不同於一般功能比較的場景的實際程式碼遷移範例——包括範本配置消除、運行時設定JSON刪除、區域定義遷移和結果解析簡化。

為何從Dynamsoft OCR遷移

Dynamsoft Label Recognizer是一個定製的專家級工具。這種精確度在狹窄的部署中效果很好,但當應用程式範圍超出原始的MRZ或VIN使用案例時會造成阻礙。

運行時設定JSON是一個維護面。每個Dynamsoft識別工作流程都始於AppendSettingsFromString,載入一個JSON文件宣告參數陣列、字元模型和區域引用。 這些JSON範本是獨立的部署工件——不會編譯到您的二進制文件中。 當Dynamsoft在各個SDK版本中改變範本模式時,這些範本需要版本控制、部署管道管理和手動更新。 IronOCR不需要任何配置文件:實例化IronTesseract,呼叫.Read(),引擎會自動選擇合適的設置。

年度訂閱定價複合。Dynamsoft Label Recognizer的授權費為每裝置每年$599以上。 沒有永久選項。 五個裝置的處理集群成本為每年$2,995以上——而這僅覆蓋MRZ和結構化標籤識別。 新增Dynamsoft Barcode Reader以識別條形碼或Dynamsoft Document Normalizer以進行邊緣校正,年度費用按產品數量倍增。 IronOCR的Lite授權為$999一次性付款,涵蓋通用OCR、結構化標籤、條形碼、原生PDF、可搜尋的PDF輸出,以及超過125種語言的單一套裝。

區域定義需要JSON範本。在Dynamsoft中定義識別區域意味著編寫一個ReferenceRegionArray JSON塊,在LabelRecognizerParameterArray中引用,並在任何圖像處理開始前載入整個文件。 IronOCR使用一個直接傳遞給OcrInput.LoadImageCropRectangle(x, y, width, height)構造函式。 改變一個區域只是修改一個數字,而不是重新解析JSON文件。

結果解析完全由您負責。 RecognizeFileRecognizeByFile會返回包含原始文字字串的LineResult陣列。 您所需要的每個結構——欄位偏移、日期轉換、校驗位驗證、名字分隔符處理——都需要在這些原始輸出之上編寫自定義解析程式碼。 IronOCR返回一個帶有.Pages.Lines.Words.CharactersOcrResult,作為已填充邊界框坐標、自信度分數和字體中繼資料的型別化集合。

缺乏原生PDF支持創造了潛在的依賴關係。Dynamsoft Label Recognizer沒有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 vs Dynamsoft OCR:功能比較

下表涵蓋了兩個程式庫之間的完整能力差距。

功能 Dynamsoft Label Recognizer IronOCR
一般文件OCR 不支持 全支持
MRZ識別 專用(原始文字輸出) 內建結構化欄位
VIN識別 專用(需範本) 標準OCR,帶區域針對性
工業標籤識別 專用(需範本) 是,通過CropRectangle
運行時設置配置 必需(通過AppendSettingsFromString的JSON) 不需要
範本部署工件 必需(JSON文件) None
本地PDF輸入 不支持
受密碼保護的 PDF 不支持
多頁TIFF輸入 手動頁面迭代 原生(LoadImageFrames
可搜尋的 PDF 輸出 不支持 是(SaveAsSearchablePdf
條碼讀取 單獨產品(Dynamsoft Barcode Reader) 內建(ReadBarCodes = true
自動預處理 有限 是(校正、降噪、對比度、二值化、銳化、膨脹、腐蝕)
結構化輸出(單詞、行、段落) 原生LineResult文字字串 完全型別化,具有坐標和自信度
語言支持 有限(拉丁MRZ) 超過125種語言,捆綁為NuGet套包
同時多語言支持 不支持 是(OcrLanguage.French + OcrLanguage.German
置信分數 僅限按行 按單詞、行、頁面
hOCR匯出 不支持
許可模式 年度訂閱(每裝置每年$599以上) 永久($999一次性,Lite層)
需要多個產品 是(三個以上完整涵蓋) 否(單一套包)
.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 gallery安裝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:初始化許可證

將每個Dynamsoft靜態InitLicense調用替換為應用程式啟動時的單一IronOCR授權分配:

// 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 Label Recognizer的專長。 其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

CropRectangle 坐標使用與Dynamsoft的百分比基準FirstPoint/SecondPoint規格相同的像素參考幀——可以通過將Dynamsoft的百分比值乘以圖片尺寸轉換。 預處理管道(ContrastSharpen)取代了Dynamsoft在其VIN範本中內建的角色模型優化。 圖像質量校正指南涵蓋了浮凸和低對比表面過濾器的選擇。

多頁TIFF批處理

Dynamsoft Label Recognizer處理單一圖像。 多頁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 Label Recognizer是執行緒安全的,但其RecognizeFile調用是同步的。 將它包裝在Task.Run中會產生並行處理,但每次調用都透過AppendSettingsFromString共享配置狀態——在多個執行緒上載入模板需要仔細的初始化次序。 IronOCR的IronTesseract實例是獨立的:每個任務建立一個,呼叫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 Label Recognizer 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:AppendSettingsFromString收到格式錯誤的JSON字串時,識別會靜默返回空的DLRResult陣列。 不會拋出異常。 從Dynamsoft遷移的團隊有時會在其結果解析程式碼中增加防禦性空值檢查以防止這種靜默故障。

解決方案:完全移除AppendSettingsFromString。 IronOCR不需要任何範本配置。 如果需要特定的區域定位,則將ReferenceRegionArray JSON替換為CropRectangle

// 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:使用Label Recognizer、Barcode Reader和Document Normalizer的應用程式在啟動時調用了三個獨立的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:RecognizeFile會為每個檢測到的標籤區域返回一個DLRResult。 使用\n分隔符跨所有結果串接lineResult.Text的應用程式,會產生混合了頁面上不同標籤區域行的輸出——因為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

問題5:跨多個產品的處置鏈管理

Dynamsoft:每個Dynamsoft產品實例(LabelRecognizerBarcodeReaderDocumentNormalizer)都實作IDisposable。 聚合多個產品的服務類具有多級Dispose實作,而在任何產品實例上遺漏一次Dispose調用都會導致原生資源洩漏,這表現為負載下記憶體增長。

解決方案:IronOCR的IronTesseractOcrInput都實作IDisposable,但處置模型更簡單——一個類,一個模式。 在DI容器中將單一IronTesseract實例註冊為單例,或對短生命週期的實例使用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範本文件
  • 識別任何ReferenceRegionArray定義(轉換為CropRectangle
  • 記錄由獨立產品(條形碼、文件標準化)覆蓋的功能

程式碼遷移

  1. 移除Dynamsoft.LabelRecognizerNuGet套件(及任何其他Dynamsoft套件)
  2. 安裝IronOcr NuGet套件(dotnet add package IronOcr
  3. 將所有using Dynamsoft.LabelRecognizer;using Dynamsoft.Core;替換為using IronOcr;
  4. 在應用程式啟動時將所有LabelRecognizer.InitLicense(key)調用替換為單一的IronOcr.License.LicenseKey = key
  5. 移除所有AppendSettingsFromString(json)調用—無需等效項
  6. ReferenceRegionArray JSON座標替換為new CropRectangle(x, y, width, height)構造函式
  7. new LabelRecognizer()實例化替換為new IronTesseract()
  8. recognizer.RecognizeFile(path)替換為ocr.Read(path)
  9. DLRResult/DLRLineResult結果迭代替換為OcrResult.Pages/.Lines/.Words階層結構
  10. 將手動的lineResult.Text字串聚合替換為result.Text或結構化頁面列舉
  11. BarcodeReader.InitLicense + BarcodeReader.DecodeFile替換為ocr.Configuration.ReadBarCodes = true
  12. 移除多產品Dispose鏈,並替換為using var ocr = new IronTesseract()
  13. 從專案和部署工件中刪除JSON範本文件
  14. input.LoadImageFrames(path)替換TIFF文件的幀迭代迴圈
  15. 在掃描輸出需要存檔的地方新增result.SaveAsSearchablePdf(outputPath)

遷移後

  • 驗證IronOcr.License.IsValidLicense在應用程式啟動時返回true
  • 確保所有識別結果對於之前有效的輸入返回非空的result.Text
  • 在代表性樣品中檢查result.Confidence——超過80%的值表示良好的識別質量
  • 通過比較CropRectangle輸出與先前的Dynamsoft區域結果測試區域針對精度
  • 通過在先前需要Dynamsoft Barcode Reader的輸入上運行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 Label Recognizer沒有永久選項。 僅運行五年的處理集群每年花費$2,995以上,用於標籤識別即可,尚未考慮新增條形碼或文件標準化產品。 IronOCR的$999Lite授權是一次性購買,無限期涵蓋所有功能。 對於政府、企業和長期生產部署,從核心文件處理組件中移除年度續費依賴減少了采購複雜性並消除了mid項目訂閱中斷的風險。 IronOCR 授權頁面列出了所有等級及其涵蓋內容。

跨平台,無需平台特定配置。IronOCR通過NuGet的運行時標識符圖解決Windows x64、Linux x64、macOS x64和macOS ARM的正確運行時二進制文件。 無條件的using IronOcr;程式碼編譯並在WindowsLinuxAWS LambdaAzure App Service上運行,無需修改。

語言覆蓋隨應用需求擴展。Dynamsoft Label Recognizer針對拉丁字元MRZ格式進行設計。 非拉丁字元系統——如阿拉伯語、中文、日文、韓文、希伯來文——不在其設計範圍內。 IronOCR支持125多種語言,安裝為獨立的NuGet包: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生命週期管理(顯式Create/Load/Close模式),並更新結果屬性名稱。結果是顯著減少了樣板程式碼行數。

如何安裝IronOCR以開始遷移?

在套件管理器控制台中運行 'Install-Package IronOcr' 或在CLI中運行 'dotnet add package IronOcr'。語言包是單獨的包:例如 'dotnet add package IronOcr.Languages.French' 安裝法語支持。

IronOCR能否匹配Dynamsoft OCR SDK在標準商業文件上的OCR準確性?

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。實例化IronTesseract,調用ocr.Read(input),其中input是PDF路徑或OcrPdfInput,並迭代OcrResult頁面。不需要單獨的PDF渲染管道。

IronOCR如何處理大批量處理中的多執行緒?

IronTesseract可以安全地在每個執行緒中實例化。在Parallel.ForEach或Task池中為每個執行緒建立一個實例,並發併運行OCR,當完成時處置每個實例。不需要全域狀態或鎖定。

IronOCR在文字提取後支持哪些輸出格式?

IronOCR返回結構化結果,包括文字、單詞坐標、信心分數和頁面結構。導出選項包括純文字、可搜索PDF以及下游處理的結構化結果物件。

IronOCR的定價對於擴展工作負載來說是否比Dynamsoft OCR SDK更可預測?

IronOCR使用固定費率的永久性許可證,沒有按頁或按量收費。無論您處理10000頁還是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上玩遊戲或重看The Last of Us。

Iron 支援團隊

我們線上24小時,每週5天。
聊天
電子郵件
給我打電話