
請求書OCRオープンソース比較:最適なツールを見つける
LEADTOOLSは1990年からSDKソフトウェアを提供しており、そのOCR統合はその歴史を完全に継承しています。単一の文字を認識する前に、アプリケーションはディスク上の2つのバイナリファイルを見つけ、engine.Startup()を呼び出し、そしてそれからのみ実際の認識作業を開始します。 すべての運用マシンには、ライセンスファイルを特定のパスに配置する必要があります。 Dockerコンテナでは、これらのファイルをマウントまたはベイクする必要があります。 CI/CDパイプラインでは、ファイルが正しい場所に配置されている必要があります。そうでない場合、アプリケーションは起動時にエラーをスローします。また、開発者が間違ったバンドルを購入した場合、OCRモジュールがまったく含まれていない可能性があります。なぜなら、LEADTOOLSはOCR機能を画像コーデックとは別のモジュールとして販売しているからです。
LEADTOOLS OCRについて理解する
LEADTOOLSは、1990年から商用SDKソフトウェアを出荷しているLEAD Technologies社が提供するドキュメントイメージングプラットフォームです。OCR機能は、ドキュメントビューア、医用画像処理(DICOM/PACS)、バーコード読み取り、フォーム認識、注釈付け、PDF操作など、多くの機能を網羅したツールキットに含まれるモジュールの1つです。 このアーキテクチャは、LEADTOOLSがOCRライブラリではなく、OCR機能を搭載した画像処理プラットフォームであることを意味します。
LEADTOOLS OCRの主なアーキテクチャ特性:
- 3つの独立したOCRエンジン: LEAD独自のエンジン(基本ライセンスに含まれる)、Tesseractラッパー(tessdataファイルが必要)、およびOmniPageエンジン(別途Kofaxライセンス契約と別のベンダーとの関係が必要)。 エンジンの選択は、コードとコストの両方に影響を与える。
- ファイルベースのライセンス展開: 2つのファイル -
LEADTOOLS.LIC.KEY- はランタイム時に読み取り可能でなければなりません。パスはハードコードされているか、スタートアップ時に解決されます。パスの解決はIISとコンソールホストで異なり、Dockerとベアメタル、リリースビルドとデバッグでも異なります。 RasterCodecsが必須の中間ステップ: 画像の読み込みは直接OCRエンジンに移行しません。PDFページを含むすべての画像は、最初にRasterCodecsインスタンスを通じて読み込まれ、エンジンが起動する前に初期化されなければなりません。- 明示的なエンジン生命周期:
Dispose()の前に呼び出されなければなりません。 シャットダウン呼び出しを誤った順序でスキップすると、エラーが発生します。 -バンドル価格設定の複雑さ: LEADTOOLSは価格を一般に公開していません。 特定の要件に基づいた見積もりについては、LEADTOOLS営業にお問い合わせください。 アップデートを受け取るためには、年間のメンテナンス料金が必要です。 - 大きな展開フットプリント: プロダクション展開には複数のLEADTOOLS DLL、
tessdata/フォルダが含まれます。
初期化シーケンス
LEADTOOLSでは、認識を行う前に特定の複数ステップの設定が必要です。 順序はオプションではありません - RasterCodecsが初期化される前に呼び出すと、ランタイムエラーが発生します。
using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;
public class LeadtoolsOcrService : IDisposable
{
private IOcrEngine _ocrEngine;
private RasterCodecs _codecs;
public LeadtoolsOcrService()
{
// Step 1: Locate and validate both license files
RasterSupport.SetLicense(
@"C:\LEADTOOLS\License\LEADTOOLS.LIC",
File.ReadAllText(@"C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY"));
// Step 2: Initialize image codec layer
_codecs = new RasterCodecs();
// Step 3: Select engine type — wrong choice means different behavior
_ocrEngine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD);
// Step 4: Load runtime into memory (500–2000ms)
_ocrEngine.Startup(_codecs, null, null,
@"C:\LEADTOOLS\OCR\OcrRuntime");
}
public string ExtractText(string imagePath)
{
using var image = _codecs.Load(imagePath);
using var document = _ocrEngine.DocumentManager.CreateDocument();
var page = document.Pages.AddPage(image, null);
page.Recognize(null); // Recognition is not automatic
return page.GetText(-1);
}
public void Dispose()
{
_ocrEngine?.Shutdown(); // Must call before Dispose
_ocrEngine?.Dispose();
_codecs?.Dispose();
}
}Imports Leadtools
Imports Leadtools.Ocr
Imports Leadtools.Codecs
Imports System.IO
Public Class LeadtoolsOcrService
Implements IDisposable
Private _ocrEngine As IOcrEngine
Private _codecs As RasterCodecs
Public Sub New()
' Step 1: Locate and validate both license files
RasterSupport.SetLicense(
"C:\LEADTOOLS\License\LEADTOOLS.LIC",
File.ReadAllText("C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY"))
' Step 2: Initialize image codec layer
_codecs = New RasterCodecs()
' Step 3: Select engine type — wrong choice means different behavior
_ocrEngine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD)
' Step 4: Load runtime into memory (500–2000ms)
_ocrEngine.Startup(_codecs, Nothing, Nothing,
"C:\LEADTOOLS\OCR\OcrRuntime")
End Sub
Public Function ExtractText(imagePath As String) As String
Using image = _codecs.Load(imagePath)
Using document = _ocrEngine.DocumentManager.CreateDocument()
Dim page = document.Pages.AddPage(image, Nothing)
page.Recognize(Nothing) ' Recognition is not automatic
Return page.GetText(-1)
End Using
End Using
End Function
Public Sub Dispose() Implements IDisposable.Dispose
If _ocrEngine IsNot Nothing Then
_ocrEngine.Shutdown() ' Must call before Dispose
_ocrEngine.Dispose()
End If
If _codecs IsNot Nothing Then
_codecs.Dispose()
End If
End Sub
End ClassImports Leadtools
Imports Leadtools.Ocr
Imports Leadtools.Codecs
Imports System.IO
Public Class LeadtoolsOcrService
Implements IDisposable
Private _ocrEngine As IOcrEngine
Private _codecs As RasterCodecs
Public Sub New()
' Step 1: Locate and validate both license files
RasterSupport.SetLicense(
"C:\LEADTOOLS\License\LEADTOOLS.LIC",
File.ReadAllText("C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY"))
' Step 2: Initialize image codec layer
_codecs = New RasterCodecs()
' Step 3: Select engine type — wrong choice means different behavior
_ocrEngine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD)
' Step 4: Load runtime into memory (500–2000ms)
_ocrEngine.Startup(_codecs, Nothing, Nothing,
"C:\LEADTOOLS\OCR\OcrRuntime")
End Sub
Public Function ExtractText(imagePath As String) As String
Using image = _codecs.Load(imagePath)
Using document = _ocrEngine.DocumentManager.CreateDocument()
Dim page = document.Pages.AddPage(image, Nothing)
page.Recognize(Nothing) ' Recognition is not automatic
Return page.GetText(-1)
End Using
End Using
End Function
Public Sub Dispose() Implements IDisposable.Dispose
If _ocrEngine IsNot Nothing Then
_ocrEngine.Shutdown() ' Must call before Dispose
_ocrEngine.Dispose()
End If
If _codecs IsNot Nothing Then
_codecs.Dispose()
End If
End Sub
End Classこれは必要最低限の実装です。 ライセンスパスの失敗、エンジン状態の検証、またはバッチ処理に必要なメモリ管理に対するエラーハンドリングが含まれていません(RasterImageインスタンスの放置を忘れると、プロセスがクラッシュするまでメモリが蓄積されます)。
IronOCRを理解する
IronOCRは、最適化されたTesseract 5 LSTMエンジンをベースに構築された商用.NET OCRライブラリです。これはOCRに特化した製品であり、OCRをモジュールの一つとして搭載した画像処理プラットフォームではありません。 設計目標は、開発者と認識されるテキストの間にあるあらゆるインフラストラクチャ上のオーバーヘッドを排除することです。
IronOCRの主な特徴:
- 単一のNuGetパッケージ:
dotnet add package IronOcrは、ネイティブ依存関係を含むすべてをインストールし、追加のランタイムディレクトリ、tessdataフォルダ、または展開するライセンスファイルは必要ありません。 -文字列ベースのライセンス: 1行でライセンスキーを割り当てます。 キーは環境変数、appsettings.json、Azure Key Vault、AWS Secrets Managerから取得できます - 既に使用されているどの秘密管理パターンでも利用可能です。 ディスク上にファイルはありません。 - エンジンライフサイクルなし:
IronTesseractは初めて使用する際に遅延ロードで初期化されます。Shutdown()コールもなく、操作間でエンジン自体を廃棄する必要もありません。 - ネイティブPDF入力: PDFは
OcrInput.LoadPdf()を通じて直接ロードされます。 ページごとのラスタループ、バイト順指定、中間RasterImageオブジェクトの手動廃棄はありません。 - 自動前処理: スキュー補正、ノイズ除去、コントラスト強調、二値化、解像度正規化が
OcrInputで単一のメソッドコールとして利用可能です。 これらは全てのページに同時に適用されます。 - NuGetによる125以上の言語: 各言語は個別のNuGetパッケージです(
IronOcr.Languages.Frenchなど)。 管理すべきtessdataディレクトリはなく、パスの設定も不要です。 - デフォルトでスレッドセーフ:
IronTesseractインスタンスは同時使用に安全です。 並列処理には同期コードは不要です。 - 永続ライセンス: $999 Lite / $1,499 Plus / $2,999 Professional / $5,999 Unlimited、1回限りの購入で1年間の更新が含まれます。
機能比較
| フィーチャー | リードツールズOCR | IronOCR |
|---|---|---|
| セットアップの複雑さ | 高 - 4段階の初期化シーケンス | 低レベル - NuGetパッケージ 1 つ |
| ライセンス展開 | すべてのマシンに2つのファイル(.LIC + .LIC.KEY) | 文字列キー、どこにでも保存可能 |
| 価格設定モデル | 価格については、LEADTOOLS営業にお問い合わせください | $5,999 一回限りの永続 |
| PDFサポート | 手動によるページごとのラスタライズ | ネイティブLoadPdf() |
| 前処理 | マニュアル - 個別の画像処理コマンド | 組み込みのフィルタメソッド |
| エンジンライフサイクル | 手動のStartup() / Shutdown()が必要 | 自動、怠惰 |
| NuGetパッケージ | 複数(Leadtools、Leadtools.Ocr、Leadtools.Codecs、Leadtools.Pdf) | 1つ(IronOcr) |
| スレッドセーフ。 | 慎重な管理が必要 | 内蔵 |
詳細な機能比較
| フィーチャー | リードツールズOCR | IronOCR |
|---|---|---|
| ライセンスについて | ||
| ライセンスメカニズム | .LIC + .LIC.KEYファイルペア | 文字列キー |
| ライセンス展開 | すべての生産機械上のファイル | 環境変数または設定 |
| 価格の透明性 | 営業相談が必要です | ウェブサイトに掲載 |
| 永久オプション | いいえ(年間メンテナンス不要) | はい |
| セットアップとインストール | ||
| 必要なNuGetパッケージ | 最低3~4ページ(PDFの場合はそれ以上) | 1 |
| ランタイムファイルが必要です | はい - OcrRuntime/ディレクトリ | なし |
| エンジン初期化 | ランタイムパスでの手動Startup() | None |
| エンジン停止 | Dispose() | None |
| tessdata management | Tesseractエンジンオプションに必要 | なし |
| OCR機能 | ||
| エンジンオプション | LEAD、Tesseract、OmniPage(別途ライセンスが必要) | IronTesseract(最適化されたTesseract 5 LSTM) |
| 言語 | 60~120(エンジンによる) | NuGet経由で125件以上 |
| 言語展開 | tessdata files or engine-bundled | NuGet言語パッケージ |
| 信頼度スコア | page.RecognizeStatus | result.Confidence (割合) |
| 構造化された出力 | ページ、ゾーンレベル | ページ、段落、行、単語、文字 |
| バーコード読み取り | LEADTOOLSバーコードモジュール(別売) | 組み込み(ReadBarCodes = true) |
| PDF処理 | ||
| PDF入力 | ページごとのラスタライズループ | ネイティブLoadPdf() |
| パスワードPDF | 追加のライセンスが必要なLeadtools.Pdfモジュールが必要 | 組み込みPasswordパラメータ |
| 検索可能なPDF出力 | DocumentWriter構成 + document.Save() | result.SaveAsSearchablePdf() |
| ページ範囲の選択 | 手動ループ境界 | LoadPdfPages(path, start, end) |
| 前処理 | ||
| デスキュー | DeskewCommand (手動で画像ごと) | input.Deskew() |
| ノイズ除去 | DespeckleCommand (手動で画像ごと) | input.DeNoise() |
| バイナリ化 | AutoBinarizeCommand (手動で画像ごと) | input.Binarize() |
| コントラスト強調 | ContrastBrightnessCommand (手動) | input.Contrast() |
| 解像度の向上 | 手動グレースケール+リサンプルコマンド | input.EnhanceResolution(300) |
| デプロイメント | ||
| Docker | LIC/KEY ファイルはマウントまたはベイクする必要があります | 標準dotnet publish |
| Linux | ネイティブ依存関係でサポートされています | サポート対象、依存関係がバンドルされています |
| エアギャップ | はい | はい |
| CI/CDの複雑性 | 各環境におけるライセンスファイルとランタイムパス | シークレットマネージャーのライセンスキー |
ライセンスアーキテクチャ:ファイル展開方式と文字列キー方式
LEADTOOLSとIronOCRのライセンスアーキテクチャの違いは、開発者の利便性だけの問題ではなく、デプロイメントパイプライン、コンテナ戦略、運用コストに直接影響を与えます。
リードツールズアプローチ
LEADTOOLSはアプリケーションの起動時に存在し読み取り可能な2つの物理ファイルを必要とします。.csソースファイルのコードはそのパターンを確認します。
// From leadtools-migration-examples.cs
public void InitializeLicense()
{
// Option 1: Absolute paths — deployment dependent
string licPath = @"C:\LEADTOOLS\License\LEADTOOLS.LIC";
string keyPath = @"C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY";
// Option 2: Relative paths — working directory dependent
licPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LEADTOOLS.LIC");
keyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LEADTOOLS.LIC.KEY");
// Read key file content (not the path — the content)
string key = File.ReadAllText(keyPath);
// Set license — silent failure on some error types
RasterSupport.SetLicense(licPath, key);
// Verify license is valid for the module you purchased
if (!RasterSupport.IsLocked(RasterSupportType.Document))
{
throw new InvalidOperationException("Document module not licensed");
}
}// From leadtools-migration-examples.cs
public void InitializeLicense()
{
// Option 1: Absolute paths — deployment dependent
string licPath = @"C:\LEADTOOLS\License\LEADTOOLS.LIC";
string keyPath = @"C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY";
// Option 2: Relative paths — working directory dependent
licPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LEADTOOLS.LIC");
keyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LEADTOOLS.LIC.KEY");
// Read key file content (not the path — the content)
string key = File.ReadAllText(keyPath);
// Set license — silent failure on some error types
RasterSupport.SetLicense(licPath, key);
// Verify license is valid for the module you purchased
if (!RasterSupport.IsLocked(RasterSupportType.Document))
{
throw new InvalidOperationException("Document module not licensed");
}
}Imports System
Imports System.IO
Public Sub InitializeLicense()
' Option 1: Absolute paths — deployment dependent
Dim licPath As String = "C:\LEADTOOLS\License\LEADTOOLS.LIC"
Dim keyPath As String = "C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY"
' Option 2: Relative paths — working directory dependent
licPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LEADTOOLS.LIC")
keyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LEADTOOLS.LIC.KEY")
' Read key file content (not the path — the content)
Dim key As String = File.ReadAllText(keyPath)
' Set license — silent failure on some error types
RasterSupport.SetLicense(licPath, key)
' Verify license is valid for the module you purchased
If Not RasterSupport.IsLocked(RasterSupportType.Document) Then
Throw New InvalidOperationException("Document module not licensed")
End If
End SubImports System
Imports System.IO
Public Sub InitializeLicense()
' Option 1: Absolute paths — deployment dependent
Dim licPath As String = "C:\LEADTOOLS\License\LEADTOOLS.LIC"
Dim keyPath As String = "C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY"
' Option 2: Relative paths — working directory dependent
licPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LEADTOOLS.LIC")
keyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LEADTOOLS.LIC.KEY")
' Read key file content (not the path — the content)
Dim key As String = File.ReadAllText(keyPath)
' Set license — silent failure on some error types
RasterSupport.SetLicense(licPath, key)
' Verify license is valid for the module you purchased
If Not RasterSupport.IsLocked(RasterSupportType.Document) Then
Throw New InvalidOperationException("Document module not licensed")
End If
End Sub故障モードは複数あり、それぞれに個別の修復が必要となる。 パスの解決はIISとコンソールホストの間で異なります。bin/ReleaseまたはDockerコンテナ内のパスと一致しません。明示的にマッピングしない限り。 LICファイルとKEYファイルは、同じダウンロードファイルから取得する必要があります。異なるダウンロードファイルから取得されたファイルを使用すると、"キーがライセンスファイルと一致しません"というエラーが発生します。 ライセンスがドキュメントバンドルのみをカバーしているが、コードが認識バンドルの機能を使用する場合、IsLocked()はfalseを返し、アプリケーションは起動時に不一致を処理しなければなりません。
制作におけるよくあるエラー:
"License file not found at specified path"- パスが誤ったディレクトリに解決された"License key does not match license file"- 異なるダウンロードからのファイル、または転送中に破損した"Document module not licensed"- 誤ったバンドルが購入された"License has expired"- 評価ライセンスが切れているか、メンテナンスが失効している
IronOCRのアプローチ
IronOCRのライセンスは、単一の文字列割り当てです。 ファイルなし、パス解決なし、2ピース検証なし:
// From application startup — store key using any secrets management pattern
IronOcr.License.LicenseKey = "IRONSUITE.YOUR-LICENSE-KEY";
// Production: pull from environment variable
IronOcr.License.LicenseKey =
Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
// Or from ASP.NET configuration
IronOcr.License.LicenseKey = Configuration["IronOCR:LicenseKey"];// From application startup — store key using any secrets management pattern
IronOcr.License.LicenseKey = "IRONSUITE.YOUR-LICENSE-KEY";
// Production: pull from environment variable
IronOcr.License.LicenseKey =
Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
// Or from ASP.NET configuration
IronOcr.License.LicenseKey = Configuration["IronOCR:LicenseKey"];' From application startup — store key using any secrets management pattern
IronOcr.License.LicenseKey = "IRONSUITE.YOUR-LICENSE-KEY"
' Production: pull from environment variable
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
' Or from ASP.NET configuration
IronOcr.License.LicenseKey = Configuration("IronOCR:LicenseKey")' From application startup — store key using any secrets management pattern
IronOcr.License.LicenseKey = "IRONSUITE.YOUR-LICENSE-KEY"
' Production: pull from environment variable
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
' Or from ASP.NET configuration
IronOcr.License.LicenseKey = Configuration("IronOCR:LicenseKey")キーは、Azure Key Vault、AWS Secrets Manager、Kubernetes Secrets、またはDocker環境変数に格納できます。 Dockerイメージにはファイルを含める必要はありません。 CI/CDパイプラインのステップでは、ライセンス成果物をコピーしてエージェントを構築することはありません。 IronOCRのライセンスページには、営業電話なしで利用可能なプランが直接記載されています。
エンジン設定と詳細度
基本的なOCRタスクにおけるLEADTOOLSとIronOCRのコードの違いは、それぞれのライブラリのAPI設計思想を明確に示している。
リードツールズアプローチ
最小限の働くLEADTOOLS実装(leadtools-vs-ironocr-examples.csから直接取り出した)は、テキストが返される前に10の異なる操作が必要です。
using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;
public class LeadtoolsOcrService : IDisposable
{
private IOcrEngine _ocrEngine;
private RasterCodecs _codecs;
public LeadtoolsOcrService()
{
// Step 1: License files
RasterSupport.SetLicense(
@"C:\LEADTOOLS\License\LEADTOOLS.LIC",
File.ReadAllText(@"C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY"));
// Step 2: Codec layer
_codecs = new RasterCodecs();
// Step 3: Engine factory
_ocrEngine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD);
// Step 4: Engine startup (500–2000ms blocking call)
_ocrEngine.Startup(_codecs, null, null,
@"C:\LEADTOOLS\OCR\OcrRuntime");
}
public string ExtractText(string imagePath)
{
using var image = _codecs.Load(imagePath); // Step 5: Load via codecs
using var document = _ocrEngine.DocumentManager // Step 6: Create document
.CreateDocument();
var page = document.Pages.AddPage(image, null); // Step 7: Add page
page.Recognize(null); // Step 8: Explicit recognize
return page.GetText(-1); // Step 9: Extract text
}
public void Dispose()
{
_ocrEngine?.Shutdown(); // Step 10: Shutdown before dispose
_ocrEngine?.Dispose();
_codecs?.Dispose();
}
}using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;
public class LeadtoolsOcrService : IDisposable
{
private IOcrEngine _ocrEngine;
private RasterCodecs _codecs;
public LeadtoolsOcrService()
{
// Step 1: License files
RasterSupport.SetLicense(
@"C:\LEADTOOLS\License\LEADTOOLS.LIC",
File.ReadAllText(@"C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY"));
// Step 2: Codec layer
_codecs = new RasterCodecs();
// Step 3: Engine factory
_ocrEngine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD);
// Step 4: Engine startup (500–2000ms blocking call)
_ocrEngine.Startup(_codecs, null, null,
@"C:\LEADTOOLS\OCR\OcrRuntime");
}
public string ExtractText(string imagePath)
{
using var image = _codecs.Load(imagePath); // Step 5: Load via codecs
using var document = _ocrEngine.DocumentManager // Step 6: Create document
.CreateDocument();
var page = document.Pages.AddPage(image, null); // Step 7: Add page
page.Recognize(null); // Step 8: Explicit recognize
return page.GetText(-1); // Step 9: Extract text
}
public void Dispose()
{
_ocrEngine?.Shutdown(); // Step 10: Shutdown before dispose
_ocrEngine?.Dispose();
_codecs?.Dispose();
}
}Imports Leadtools
Imports Leadtools.Ocr
Imports Leadtools.Codecs
Imports System.IO
Public Class LeadtoolsOcrService
Implements IDisposable
Private _ocrEngine As IOcrEngine
Private _codecs As RasterCodecs
Public Sub New()
' Step 1: License files
RasterSupport.SetLicense(
"C:\LEADTOOLS\License\LEADTOOLS.LIC",
File.ReadAllText("C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY"))
' Step 2: Codec layer
_codecs = New RasterCodecs()
' Step 3: Engine factory
_ocrEngine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD)
' Step 4: Engine startup (500–2000ms blocking call)
_ocrEngine.Startup(_codecs, Nothing, Nothing,
"C:\LEADTOOLS\OCR\OcrRuntime")
End Sub
Public Function ExtractText(imagePath As String) As String
Using image = _codecs.Load(imagePath) ' Step 5: Load via codecs
Using document = _ocrEngine.DocumentManager ' Step 6: Create document
.CreateDocument()
Dim page = document.Pages.AddPage(image, Nothing) ' Step 7: Add page
page.Recognize(Nothing) ' Step 8: Explicit recognize
Return page.GetText(-1) ' Step 9: Extract text
End Using
End Using
End Function
Public Sub Dispose() Implements IDisposable.Dispose
_ocrEngine?.Shutdown() ' Step 10: Shutdown before dispose
_ocrEngine?.Dispose()
_codecs?.Dispose()
End Sub
End ClassImports Leadtools
Imports Leadtools.Ocr
Imports Leadtools.Codecs
Imports System.IO
Public Class LeadtoolsOcrService
Implements IDisposable
Private _ocrEngine As IOcrEngine
Private _codecs As RasterCodecs
Public Sub New()
' Step 1: License files
RasterSupport.SetLicense(
"C:\LEADTOOLS\License\LEADTOOLS.LIC",
File.ReadAllText("C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY"))
' Step 2: Codec layer
_codecs = New RasterCodecs()
' Step 3: Engine factory
_ocrEngine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD)
' Step 4: Engine startup (500–2000ms blocking call)
_ocrEngine.Startup(_codecs, Nothing, Nothing,
"C:\LEADTOOLS\OCR\OcrRuntime")
End Sub
Public Function ExtractText(imagePath As String) As String
Using image = _codecs.Load(imagePath) ' Step 5: Load via codecs
Using document = _ocrEngine.DocumentManager ' Step 6: Create document
.CreateDocument()
Dim page = document.Pages.AddPage(image, Nothing) ' Step 7: Add page
page.Recognize(Nothing) ' Step 8: Explicit recognize
Return page.GetText(-1) ' Step 9: Extract text
End Using
End Using
End Function
Public Sub Dispose() Implements IDisposable.Dispose
_ocrEngine?.Shutdown() ' Step 10: Shutdown before dispose
_ocrEngine?.Dispose()
_codecs?.Dispose()
End Sub
End ClassDocumentManager.CreateDocument() / Pages.AddPage() / page.Recognize() / page.GetText() チェーンは折りたたむことができません。 各ステップは個別のAPI呼び出しです。 page.GetText(-1)の前に省くと空のテキストが返されます - ページを追加することで認識が自動的に引き起こされるわけではありません。
IronOCRのアプローチ
同等のIronOCR実装(同じ比較ファイルから)は、コア操作が1行で、ライフサイクル管理はゼロです。
using IronOcr;
public class OcrService
{
public string ExtractText(string imagePath)
{
return new IronTesseract().Read(imagePath).Text;
}
}using IronOcr;
public class OcrService
{
public string ExtractText(string imagePath)
{
return new IronTesseract().Read(imagePath).Text;
}
}Imports IronOcr
Public Class OcrService
Public Function ExtractText(imagePath As String) As String
Return New IronTesseract().Read(imagePath).Text
End Function
End ClassImports IronOcr
Public Class OcrService
Public Function ExtractText(imagePath As String) As String
Return New IronTesseract().Read(imagePath).Text
End Function
End Classコール間でIronTesseractインスタンスが再利用される本番使用の場合:
public class OcrService
{
private readonly IronTesseract _ocr = new IronTesseract();
public string ExtractText(string imagePath)
{
return _ocr.Read(imagePath).Text;
}
}public class OcrService
{
private readonly IronTesseract _ocr = new IronTesseract();
public string ExtractText(string imagePath)
{
return _ocr.Read(imagePath).Text;
}
}Imports IronTesseract
Public Class OcrService
Private ReadOnly _ocr As New IronTesseract()
Public Function ExtractText(imagePath As String) As String
Return _ocr.Read(imagePath).Text
End Function
End ClassImports IronTesseract
Public Class OcrService
Private ReadOnly _ocr As New IronTesseract()
Public Function ExtractText(imagePath As String) As String
Return _ocr.Read(imagePath).Text
End Function
End ClassShutdown()なし、コーデック層なし、ドキュメントコンテナなし、明示的な認識呼び出しなし。 IronTesseract APIのリファレンスには、表面積全体が表示されます。 セットアップガイドでは、本番環境における設定オプションについて説明します。
PDF処理
LEADTOOLSのページ単位のラスタライズモデルが最も顕著に現れるのは、PDF OCRの処理時です。 LEADTOOLSにはネイティブなPDF認識パスがなく、各PDFページはIOcrDocumentに追加され、個別に認識され、個別にテキストが抽出されなければなりません。
リードツールズアプローチ
leadtools-pdf-processing.csからの基本PDFワークフロー:
public string ExtractTextFromPdf(string pdfPath)
{
var text = new StringBuilder();
// Get page count first — separate call
var pdfInfo = _codecs.GetInformation(pdfPath, true);
int totalPages = pdfInfo.TotalPages;
using var document = _ocrEngine.DocumentManager.CreateDocument();
for (int pageNum = 1; pageNum <= totalPages; pageNum++)
{
// Load each page as a raster image — must dispose each
using var pageImage = _codecs.Load(
pdfPath,
0, // bitsPerPixel
CodecsLoadByteOrder.BgrOrGray,
pageNum, // firstPage
pageNum); // lastPage
var page = document.Pages.AddPage(pageImage, null);
page.Recognize(null);
text.AppendLine(page.GetText(-1));
}
return text.ToString();
}public string ExtractTextFromPdf(string pdfPath)
{
var text = new StringBuilder();
// Get page count first — separate call
var pdfInfo = _codecs.GetInformation(pdfPath, true);
int totalPages = pdfInfo.TotalPages;
using var document = _ocrEngine.DocumentManager.CreateDocument();
for (int pageNum = 1; pageNum <= totalPages; pageNum++)
{
// Load each page as a raster image — must dispose each
using var pageImage = _codecs.Load(
pdfPath,
0, // bitsPerPixel
CodecsLoadByteOrder.BgrOrGray,
pageNum, // firstPage
pageNum); // lastPage
var page = document.Pages.AddPage(pageImage, null);
page.Recognize(null);
text.AppendLine(page.GetText(-1));
}
return text.ToString();
}Imports System.Text
Public Function ExtractTextFromPdf(ByVal pdfPath As String) As String
Dim text As New StringBuilder()
' Get page count first — separate call
Dim pdfInfo = _codecs.GetInformation(pdfPath, True)
Dim totalPages As Integer = pdfInfo.TotalPages
Using document = _ocrEngine.DocumentManager.CreateDocument()
For pageNum As Integer = 1 To totalPages
' Load each page as a raster image — must dispose each
Using pageImage = _codecs.Load(pdfPath, 0, CodecsLoadByteOrder.BgrOrGray, pageNum, pageNum)
Dim page = document.Pages.AddPage(pageImage, Nothing)
page.Recognize(Nothing)
text.AppendLine(page.GetText(-1))
End Using
Next
End Using
Return text.ToString()
End FunctionImports System.Text
Public Function ExtractTextFromPdf(ByVal pdfPath As String) As String
Dim text As New StringBuilder()
' Get page count first — separate call
Dim pdfInfo = _codecs.GetInformation(pdfPath, True)
Dim totalPages As Integer = pdfInfo.TotalPages
Using document = _ocrEngine.DocumentManager.CreateDocument()
For pageNum As Integer = 1 To totalPages
' Load each page as a raster image — must dispose each
Using pageImage = _codecs.Load(pdfPath, 0, CodecsLoadByteOrder.BgrOrGray, pageNum, pageNum)
Dim page = document.Pages.AddPage(pageImage, Nothing)
page.Recognize(Nothing)
text.AppendLine(page.GetText(-1))
End Using
Next
End Using
Return text.ToString()
End Functionパスワードで保護されたPDFを処理すると、別の依存関係が追加されます。 leadtools-pdf-processing.csから:
// Requires Leadtools.Pdf module — additional license
using Leadtools.Pdf;
public string ExtractFromEncryptedPdf(string pdfPath, string password)
{
var pdfFile = new PDFFile(pdfPath);
pdfFile.Password = password;
var loadOptions = new CodecsLoadOptions();
_codecs.Options.Pdf.Load.Password = password;
// ... same page iteration loop follows
}// Requires Leadtools.Pdf module — additional license
using Leadtools.Pdf;
public string ExtractFromEncryptedPdf(string pdfPath, string password)
{
var pdfFile = new PDFFile(pdfPath);
pdfFile.Password = password;
var loadOptions = new CodecsLoadOptions();
_codecs.Options.Pdf.Load.Password = password;
// ... same page iteration loop follows
}Imports Leadtools.Pdf
Public Function ExtractFromEncryptedPdf(ByVal pdfPath As String, ByVal password As String) As String
Dim pdfFile As New PDFFile(pdfPath)
pdfFile.Password = password
Dim loadOptions As New CodecsLoadOptions()
_codecs.Options.Pdf.Load.Password = password
' ... same page iteration loop follows
End FunctionImports Leadtools.Pdf
Public Function ExtractFromEncryptedPdf(ByVal pdfPath As String, ByVal password As String) As String
Dim pdfFile As New PDFFile(pdfPath)
pdfFile.Password = password
Dim loadOptions As New CodecsLoadOptions()
_codecs.Options.Pdf.Load.Password = password
' ... same page iteration loop follows
End FunctionLeadtools.Pdf名前空間は別のモジュールです。 開発チームがOCRモジュールを購入したがPDFモジュールを購入していない場合、別途ライセンスを購入しない限り、暗号化されたPDFのサポートは利用できません。
検索可能なPDF出力を作成するには、保存する前にDocumentWriterを構成する必要があります。
// From leadtools-pdf-processing.cs
var pdfOptions = new PdfDocumentOptions
{
DocumentType = PdfDocumentType.Pdf,
ImageOverText = true,
Linearized = false,
Title = Path.GetFileNameWithoutExtension(inputPdfPath)
};
_engine.DocumentWriterInstance.SetOptions(DocumentFormat.Pdf, pdfOptions);
document.Save(outputPdfPath, DocumentFormat.Pdf, null);// From leadtools-pdf-processing.cs
var pdfOptions = new PdfDocumentOptions
{
DocumentType = PdfDocumentType.Pdf,
ImageOverText = true,
Linearized = false,
Title = Path.GetFileNameWithoutExtension(inputPdfPath)
};
_engine.DocumentWriterInstance.SetOptions(DocumentFormat.Pdf, pdfOptions);
document.Save(outputPdfPath, DocumentFormat.Pdf, null);Imports System.IO
Dim pdfOptions As New PdfDocumentOptions With {
.DocumentType = PdfDocumentType.Pdf,
.ImageOverText = True,
.Linearized = False,
.Title = Path.GetFileNameWithoutExtension(inputPdfPath)
}
_engine.DocumentWriterInstance.SetOptions(DocumentFormat.Pdf, pdfOptions)
document.Save(outputPdfPath, DocumentFormat.Pdf, Nothing)Imports System.IO
Dim pdfOptions As New PdfDocumentOptions With {
.DocumentType = PdfDocumentType.Pdf,
.ImageOverText = True,
.Linearized = False,
.Title = Path.GetFileNameWithoutExtension(inputPdfPath)
}
_engine.DocumentWriterInstance.SetOptions(DocumentFormat.Pdf, pdfOptions)
document.Save(outputPdfPath, DocumentFormat.Pdf, Nothing)IronOCRのアプローチ
IronOCRはPDFをネイティブに処理します。 基本的なPDF、パスワード保護されたPDF、検索可能なPDF出力という、同じ3つのシナリオは、それぞれ2~3行に短縮されます。
using IronOcr;
// Basic PDF — all pages, automatic handling
public string ExtractTextFromPdf(string pdfPath)
{
using var input = new OcrInput();
input.LoadPdf(pdfPath);
return new IronTesseract().Read(input).Text;
}
// Password-protected PDF — no additional module required
public string ExtractFromEncryptedPdf(string pdfPath, string password)
{
using var input = new OcrInput();
input.LoadPdf(pdfPath, Password: password);
return new IronTesseract().Read(input).Text;
}
// 検索可能なPDF出力 — one method call
public void CreateSearchablePdf(string inputPdfPath, string outputPdfPath)
{
using var input = new OcrInput();
input.LoadPdf(inputPdfPath);
var result = new IronTesseract().Read(input);
result.SaveAsSearchablePdf(outputPdfPath);
}using IronOcr;
// Basic PDF — all pages, automatic handling
public string ExtractTextFromPdf(string pdfPath)
{
using var input = new OcrInput();
input.LoadPdf(pdfPath);
return new IronTesseract().Read(input).Text;
}
// Password-protected PDF — no additional module required
public string ExtractFromEncryptedPdf(string pdfPath, string password)
{
using var input = new OcrInput();
input.LoadPdf(pdfPath, Password: password);
return new IronTesseract().Read(input).Text;
}
// 検索可能なPDF出力 — one method call
public void CreateSearchablePdf(string inputPdfPath, string outputPdfPath)
{
using var input = new OcrInput();
input.LoadPdf(inputPdfPath);
var result = new IronTesseract().Read(input);
result.SaveAsSearchablePdf(outputPdfPath);
}Imports IronOcr
' Basic PDF — all pages, automatic handling
Public Function ExtractTextFromPdf(ByVal pdfPath As String) As String
Using input As New OcrInput()
input.LoadPdf(pdfPath)
Return New IronTesseract().Read(input).Text
End Using
End Function
' Password-protected PDF — no additional module required
Public Function ExtractFromEncryptedPdf(ByVal pdfPath As String, ByVal password As String) As String
Using input As New OcrInput()
input.LoadPdf(pdfPath, Password:=password)
Return New IronTesseract().Read(input).Text
End Using
End Function
' 検索可能なPDF出力 — one method call
Public Sub CreateSearchablePdf(ByVal inputPdfPath As String, ByVal outputPdfPath As String)
Using input As New OcrInput()
input.LoadPdf(inputPdfPath)
Dim result = New IronTesseract().Read(input)
result.SaveAsSearchablePdf(outputPdfPath)
End Using
End SubImports IronOcr
' Basic PDF — all pages, automatic handling
Public Function ExtractTextFromPdf(ByVal pdfPath As String) As String
Using input As New OcrInput()
input.LoadPdf(pdfPath)
Return New IronTesseract().Read(input).Text
End Using
End Function
' Password-protected PDF — no additional module required
Public Function ExtractFromEncryptedPdf(ByVal pdfPath As String, ByVal password As String) As String
Using input As New OcrInput()
input.LoadPdf(pdfPath, Password:=password)
Return New IronTesseract().Read(input).Text
End Using
End Function
' 検索可能なPDF出力 — one method call
Public Sub CreateSearchablePdf(ByVal inputPdfPath As String, ByVal outputPdfPath As String)
Using input As New OcrInput()
input.LoadPdf(inputPdfPath)
Dim result = New IronTesseract().Read(input)
result.SaveAsSearchablePdf(outputPdfPath)
End Using
End SubPDF入力ガイドでは、ページ範囲の選択、ストリーム入力、およびページごとの結果へのアクセスについて説明します。 検索可能なPDFのサンプルには、出力ワークフロー全体が表示されています。 認識前に低品質のスキャン済みPDFを前処理する方法については、画像品質補正ガイドを参照してください。
前処理:手動コマンドと組み込みフィルターの比較
LEADTOOLSは画像処理コマンドを別のLeadtools.ImageProcessing名前空間を通じて提供します。 これらのコマンドは、OCRエンジンに画像を渡す前に、各RasterImageインスタンスに個別に適用しなければなりません。
リードツールズアプローチ
leadtools-pdf-processing.csから、PDFページの前処理:
using Leadtools.ImageProcessing;
public string ProcessLowQualityPdf(string pdfPath)
{
var text = new StringBuilder();
var pdfInfo = _codecs.GetInformation(pdfPath, true);
using var document = _engine.DocumentManager.CreateDocument();
for (int i = 1; i <= pdfInfo.TotalPages; i++)
{
using var pageImage = _codecs.Load(pdfPath, 0,
CodecsLoadByteOrder.BgrOrGray, i, i);
// Each command is a separate instantiation and Run() call
var deskewCommand = new DeskewCommand();
deskewCommand.Run(pageImage);
var despeckleCommand = new DespeckleCommand();
despeckleCommand.Run(pageImage);
if (pageImage.BitsPerPixel > 8)
{
var grayscaleCommand = new GrayscaleCommand(8);
grayscaleCommand.Run(pageImage);
}
var binarizeCommand = new AutoBinarizeCommand();
binarizeCommand.Run(pageImage);
var page = document.Pages.AddPage(pageImage, null);
page.Recognize(null);
text.AppendLine(page.GetText(-1));
}
return text.ToString();
}using Leadtools.ImageProcessing;
public string ProcessLowQualityPdf(string pdfPath)
{
var text = new StringBuilder();
var pdfInfo = _codecs.GetInformation(pdfPath, true);
using var document = _engine.DocumentManager.CreateDocument();
for (int i = 1; i <= pdfInfo.TotalPages; i++)
{
using var pageImage = _codecs.Load(pdfPath, 0,
CodecsLoadByteOrder.BgrOrGray, i, i);
// Each command is a separate instantiation and Run() call
var deskewCommand = new DeskewCommand();
deskewCommand.Run(pageImage);
var despeckleCommand = new DespeckleCommand();
despeckleCommand.Run(pageImage);
if (pageImage.BitsPerPixel > 8)
{
var grayscaleCommand = new GrayscaleCommand(8);
grayscaleCommand.Run(pageImage);
}
var binarizeCommand = new AutoBinarizeCommand();
binarizeCommand.Run(pageImage);
var page = document.Pages.AddPage(pageImage, null);
page.Recognize(null);
text.AppendLine(page.GetText(-1));
}
return text.ToString();
}Imports Leadtools.ImageProcessing
Imports System.Text
Public Function ProcessLowQualityPdf(pdfPath As String) As String
Dim text As New StringBuilder()
Dim pdfInfo = _codecs.GetInformation(pdfPath, True)
Using document = _engine.DocumentManager.CreateDocument()
For i As Integer = 1 To pdfInfo.TotalPages
Using pageImage = _codecs.Load(pdfPath, 0, CodecsLoadByteOrder.BgrOrGray, i, i)
' Each command is a separate instantiation and Run() call
Dim deskewCommand As New DeskewCommand()
deskewCommand.Run(pageImage)
Dim despeckleCommand As New DespeckleCommand()
despeckleCommand.Run(pageImage)
If pageImage.BitsPerPixel > 8 Then
Dim grayscaleCommand As New GrayscaleCommand(8)
grayscaleCommand.Run(pageImage)
End If
Dim binarizeCommand As New AutoBinarizeCommand()
binarizeCommand.Run(pageImage)
Dim page = document.Pages.AddPage(pageImage, Nothing)
page.Recognize(Nothing)
text.AppendLine(page.GetText(-1))
End Using
Next
End Using
Return text.ToString()
End FunctionImports Leadtools.ImageProcessing
Imports System.Text
Public Function ProcessLowQualityPdf(pdfPath As String) As String
Dim text As New StringBuilder()
Dim pdfInfo = _codecs.GetInformation(pdfPath, True)
Using document = _engine.DocumentManager.CreateDocument()
For i As Integer = 1 To pdfInfo.TotalPages
Using pageImage = _codecs.Load(pdfPath, 0, CodecsLoadByteOrder.BgrOrGray, i, i)
' Each command is a separate instantiation and Run() call
Dim deskewCommand As New DeskewCommand()
deskewCommand.Run(pageImage)
Dim despeckleCommand As New DespeckleCommand()
despeckleCommand.Run(pageImage)
If pageImage.BitsPerPixel > 8 Then
Dim grayscaleCommand As New GrayscaleCommand(8)
grayscaleCommand.Run(pageImage)
End If
Dim binarizeCommand As New AutoBinarizeCommand()
binarizeCommand.Run(pageImage)
Dim page = document.Pages.AddPage(pageImage, Nothing)
page.Recognize(Nothing)
text.AppendLine(page.GetText(-1))
End Using
Next
End Using
Return text.ToString()
End Function各前処理ステップには、コマンドクラスをインスタンス化し、画像に対して.Run()を呼び出す必要があります。 開発者は、各変換の順序と適用範囲を管理します。 画像がすでにバイナリである場合、AutoBinarizeCommandは品質を損なう可能性があります。 条件付きBitsPerPixelチェックは開発者の責任です。
IronOCRのアプローチ
IronOCR前処理はOcrInputオブジェクトに適用され、読み込まれたすべてのページに同時に影響を与えます:
using IronOcr;
public string ProcessLowQualityPdf(string pdfPath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf(pdfPath);
// Applied to all pages
input.Deskew();
input.DeNoise();
input.Binarize();
input.Contrast();
input.EnhanceResolution(300);
var result = ocr.Read(input);
Console.WriteLine($"Confidence: {result.Confidence}%");
return result.Text;
}using IronOcr;
public string ProcessLowQualityPdf(string pdfPath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf(pdfPath);
// Applied to all pages
input.Deskew();
input.DeNoise();
input.Binarize();
input.Contrast();
input.EnhanceResolution(300);
var result = ocr.Read(input);
Console.WriteLine($"Confidence: {result.Confidence}%");
return result.Text;
}Imports IronOcr
Public Function ProcessLowQualityPdf(pdfPath As String) As String
Dim ocr = New IronTesseract()
Using input As New OcrInput()
input.LoadPdf(pdfPath)
' Applied to all pages
input.Deskew()
input.DeNoise()
input.Binarize()
input.Contrast()
input.EnhanceResolution(300)
Dim result = ocr.Read(input)
Console.WriteLine($"Confidence: {result.Confidence}%")
Return result.Text
End Using
End FunctionImports IronOcr
Public Function ProcessLowQualityPdf(pdfPath As String) As String
Dim ocr = New IronTesseract()
Using input As New OcrInput()
input.LoadPdf(pdfPath)
' Applied to all pages
input.Deskew()
input.DeNoise()
input.Binarize()
input.Contrast()
input.EnhanceResolution(300)
Dim result = ocr.Read(input)
Console.WriteLine($"Confidence: {result.Confidence}%")
Return result.Text
End Using
End Function5つの前処理ステップが、すべてのページに一様に適用され、ページごとのループは禁止。IronOCRの自動前処理はデフォルトで毎回Read()呼び出しに実行され、明示的なフィルター呼び出しなしで一般的なスキャンの問題を処理します。 画像フィルターのチュートリアルでは、フィルターカタログ全体を網羅しています。 低品質スキャンの例では、処理が難しい文書に対する前処理前と前処理後の違いを示しています。
APIマッピングリファレンス
| LEADTOOLS API | IronOCR相当値 |
|---|---|
RasterSupport.SetLicense(licPath, key) | IronOcr.License.LicenseKey = "key" |
RasterCodecs | OcrInput |
_codecs.Load(path) | input.LoadPdf(path) |
_codecs.GetInformation(path, true).TotalPages | 自動 - ページ数カウントは不要 |
OcrEngineManager.CreateEngine(OcrEngineType.LEAD) | new IronTesseract() |
engine.Startup(_codecs, null, null, runtimePath) | 不要 |
engine.Shutdown() | 不要 |
engine.DocumentManager.CreateDocument() | 不要 |
document.Pages.AddPage(image, null) | input.LoadImage(path) |
page.Recognize(null) | ocr.Read(input) (認識は読み取りの一部です) |
page.GetText(-1) | result.Text |
page.RecognizeStatus | result.Confidence |
Bounds = new LeadRect(x, y, w, h)がある | input.LoadImage()に渡した |
OcrZoneType.Text | デフォルト - 型指定は不要 |
page.Zones.Add(zone) | input.LoadImage(path, cropRect) |
DeskewCommand().Run(image) | input.Deskew() |
DespeckleCommand().Run(image) | input.DeNoise() |
AutoBinarizeCommand().Run(image) | input.Binarize() |
PdfDocumentOptions { ImageOverText = true } | result.SaveAsSearchablePdf()により処理される |
_engine.DocumentWriterInstance.SetOptions(...) | 不要 |
document.Save(path, DocumentFormat.Pdf, null) | result.SaveAsSearchablePdf(path) |
CodecsLoadByteOrder.BgrOrGray | 不要 - 自動的に処理されます |
_codecs.Options.Pdf.Load.Password = password | input.LoadPdf(path, Password: password) |
チームがLEADTOOLS OCRからIronOCRへの移行を検討する際
イメージングツールキットを必要としないOCR専用プロジェクト
LEADTOOLSは、ドキュメントビューア、医用画像処理、フォーム認識、OCRなど、複数のモジュールが統合プラットフォーム上で連携して動作する必要がある場合に、財務面とアーキテクチャ面の両方で理にかなったソリューションとなります。 画像やPDFからテキストを抽出することが要件である場合、計算方法が変わります。 LEADTOOLSのライセンスコストは見積もりのために営業の関与が必要です - 現在の価格はLEADTOOLSにお問い合わせください。 ライセンス費用に加えて年次メンテナンスが必要です。Professionalティア用のIronOCR$2,999は、10人の開発者をカバーし、継続使用のための更新要件がありません。 LEADTOOLSの更新期間が終了し、コストを再評価しているチームは、バンドルに含まれる機能の中でOCRモジュールしか使用していなかったことに気づくことが多い。
コンテナ化およびサーバーレスデプロイメント
LEADTOOLSのファイルベースのライセンスは、現代の導入アーキテクチャにおいて大きな摩擦点となっている。 Dockerコンテナには、2つの物理ライセンスファイルが必要です。これらのファイルはイメージに組み込まれてレイヤー履歴に表示されるか、オーケストレーション環境ごとにボリューム調整を使用して実行時にマウントされます。 Azure FunctionsとAWS Lambdaには、回避策を用いない限り、ライセンスファイルをデプロイするための明確な仕組みがありません。 LEADTOOLSは、ランタイムファイルをメモリにロードするブロッキングエンジン起動も実行するため、コールドスタート時間に500~2000ミリ秒が追加されます。これは、レイテンシがユーザーエクスペリエンスに直接影響するサーバーレス関数にとって重要な点です。 IronOCRは標準のNuGet参照としてデプロイされ、環境変数からライセンスキーを設定し、初回使用時に遅延初期化を行います。 DockerのデプロイガイドとAWSのデプロイガイドでは、一般的なコンテナ化シナリオに関する具体的な手順を説明しています。
間違ったバンドルの問題
LEADTOOLSのモジュール構造は、 IronOCRにはない問題、つまり間違ったプランを購入してしまうという問題を生み出します。 OCR機能は、すべてのLEADTOOLSバンドルに含まれているわけではありません。 パスワードで保護されたPDFをサポートするには、別途PDFモジュールが必要となり、これはすべてのOCR特化型ライセンスに含まれているわけではありません。 より高精度なエンジンオプションを利用するには、LEADTOOLSの購入とは別に、ベンダーとの契約が必要となります。 必要な機能が含まれていると思い込んで購入したチームが、本番環境で実行時に初めてそうではないことに気づいた場合、その機能を有効にする前に営業担当者との再交渉を強いられることになる。 IronOCRのライセンスは、各ティアのすべての機能をカバーしています。 独立したPDFモジュールはなく、パスワードで保護されたドキュメント用のアドオンもなく、より高精度なエンジンを提供する別のベンダーもありません。
保守およびリソース管理の複雑性
LEADTOOLSのエンジンライフサイクルは、単なるセットアップの手順ではなく、継続的なメンテナンスのための基盤を構築するものです。 エンジンは、正しい順序で始動、使用、停止、そして廃棄されなければならない。 その手順から逸脱するとエラーが発生します。 LEADTOOLS のバッチ処理実装におけるメモリリークは、通常、破棄されなかった中間イメージオブジェクト、処理後に開いたままになっているドキュメントコンテナ、または適切なクリーンアップなしに複数回作成されたエンジンインスタンスに起因します。本番環境のバッチプロセッサでは、補償メカニズムとしてドキュメントチャンク間に強制的なガベージコレクション呼び出しが含まれることがよくあります。これは、基盤となるオブジェクトモデルがランタイムと競合していることを示すパターンです。午前 2 時にドキュメント処理サービスのメモリ増加をデバッグしたチームは、LEADTOOLS の破棄パターンに根本原因を見出すことがよくあります。 IronOCRは標準 for .NET破棄スコープを使用します。明示的な破棄が必要なのは入力コンテナのみです。 エンジン自体はステートレスであり、スレッドセーフです。
複数環境におけるデプロイメントの一貫性
開発環境、ステージング環境、本番環境のすべてにおいて、LEADTOOLSのライセンスファイルは一貫したパスに配置する必要があり、Plusアプリケーションが起動時に想定する正確なパスにランタイムディレクトリを配置する必要があります。ステージングサーバーのランタイムディレクトリパスが本番環境とドライブレター1つ分異なるなど、環境間で不一致が生じると、その環境固有のエラーが発生し、修正するにはコードまたは構成の変更が必要になります。 IronOCRのライセンスキーと単一のNuGetパッケージは、すべての環境で同じように動作します。 ライセンスキーは環境固有の値としては唯一のものであり、 .NETチームがデータベース接続文字列やAPIキーに使用しているものと同じシークレット管理パターンに従います。
一般的な移行の考慮事項
エンジンライフサイクル除去
LEADTOOLSコードはライフサイクルを管理する必要があるため、IDisposableを実装するサービスクラスにエンジンをラップします。 この移行により、その要件は完全に解消されます。
// LEADTOOLS: Service class required for lifecycle management
public class LeadtoolsService : IDisposable
{
private IOcrEngine _engine;
private RasterCodecs _codecs;
public LeadtoolsService()
{
RasterSupport.SetLicense(licPath, key);
_codecs = new RasterCodecs();
_engine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD);
_engine.Startup(_codecs, null, null, runtimePath);
}
public string Process(string path)
{
using var image = _codecs.Load(path);
using var doc = _engine.DocumentManager.CreateDocument();
var page = doc.Pages.AddPage(image, null);
page.Recognize(null);
return page.GetText(-1);
}
public void Dispose()
{
_engine?.Shutdown();
_engine?.Dispose();
_codecs?.Dispose();
}
}
// IronOCR: なし lifecycle to manage
public class OcrService
{
private readonly IronTesseract _ocr = new IronTesseract();
public string Process(string path) => _ocr.Read(path).Text;
}// LEADTOOLS: Service class required for lifecycle management
public class LeadtoolsService : IDisposable
{
private IOcrEngine _engine;
private RasterCodecs _codecs;
public LeadtoolsService()
{
RasterSupport.SetLicense(licPath, key);
_codecs = new RasterCodecs();
_engine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD);
_engine.Startup(_codecs, null, null, runtimePath);
}
public string Process(string path)
{
using var image = _codecs.Load(path);
using var doc = _engine.DocumentManager.CreateDocument();
var page = doc.Pages.AddPage(image, null);
page.Recognize(null);
return page.GetText(-1);
}
public void Dispose()
{
_engine?.Shutdown();
_engine?.Dispose();
_codecs?.Dispose();
}
}
// IronOCR: なし lifecycle to manage
public class OcrService
{
private readonly IronTesseract _ocr = new IronTesseract();
public string Process(string path) => _ocr.Read(path).Text;
}' LEADTOOLS: Service class required for lifecycle management
Public Class LeadtoolsService
Implements IDisposable
Private _engine As IOcrEngine
Private _codecs As RasterCodecs
Public Sub New()
RasterSupport.SetLicense(licPath, key)
_codecs = New RasterCodecs()
_engine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD)
_engine.Startup(_codecs, Nothing, Nothing, runtimePath)
End Sub
Public Function Process(path As String) As String
Using image = _codecs.Load(path)
Using doc = _engine.DocumentManager.CreateDocument()
Dim page = doc.Pages.AddPage(image, Nothing)
page.Recognize(Nothing)
Return page.GetText(-1)
End Using
End Using
End Function
Public Sub Dispose() Implements IDisposable.Dispose
If _engine IsNot Nothing Then
_engine.Shutdown()
_engine.Dispose()
End If
If _codecs IsNot Nothing Then
_codecs.Dispose()
End If
End Sub
End Class
' IronOCR: なし lifecycle to manage
Public Class OcrService
Private ReadOnly _ocr As New IronTesseract()
Public Function Process(path As String) As String
Return _ocr.Read(path).Text
End Function
End Class' LEADTOOLS: Service class required for lifecycle management
Public Class LeadtoolsService
Implements IDisposable
Private _engine As IOcrEngine
Private _codecs As RasterCodecs
Public Sub New()
RasterSupport.SetLicense(licPath, key)
_codecs = New RasterCodecs()
_engine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD)
_engine.Startup(_codecs, Nothing, Nothing, runtimePath)
End Sub
Public Function Process(path As String) As String
Using image = _codecs.Load(path)
Using doc = _engine.DocumentManager.CreateDocument()
Dim page = doc.Pages.AddPage(image, Nothing)
page.Recognize(Nothing)
Return page.GetText(-1)
End Using
End Using
End Function
Public Sub Dispose() Implements IDisposable.Dispose
If _engine IsNot Nothing Then
_engine.Shutdown()
_engine.Dispose()
End If
If _codecs IsNot Nothing Then
_codecs.Dispose()
End If
End Sub
End Class
' IronOCR: なし lifecycle to manage
Public Class OcrService
Private ReadOnly _ocr As New IronTesseract()
Public Function Process(path As String) As String
Return _ocr.Read(path).Text
End Function
End ClassIronTesseractインスタンスはスレッドセーフであり、シングルトンとして共有できます。 エンジンの利益のためにサービスクラスにIDisposableを実装する必要はありません。
ゾーンベースのOCR移行
LEADTOOLSゾーン構成はLeadRect境界と共に使用し、カスタムなものを追加する前に自動で検出されたゾーンをクリアする必要があります。 IronOCRはLoadImage()に直接渡して使用します:
// LEADTOOLS zone setup
var zone = new OcrZone
{
Bounds = new LeadRect(x, y, width, height),
ZoneType = OcrZoneType.Text,
CharacterFilters = OcrZoneCharacterFilters.None,
RecognitionModule = OcrZoneRecognitionModule.Auto
};
page.Zones.Clear(); // Must clear auto-detected zones first
page.Zones.Add(zone);
// IronOCR equivalent
using var input = new OcrInput();
input.LoadImage(imagePath, new CropRectangle(x, y, width, height));
var text = new IronTesseract().Read(input).Text;// LEADTOOLS zone setup
var zone = new OcrZone
{
Bounds = new LeadRect(x, y, width, height),
ZoneType = OcrZoneType.Text,
CharacterFilters = OcrZoneCharacterFilters.None,
RecognitionModule = OcrZoneRecognitionModule.Auto
};
page.Zones.Clear(); // Must clear auto-detected zones first
page.Zones.Add(zone);
// IronOCR equivalent
using var input = new OcrInput();
input.LoadImage(imagePath, new CropRectangle(x, y, width, height));
var text = new IronTesseract().Read(input).Text;Imports Leadtools
Imports IronOcr
' LEADTOOLS zone setup
Dim zone As New OcrZone With {
.Bounds = New LeadRect(x, y, width, height),
.ZoneType = OcrZoneType.Text,
.CharacterFilters = OcrZoneCharacterFilters.None,
.RecognitionModule = OcrZoneRecognitionModule.Auto
}
page.Zones.Clear() ' Must clear auto-detected zones first
page.Zones.Add(zone)
' IronOCR equivalent
Using input As New OcrInput()
input.LoadImage(imagePath, New CropRectangle(x, y, width, height))
Dim text As String = New IronTesseract().Read(input).Text
End UsingImports Leadtools
Imports IronOcr
' LEADTOOLS zone setup
Dim zone As New OcrZone With {
.Bounds = New LeadRect(x, y, width, height),
.ZoneType = OcrZoneType.Text,
.CharacterFilters = OcrZoneCharacterFilters.None,
.RecognitionModule = OcrZoneRecognitionModule.Auto
}
page.Zones.Clear() ' Must clear auto-detected zones first
page.Zones.Add(zone)
' IronOCR equivalent
Using input As New OcrInput()
input.LoadImage(imagePath, New CropRectangle(x, y, width, height))
Dim text As String = New IronTesseract().Read(input).Text
End Using地域ベースのOCRガイドでは、単一地域および複数地域の抽出パターンについて解説しています。 地域作物の例では、請求書ヘッダーの抽出を実用的なユースケースとして示しています。
NuGetパッケージのクリーンアップ
LEADTOOLSには複数のパッケージが必要です。 この移行では、それら全てを削除し、一つ追加します。
# Remove LEADTOOLS packages
dotnet remove package Leadtools
dotnet remove package Leadtools.Ocr
dotnet remove package Leadtools.Codecs
dotnet remove package Leadtools.Pdf
# Add IronOCR
dotnet add package IronOcr
設置面積の大幅な削減が実現する。 ビルド出力から消えた:OcrRuntime/ディレクトリ、複数のLEADTOOLS DLL、Tesseractエンジンオプションからのどのtessdata/フォルダ。 残るのは、単一のパッケージ参照のみです。 IronOCR NuGetパッケージには、すべてのネイティブ依存関係が含まれています。
信頼度スコアへのアクセス
LEADTOOLSは認識品質をOcrPageRecognizeStatus.Doneは完了を示しますが、精度レベルを示しません)。 IronOCRはresult.Confidenceを通じて直接パーセンテージを提供します:
var result = new IronTesseract().Read("document.jpg");
Console.WriteLine($"Confidence: {result.Confidence}%");
// Branch on quality threshold
if (result.Confidence < 60)
{
// Apply additional preprocessing and retry
using var input = new OcrInput();
input.LoadImage("document.jpg");
input.Deskew();
input.DeNoise();
input.EnhanceResolution(300);
result = new IronTesseract().Read(input);
}var result = new IronTesseract().Read("document.jpg");
Console.WriteLine($"Confidence: {result.Confidence}%");
// Branch on quality threshold
if (result.Confidence < 60)
{
// Apply additional preprocessing and retry
using var input = new OcrInput();
input.LoadImage("document.jpg");
input.Deskew();
input.DeNoise();
input.EnhanceResolution(300);
result = new IronTesseract().Read(input);
}Imports System
Imports IronOcr
Dim result = New IronTesseract().Read("document.jpg")
Console.WriteLine($"Confidence: {result.Confidence}%")
' Branch on quality threshold
If result.Confidence < 60 Then
' Apply additional preprocessing and retry
Using input As New OcrInput()
input.LoadImage("document.jpg")
input.Deskew()
input.DeNoise()
input.EnhanceResolution(300)
result = New IronTesseract().Read(input)
End Using
End IfImports System
Imports IronOcr
Dim result = New IronTesseract().Read("document.jpg")
Console.WriteLine($"Confidence: {result.Confidence}%")
' Branch on quality threshold
If result.Confidence < 60 Then
' Apply additional preprocessing and retry
Using input As New OcrInput()
input.LoadImage("document.jpg")
input.Deskew()
input.DeNoise()
input.EnhanceResolution(300)
result = New IronTesseract().Read(input)
End Using
End If信頼度スコアガイドでは、閾値の選択と品質に基づいた再試行パターンについて解説しています。
IronOCRの追加機能
上記で挙げた比較点以外にも、 IronOCRにはLEADTOOLS OCRにはない機能、あるいは追加モジュールの購入が必要な機能が含まれています。
- OCR中のバーコード読み取り:
Read()でテキストとともに抽出されます。 LEADTOOLSバーコードモジュールのライセンスは別途必要ありません。 バーコード読み取りガイドとバーコードOCRの例を参照してください。 - 構造化データ抽出:
result.Charactersは、完全なドキュメント階層を、バウンディングボックスと要素ごとの信頼スコアと共に公開します。 読み取り結果ガイドには、出力モデル全体が記載されています。 - hOCRエクスポート:
result.SaveAsHocrFile()は、位置データを埋め込んだHTMLを作成して、ダウンストリームレイアウト分析に役立ちます。 hOCRエクスポートガイドを参照してください。 - 125以上の言語をサポート:各言語はNuGetパッケージとして提供されます。 tessdataディレクトリがなく、ファイルパスの設定もありません。 言語索引全体と多言語ガイドをご覧ください。
- 非同期OCR: ASP.NETおよびバックグラウンドサービスで非ブロッキングのドキュメント処理用に
await ocr.ReadAsync(input)。 非同期OCRガイドを参照してください。 -特殊な文書認識機能:パスポート、ナンバープレート、MICR小切手、手書き文字の認識を内蔵サポート。 特殊機能のページをご覧ください。 - 進捗追跡:
ocr.Configuration.ProgressCallbackは、長時間実行されるバッチジョブのページごとの進捗を報告します。 進捗状況追跡ガイドを参照してください。
.NETの互換性と将来の準備
IronOCRは、.NET 6、 .NET 7、 .NET 8、 .NET 9、および.NET Standard 2.0を対象としています。最後 for .NET Standard 2.0は、 .NET Framework 4.6.2以降を対象としています。 クロスプラットフォーム対応で、Windows(x86およびx64)、Linux x64、macOS、Azure App Service、AWS Lambda、Dockerをサポートしており、すべてのネイティブ依存関係はNuGetパッケージにバンドルされています。 プラットフォーム固有の設定は不要です。 このパッケージは実行時に適切なネイティブバイナリを選択します。LEADTOOLSはクロスプラットフォーム for .NET展開をサポートしていますが、各ターゲット環境ごとにプラットフォーム固有のランタイムファイルと対応するパス構成が必要です。 Windows本番サーバーと並行してLinuxコンテナやmacOS開発マシンを対象とするチームにとって、IronOCRの単一パッケージ展開はプラットフォームごとの設定レイヤーを排除します。
結論
LEADTOOLS OCRは、35年にわたる開発実績を持つ、包括的な画像処理プラットフォームに組み込まれた成熟した技術です。 文書ビューア、医用画像処理、フォーム認識などに既にLEADTOOLSを標準導入している組織にとって、既存の投資にOCR機能を追加することは妥当な判断と言えるでしょう。統合にかかる費用は既に支払済みであり、統一されたAPIインターフェースには価値があるからです。
画像やPDFからテキストを抽出することが要件となるチームの場合、計算方法は異なります。 LEADTOOLSの4段階の初期化シーケンス、ファイルベースのライセンス展開、独立したコーデックレイヤー、および明示的なエンジンライフサイクルは、OCRの精度や機能を向上させるための複雑さではありません。 これらは、チームのすべての開発者が理解し、すべてのデプロイ環境が対応し、すべてのCI/CDパイプラインが担わなければならないインフラストラクチャのオーバーヘッドです。 OCRモジュールのみを購入しPDFモジュールが含まれていない、あるいはLEADエンジンのみを購入しOmniPageの精度レベルが含まれていないなど、誤ったバンドルを購入すると、購入時ではなく運用段階で初めて問題が顕在化する、目に見えない不具合が発生します。
IronOCRは、単一のNuGetパッケージ、文字列ベースのライセンス、および1行の認識パスにより、オーバーヘッドを排除しつつ、本番環境のOCRにとって重要な機能(自動前処理、ネイティブPDFサポート、パスワード保護されたドキュメント処理、構造化出力抽出、125以上の言語サポート、スレッドセーフな並列処理)を犠牲にすることなく実現しています。 価格差 - $2,999 永続的 vs. LEADTOOLSの多年ライセンスとメンテナンス料金 - は顕著です。 それは、一度限りのエンジニアリングツールの購入と、年間予算の正当化が必要となる継続的なサブスクリプションとの違いである。
冒頭で指摘した問題点――各本番環境マシンにライセンスファイルが存在すること、バンドルの混乱、文字を読み込む前に初期化処理が必要となること――は、実際の導入および運用コストを反映しています。両方のオプションを検討しているチームは、導入を決定する前に、LEADTOOLSの初期化シーケンスを自社のコンテナ戦略、CI/CDパイプライン、およびシークレット管理手法に照らし合わせて実行する必要があります。 答えはしばしば選択肢を明確にする。
関連する記事


