ABBYY FineReader から IronOCR への移行
このガイドでは、 .NET開発者がABBYY FineReader Engine SDKをIronOCRに置き換えるためのすべての手順を説明します。 本書では、COM依存関係とSDKインストーラーの成果物を削除する具体的な手順、ABBYYのAPIとIronOCRの同等のAPIのマッピング、およびABBYY製品との統合で最もよく見られるパターンに対する変更前後のコード例について解説します。 移行対象は、ABBYYのエンタープライズコストと展開の複雑さがプロジェクトの要件に適合しなくなったと判断したチームです。
ABBYY FineReaderから移行する理由
ABBYY FineReader Engineは高性能なOCRプラットフォームですが、そのアーキテクチャは、専任のインフラチームを持つEnterpriseWindows環境向けに設計されています。 .NETチームの実際の業務が請求書処理、契約書のデジタル化、スキャンされたフォームの抽出などである場合、そのアーキテクチャは資産ではなく負債となる。
COM相互運用性の負債は時間とともに増大する。.NET.NETABBYYのすべての統合は、COM相互運用性レイヤーを経由して実行される。 COM オブジェクトは明示的なライフサイクル管理を必要とします: 作成、初期化、処理、そして finally ブロック内でのクローズまたはプロセスがメモリをリークします。 ABBYYに触れるすべてのコードパスは、このパターンを踏襲している。 2、3年にわたる機能追加の中で、そのライフサイクル処理はサービス クラス、バックグラウンド ワーカー、リクエスト ハンドラーへと伝播していく。 結果は、OCR に関連する各クラスの 30-50% がボイラープレートになり、IronTesseract に切り替えると完全に消えます。
SDKインストーラーが最新の展開パターンを阻害する。ABBYYは、バイナリ、言語データ、ランタイムファイル、ライセンスファイルをハードコードされたパスに配置するWindows SDKインストーラーを介して展開する。 ABBYY を使用するサービスをコンテナ化するには、インストーラー出力から 300MB 以上のカスタムベースイメージを作成するか、ライセンスファイルをマウントして起動する必要があります。どちらのアプローチも標準的な Kubernetes やクラウドネイティブパイプラインに適合しません。IronOCR は NuGet パッケージです: 他のすべての依存関係をプルする同じ dotnet restore が完全な OCR エンジンをプルします。
ページ単位のライセンスでは、処理量がコストセンターに変わります。ABBYYのボリュームベースのライセンスモデルでは、規定のしきい値を超えたページ数に応じて課金されます。 サービス開始当初に月間5万件の文書を処理し、2年後には50万件に達したアプリケーションの場合、OCRにかかるコストは成功に正比例して増加する。 IronOCRはライセンス料を定額制にしており、月に200万ページを処理するチームも、2000ページを処理するチームも、全く同じライセンス料を支払うことになる。
言語データには手動での展開調整が必要です。ABBYY言語パックはSDKランタイムディレクトリ内のファイルとして存在します。 言語を追加するということは、適切なデータファイルを特定し、それらを各デプロイメントターゲットの適切なパスにコピーし、CI/CDスクリプトを更新してそれらを含めることを意味します。 IronOCR では、フランス語の追加は dotnet add package IronOcr.Languages.French です — パッケージマネージャーが残りを処理します。
ライセンスファイルの失敗が事前の警告なしに本番環境に影響を与えます。 ABBYY のライセンスは .lic および .key ファイルとして存在し、loader.GetEngineObject() が実行される際に特定のディスクパスに存在しなければなりません。 新しい本番サーバーでこれらのファイルが見つからない場合(デプロイスクリプトの誤り、ファイルコピーの失敗、権限の問題など)、起動時に例外が発生します。ライセンスの有効期限切れも同様に失敗します。 IronOCR の ライセンス は、開始コードで割り当てられる文字列キーであり、秘密管理に格納可能で、IronOcr.License.IsValidLicense による検証がアプリケーションがトラフィックを受け入れる前に行われます。
スレッドセーフには単一の共有エンジンインスタンスが必要です。 ABBYY のエンジンは、複数のスレッドから CreateFRDocument を並行して呼び出すために安易にスレッドセーフではありません。 本番環境では、ロック戦略またはプロセッサプールが使用される。 IronOCR の IronTesseract はステートレスです: スレッドごとに 1 インスタンスを起動し、ロックなしで並行して認識を実行し、完了したら破棄します。
基本的な問題
// ABBYY: COM loader + license path validation + profile load — before a single pixel of OCR runs
var loader = new EngineLoader();
var engine = loader.GetEngineObject(
@"C:\Program Files\ABBYY SDK\FineReader Engine\Bin", // Breaks on every new machine
@"C:\Program Files\ABBYY SDK\License" // Fails if .lic file is missing
);
engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
var langParams = engine.CreateLanguageParams();
langParams.Languages.Add("English");
// ABBYY: COM loader + license path validation + profile load — before a single pixel of OCR runs
var loader = new EngineLoader();
var engine = loader.GetEngineObject(
@"C:\Program Files\ABBYY SDK\FineReader Engine\Bin", // Breaks on every new machine
@"C:\Program Files\ABBYY SDK\License" // Fails if .lic file is missing
);
engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
var langParams = engine.CreateLanguageParams();
langParams.Languages.Add("English");
' ABBYY: COM loader + license path validation + profile load — before a single pixel of OCR runs
Dim loader As New EngineLoader()
Dim engine = loader.GetEngineObject(
"C:\Program Files\ABBYY SDK\FineReader Engine\Bin", ' Breaks on every new machine
"C:\Program Files\ABBYY SDK\License" ' Fails if .lic file is missing
)
engine.LoadPredefinedProfile("DocumentConversion_Accuracy")
Dim langParams = engine.CreateLanguageParams()
langParams.Languages.Add("English")
// IronOCR: the entire initialization
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var ocr = new IronTesseract();
// IronOCR: the entire initialization
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var ocr = new IronTesseract();
Imports IronOcr
' IronOCR: the entire initialization
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim ocr As New IronTesseract()
IronOCRとABBYY FineReader:機能比較
以下の表は、この移行を評価するチームにとって関連性の高い機能についてまとめたものです。
| フィーチャー | ABBYY FineReaderエンジン | IronOCR |
|---|---|---|
| インストール。 | SDKインストーラー(Windows版) | dotnet add package IronOcr |
| 取得 | 営業担当者にお問い合わせください(4~12週間) | セルフサービスNuGet |
| ライセンスモデル | Enterprise、サーバー単位、またはページ単位 | 永続的、$999- $2,999 一回限り |
| ライセンス管理 | .lic + ディスク上の .key ファイル |
コードまたは環境変数内の文字列キー |
| .NETインテグレーション | COM 相互運用 | .NET ネイティブ |
| COM依存関係 | はい | なし |
| スレッドセーフティ | ロック戦略が必要 | フル (スレッドごとに IronTesseract 1 つ) |
| 対応言語 | 190歳以上 | 125+ |
| 言語のインストール | SDK パスにあるランタイムデータファイル | NuGet言語パッケージ |
| PDF入力 | はい (via CreatePDFFile) |
はい (ネイティブ、input.LoadPdf()) |
| 検索可能なPDF出力 | はい(輸出パイプライン) | はい (result.SaveAsSearchablePdf()) |
| 自動前処理 | プロフィールに基づく | 内蔵機能(傾き補正、ノイズ除去、コントラスト補正、二値化、シャープ化) |
| 地域ベースのOCR | ゾーンオブジェクト (CreateZone, SetBounds) |
CropRectangle パラメータ |
| バーコード読み取り | はい | はい (ocr.Configuration.ReadBarCodes = true) |
| クロスプラットフォーム。 | Windows、Linux、macOS | Windows、Linux、macOS、Docker、Azure、AWS |
| Dockerデプロイメント | カスタムベース画像が必要です | スタンダード .NET ベースイメージ + libgdiplus |
| 信頼度スコアリング | はい | はい (result.Confidence) |
| 最初のOCR結果が表示されるまでの時間 | 4~12週間(調達期間) | 当日 |
クイックスタート:ABBYY FineReaderからIronOCRへの移行
ステップ 1: NuGet パッケージを置き換える
ABBYY FineReader EngineにはNuGetパッケージがありません。 SDKをアンインストールし、プロジェクトファイルから手動アセンブリ参照を削除することで、これを削除できます。
<Reference Include="FREngine">
<HintPath>C:\Program Files\ABBYY SDK\FineReader Engine\Bin\FREngine.dll</HintPath>
</Reference>
<Reference Include="FREngine">
<HintPath>C:\Program Files\ABBYY SDK\FineReader Engine\Bin\FREngine.dll</HintPath>
</Reference>
その後、Visual Studio の参照ノードから FREngine.dll COM インターロップ参照を削除するか、プロジェクトファイルから対応するエントリを直接削除してください。 NuGetからIronOCRをインストールしてください。
dotnet add package IronOcr
ステップ 2: 名前空間の更新
// Before (ABBYY)
using FREngine;
using ABBYY.FineReader;
// After (IronOCR)
using IronOcr;
// Before (ABBYY)
using FREngine;
using ABBYY.FineReader;
// After (IronOCR)
using IronOcr;
Imports IronOcr
ステップ 3: ライセンスの初期化
アプリケーション起動時に、OCR呼び出しの前に一度だけ以下を追加してください。
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
本番環境へのデプロイ時には、キーを環境変数またはシークレットマネージャに保存してください。
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY");
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY");
Imports System
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
コード移行の例
WindowsサービスにおけるエンジンライフサイクルとステートレスIronTesseractとの比較
ABBYY のエンジン初期化はサービ粘土で包む必要があります。なぜなら EngineLoader および IEngine オブジェクトは作成するのに高価だからです。 ほとんどの運用環境では、エンジンを明示的な起動メソッドと終了メソッドを持つシングルトンサービスとしてラップしています。
ABBYY FineReaderのアプローチ:
using FREngine;
public class DocumentOcrService : IHostedService, IDisposable
{
private IEngine _engine;
public Task StartAsync(CancellationToken cancellationToken)
{
// Step 1: Create loader — requires COM 相互運用 registration
var loader = new EngineLoader();
// Step 2: Load engine from SDK path — throws if license files are missing
_engine = loader.GetEngineObject(
@"C:\Program Files\ABBYY SDK\FineReader Engine\Bin",
@"C:\Program Files\ABBYY SDK\License"
);
// Step 3: Load profile before any recognition work
_engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
return Task.CompletedTask;
}
public string ProcessDocument(string imagePath)
{
// Document must be created and destroyed per call
var document = _engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
return document.PlainText.Text;
}
finally
{
document.Close(); // Memory leaks if omitted
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_engine = null; // COM cleanup
return Task.CompletedTask;
}
public void Dispose() => _engine = null;
}
using FREngine;
public class DocumentOcrService : IHostedService, IDisposable
{
private IEngine _engine;
public Task StartAsync(CancellationToken cancellationToken)
{
// Step 1: Create loader — requires COM 相互運用 registration
var loader = new EngineLoader();
// Step 2: Load engine from SDK path — throws if license files are missing
_engine = loader.GetEngineObject(
@"C:\Program Files\ABBYY SDK\FineReader Engine\Bin",
@"C:\Program Files\ABBYY SDK\License"
);
// Step 3: Load profile before any recognition work
_engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
return Task.CompletedTask;
}
public string ProcessDocument(string imagePath)
{
// Document must be created and destroyed per call
var document = _engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
return document.PlainText.Text;
}
finally
{
document.Close(); // Memory leaks if omitted
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_engine = null; // COM cleanup
return Task.CompletedTask;
}
public void Dispose() => _engine = null;
}
Imports FREngine
Imports System.Threading
Imports System.Threading.Tasks
Public Class DocumentOcrService
Implements IHostedService, IDisposable
Private _engine As IEngine
Public Function StartAsync(cancellationToken As CancellationToken) As Task Implements IHostedService.StartAsync
' Step 1: Create loader — requires COM 相互運用 registration
Dim loader As New EngineLoader()
' Step 2: Load engine from SDK path — throws if license files are missing
_engine = loader.GetEngineObject(
"C:\Program Files\ABBYY SDK\FineReader Engine\Bin",
"C:\Program Files\ABBYY SDK\License"
)
' Step 3: Load profile before any recognition work
_engine.LoadPredefinedProfile("DocumentConversion_Accuracy")
Return Task.CompletedTask
End Function
Public Function ProcessDocument(imagePath As String) As String
' Document must be created and destroyed per call
Dim document = _engine.CreateFRDocument()
Try
document.AddImageFile(imagePath, Nothing, Nothing)
document.Process(Nothing)
Return document.PlainText.Text
Finally
document.Close() ' Memory leaks if omitted
End Try
End Function
Public Function StopAsync(cancellationToken As CancellationToken) As Task Implements IHostedService.StopAsync
_engine = Nothing ' COM cleanup
Return Task.CompletedTask
End Function
Public Sub Dispose() Implements IDisposable.Dispose
_engine = Nothing
End Sub
End Class
IronOCRのアプローチ:
using IronOcr;
public class DocumentOcrService
{
// なし startup, no shutdown, no COM lifecycle
// IronTesseract is stateless — create per call or reuse per thread
public string ProcessDocument(string imagePath)
{
return new IronTesseract().Read(imagePath).Text;
}
}
using IronOcr;
public class DocumentOcrService
{
// なし startup, no shutdown, no COM lifecycle
// IronTesseract is stateless — create per call or reuse per thread
public string ProcessDocument(string imagePath)
{
return new IronTesseract().Read(imagePath).Text;
}
}
Imports IronOcr
Public Class DocumentOcrService
' なし startup, no shutdown, no COM lifecycle
' IronTesseract is stateless — create per call or reuse per thread
Public Function ProcessDocument(imagePath As String) As String
Return New IronTesseract().Read(imagePath).Text
End Function
End Class
IronTesseract にはエンジンライフサイクルがありません。 初回使用時に内部的に初期化されるため、明示的なシャットダウンは不要です。 ホスティングされたサービスラッパー、IEngine フィールド、StopAsync メソッドはすべて消えます。 アプリケーションが文書を並行して処理する場合、各スレッドは自分自身の IronTesseract インスタンスを作成します—ロックは必要ありません。 IronTesseract セットアップガイド では TesseractVersion および Configuration プロパティを含む構成オプションをカバーしています。
言語認識設定
ABBYY の言語設定には LanguageParams オブジェクトの作成、インストールされたデータファイルと一致する言語名文字列の追加、そしてエンジンが文書を処理する前にそれらのパラメータを関連付けることが含まれます。 言語を追加するごとに、対応するデータファイルをランタイムパスに配置する必要があります。
ABBYY FineReaderのアプローチ:
using FREngine;
// Engine must already be initialized with sdkPath and licensePath
private void ConfigureLanguages(IEngine engine, string[] languageCodes)
{
// Create language parameters object
var langParams = engine.CreateLanguageParams();
// Add each language — string names must match installed data file names
// Missing data file causes runtime failure
foreach (var lang in languageCodes)
{
langParams.Languages.Add(lang); // e.g., "English", "French", "German"
}
// Language params are associated at the profile level, not per-document
// Changing languages requires reloading profile or reinitializing engine
engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
}
public string RecognizeFrenchDocument(IEngine engine, string imagePath)
{
var langParams = engine.CreateLanguageParams();
langParams.Languages.Add("French"); // Requires FrenchLanguage data files at runtime path
var document = engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
return document.PlainText.Text;
}
finally
{
document.Close();
}
}
using FREngine;
// Engine must already be initialized with sdkPath and licensePath
private void ConfigureLanguages(IEngine engine, string[] languageCodes)
{
// Create language parameters object
var langParams = engine.CreateLanguageParams();
// Add each language — string names must match installed data file names
// Missing data file causes runtime failure
foreach (var lang in languageCodes)
{
langParams.Languages.Add(lang); // e.g., "English", "French", "German"
}
// Language params are associated at the profile level, not per-document
// Changing languages requires reloading profile or reinitializing engine
engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
}
public string RecognizeFrenchDocument(IEngine engine, string imagePath)
{
var langParams = engine.CreateLanguageParams();
langParams.Languages.Add("French"); // Requires FrenchLanguage data files at runtime path
var document = engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
return document.PlainText.Text;
}
finally
{
document.Close();
}
}
Imports FREngine
' Engine must already be initialized with sdkPath and licensePath
Private Sub ConfigureLanguages(engine As IEngine, languageCodes As String())
' Create language parameters object
Dim langParams = engine.CreateLanguageParams()
' Add each language — string names must match installed data file names
' Missing data file causes runtime failure
For Each lang In languageCodes
langParams.Languages.Add(lang) ' e.g., "English", "French", "German"
Next
' Language params are associated at the profile level, not per-document
' Changing languages requires reloading profile or reinitializing engine
engine.LoadPredefinedProfile("DocumentConversion_Accuracy")
End Sub
Public Function RecognizeFrenchDocument(engine As IEngine, imagePath As String) As String
Dim langParams = engine.CreateLanguageParams()
langParams.Languages.Add("French") ' Requires FrenchLanguage data files at runtime path
Dim document = engine.CreateFRDocument()
Try
document.AddImageFile(imagePath, Nothing, Nothing)
document.Process(Nothing)
Return document.PlainText.Text
Finally
document.Close()
End Try
End Function
IronOCRのアプローチ:
using IronOcr;
// Single language — install IronOcr.Languages.French via NuGet first
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.French;
var result = ocr.Read("french-document.jpg");
Console.WriteLine(result.Text);
// Multiple simultaneous languages — operator overload, no data file management
var multiOcr = new IronTesseract();
multiOcr.Language = OcrLanguage.French + OcrLanguage.German + OcrLanguage.English;
var multiResult = multiOcr.Read("multilingual-contract.jpg");
Console.WriteLine(multiResult.Text);
using IronOcr;
// Single language — install IronOcr.Languages.French via NuGet first
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.French;
var result = ocr.Read("french-document.jpg");
Console.WriteLine(result.Text);
// Multiple simultaneous languages — operator overload, no data file management
var multiOcr = new IronTesseract();
multiOcr.Language = OcrLanguage.French + OcrLanguage.German + OcrLanguage.English;
var multiResult = multiOcr.Read("multilingual-contract.jpg");
Console.WriteLine(multiResult.Text);
Imports IronOcr
' Single language — install IronOcr.Languages.French via NuGet first
Dim ocr As New IronTesseract()
ocr.Language = OcrLanguage.French
Dim result = ocr.Read("french-document.jpg")
Console.WriteLine(result.Text)
' Multiple simultaneous languages — operator overload, no data file management
Dim multiOcr As New IronTesseract()
multiOcr.Language = OcrLanguage.French + OcrLanguage.German + OcrLanguage.English
Dim multiResult = multiOcr.Read("multilingual-contract.jpg")
Console.WriteLine(multiResult.Text)
言語パックは標準的な NuGet パッケージとしてインストールします (dotnet add package IronOcr.Languages.French)。 手動でデプロイするデータファイルはなく、パスの設定も不要で、言語を切り替える際にエンジンの再初期化も必要ありません。 多言語ガイドでは言語の組み合わせ方について解説しており、言語インデックスには利用可能な125種類以上のパックがすべて掲載されています。
マルチフレームTIFF処理
ABBYYは、フレームを反復処理し、各フレームを個別のドキュメントページとして追加することで、複数ページのTIFFファイルを処理します。 フレーム数はTIFFオブジェクトから取得し、その後、各フレームを個別にドキュメントコンテナに追加する必要があります。
ABBYY FineReaderのアプローチ:
using FREngine;
public string ProcessMultiFrameTiff(IEngine engine, string tiffPath)
{
var document = engine.CreateFRDocument();
try
{
// Must add each frame individually — no automatic multi-frame handling
// Page count requires reading the TIFF metadata before processing
var imageInfo = engine.CreateImageInfo();
imageInfo.LoadImageFile(tiffPath);
int frameCount = imageInfo.FrameCount;
for (int i = 0; i < frameCount; i++)
{
// Each frame added with its frame index via image processing params
var imgParams = engine.CreateImageProcessingParams();
imgParams.FrameIndex = i;
document.AddImageFile(tiffPath, imgParams, null);
}
document.Process(null);
return document.PlainText.Text;
}
finally
{
document.Close();
}
}
using FREngine;
public string ProcessMultiFrameTiff(IEngine engine, string tiffPath)
{
var document = engine.CreateFRDocument();
try
{
// Must add each frame individually — no automatic multi-frame handling
// Page count requires reading the TIFF metadata before processing
var imageInfo = engine.CreateImageInfo();
imageInfo.LoadImageFile(tiffPath);
int frameCount = imageInfo.FrameCount;
for (int i = 0; i < frameCount; i++)
{
// Each frame added with its frame index via image processing params
var imgParams = engine.CreateImageProcessingParams();
imgParams.FrameIndex = i;
document.AddImageFile(tiffPath, imgParams, null);
}
document.Process(null);
return document.PlainText.Text;
}
finally
{
document.Close();
}
}
Imports FREngine
Public Function ProcessMultiFrameTiff(engine As IEngine, tiffPath As String) As String
Dim document = engine.CreateFRDocument()
Try
' Must add each frame individually — no automatic multi-frame handling
' Page count requires reading the TIFF metadata before processing
Dim imageInfo = engine.CreateImageInfo()
imageInfo.LoadImageFile(tiffPath)
Dim frameCount As Integer = imageInfo.FrameCount
For i As Integer = 0 To frameCount - 1
' Each frame added with its frame index via image processing params
Dim imgParams = engine.CreateImageProcessingParams()
imgParams.FrameIndex = i
document.AddImageFile(tiffPath, imgParams, Nothing)
Next
document.Process(Nothing)
Return document.PlainText.Text
Finally
document.Close()
End Try
End Function
IronOCRのアプローチ:
using IronOcr;
// LoadImageFrames handles multi-frame TIFF automatically
using var input = new OcrInput();
input.LoadImageFrames("scanned-batch.tiff");
var ocr = new IronTesseract();
var result = ocr.Read(input);
// Per-page results accessible directly
foreach (var page in result.Pages)
{
Console.WriteLine($"Frame {page.PageNumber}: {page.Text.Length} characters");
Console.WriteLine(page.Text);
}
using IronOcr;
// LoadImageFrames handles multi-frame TIFF automatically
using var input = new OcrInput();
input.LoadImageFrames("scanned-batch.tiff");
var ocr = new IronTesseract();
var result = ocr.Read(input);
// Per-page results accessible directly
foreach (var page in result.Pages)
{
Console.WriteLine($"Frame {page.PageNumber}: {page.Text.Length} characters");
Console.WriteLine(page.Text);
}
Imports IronOcr
' LoadImageFrames handles multi-frame TIFF automatically
Using input As New OcrInput()
input.LoadImageFrames("scanned-batch.tiff")
Dim ocr As New IronTesseract()
Dim result = ocr.Read(input)
' Per-page results accessible directly
For Each page In result.Pages
Console.WriteLine($"Frame {page.PageNumber}: {page.Text.Length} characters")
Console.WriteLine(page.Text)
Next
End Using
OcrInput.LoadImageFrames は手動でのイテレーションなしに、マルチページ TIFF のすべてのフレームを読み込みます。 結果は result.Pages を通じてページごとのアクセスを提供し、テキスト、座標データ、フレームごとの信頼性を含みます。 TIFF入力ガイドでは、マルチフレームTIFFとアニメーションGIFの両方の処理について説明しています。
並列バッチ処理
ABBYY の COM ベースのエンジンは、スレッド制御戦略なしで複数のスレッドから CreateFRDocument を同時に呼び出すことは安全ではありません。 本番環境のバッチプロセッサは通常、エンジンインスタンスのプールを維持するか、ロックを介してアクセスをシリアル化します。 どちらの方法も、 IronOCRが不要とするインフラストラクチャを追加する。
ABBYY FineReaderのアプローチ:
using FREngine;
using System.Collections.Concurrent;
using System.Threading;
public class AbbyyBatchProcessor
{
// Pool required because engine is not safely concurrent
private readonly SemaphoreSlim _engineLock = new SemaphoreSlim(1, 1);
private IEngine _engine;
public async Task<Dictionary<string, string>> ProcessBatchAsync(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// Must serialize — one document at a time through single engine
foreach (var imagePath in imagePaths)
{
await _engineLock.WaitAsync();
try
{
var document = _engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
results[imagePath] = document.PlainText.Text;
}
finally
{
document.Close();
}
}
finally
{
_engineLock.Release();
}
}
return new Dictionary<string, string>(results);
}
}
using FREngine;
using System.Collections.Concurrent;
using System.Threading;
public class AbbyyBatchProcessor
{
// Pool required because engine is not safely concurrent
private readonly SemaphoreSlim _engineLock = new SemaphoreSlim(1, 1);
private IEngine _engine;
public async Task<Dictionary<string, string>> ProcessBatchAsync(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// Must serialize — one document at a time through single engine
foreach (var imagePath in imagePaths)
{
await _engineLock.WaitAsync();
try
{
var document = _engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
results[imagePath] = document.PlainText.Text;
}
finally
{
document.Close();
}
}
finally
{
_engineLock.Release();
}
}
return new Dictionary<string, string>(results);
}
}
Imports FREngine
Imports System.Collections.Concurrent
Imports System.Threading
Public Class AbbyyBatchProcessor
' Pool required because engine is not safely concurrent
Private ReadOnly _engineLock As New SemaphoreSlim(1, 1)
Private _engine As IEngine
Public Async Function ProcessBatchAsync(imagePaths As String()) As Task(Of Dictionary(Of String, String))
Dim results As New ConcurrentDictionary(Of String, String)()
' Must serialize — one document at a time through single engine
For Each imagePath In imagePaths
Await _engineLock.WaitAsync()
Try
Dim document = _engine.CreateFRDocument()
Try
document.AddImageFile(imagePath, Nothing, Nothing)
document.Process(Nothing)
results(imagePath) = document.PlainText.Text
Finally
document.Close()
End Try
Finally
_engineLock.Release()
End Try
Next
Return New Dictionary(Of String, String)(results)
End Function
End Class
IronOCRのアプローチ:
using IronOcr;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class OcrBatchProcessor
{
public Dictionary<string, string> ProcessBatch(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// IronTesseract is thread-safe — one instance per thread, fully parallel
Parallel.ForEach(imagePaths, imagePath =>
{
var ocr = new IronTesseract(); // Each thread owns its instance
var result = ocr.Read(imagePath);
results[imagePath] = result.Text;
});
return new Dictionary<string, string>(results);
}
}
using IronOcr;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class OcrBatchProcessor
{
public Dictionary<string, string> ProcessBatch(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// IronTesseract is thread-safe — one instance per thread, fully parallel
Parallel.ForEach(imagePaths, imagePath =>
{
var ocr = new IronTesseract(); // Each thread owns its instance
var result = ocr.Read(imagePath);
results[imagePath] = result.Text;
});
return new Dictionary<string, string>(results);
}
}
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Threading.Tasks
Public Class OcrBatchProcessor
Public Function ProcessBatch(imagePaths As String()) As Dictionary(Of String, String)
Dim results = New ConcurrentDictionary(Of String, String)()
' IronTesseract is thread-safe — one instance per thread, fully parallel
Parallel.ForEach(imagePaths, Sub(imagePath)
Dim ocr = New IronTesseract() ' Each thread owns its instance
Dim result = ocr.Read(imagePath)
results(imagePath) = result.Text
End Sub)
Return New Dictionary(Of String, String)(results)
End Function
End Class
各 IronTesseract インスタンスは独立しています。 Parallel.ForEach は、共有状態、ロック、シリアル化なしで利用可能な CPU コアを最大限に活用します。 ABBYY版は、非同期ラッパーを使用しているにもかかわらず、ドキュメントを順次処理します。 IronOCRのバージョンでは、それらを真に並列処理します。 マルチスレッドの例では、タイミング比較によってこのパターンを実証しています。 より高度なスループット制御については、速度最適化ガイドを参照してください。
文書エクスポートパイプライン
ABBYY は Export メソッドを介して FileExportFormatEnum 値を使用して複数のエクスポート形式をサポートします。 DOCX、RTF、またはプレーンテキストへのエクスポートには、形式固有のエクスポートパラメータオブジェクトを作成し、適切な列挙値とパラメータオブジェクトを使用して document.Export を呼び出す必要があります。
ABBYY FineReaderのアプローチ:
using FREngine;
public class AbbyyExporter
{
private IEngine _engine;
public void ExportToMultipleFormats(string imagePath, string outputDir)
{
var document = _engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
string baseName = Path.GetFileNameWithoutExtension(imagePath);
// Export as plain text
document.Export(
Path.Combine(outputDir, baseName + ".txt"),
FileExportFormatEnum.FEF_TextUnicodeDefaults,
null
);
// Export as searchable PDF (requires PDF export params)
var pdfParams = _engine.CreatePDFExportParams();
pdfParams.Scenario = PDFExportScenarioEnum.PDES_Balanced;
pdfParams.UseOriginalPaperSize = true;
document.Export(
Path.Combine(outputDir, baseName + ".pdf"),
FileExportFormatEnum.FEF_PDF,
pdfParams
);
// Export as DOCX
var docxParams = _engine.CreateDOCXExportParams();
document.Export(
Path.Combine(outputDir, baseName + ".docx"),
FileExportFormatEnum.FEF_DOCX,
docxParams
);
}
finally
{
document.Close();
}
}
}
using FREngine;
public class AbbyyExporter
{
private IEngine _engine;
public void ExportToMultipleFormats(string imagePath, string outputDir)
{
var document = _engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
string baseName = Path.GetFileNameWithoutExtension(imagePath);
// Export as plain text
document.Export(
Path.Combine(outputDir, baseName + ".txt"),
FileExportFormatEnum.FEF_TextUnicodeDefaults,
null
);
// Export as searchable PDF (requires PDF export params)
var pdfParams = _engine.CreatePDFExportParams();
pdfParams.Scenario = PDFExportScenarioEnum.PDES_Balanced;
pdfParams.UseOriginalPaperSize = true;
document.Export(
Path.Combine(outputDir, baseName + ".pdf"),
FileExportFormatEnum.FEF_PDF,
pdfParams
);
// Export as DOCX
var docxParams = _engine.CreateDOCXExportParams();
document.Export(
Path.Combine(outputDir, baseName + ".docx"),
FileExportFormatEnum.FEF_DOCX,
docxParams
);
}
finally
{
document.Close();
}
}
}
Imports FREngine
Public Class AbbyyExporter
Private _engine As IEngine
Public Sub ExportToMultipleFormats(imagePath As String, outputDir As String)
Dim document = _engine.CreateFRDocument()
Try
document.AddImageFile(imagePath, Nothing, Nothing)
document.Process(Nothing)
Dim baseName As String = Path.GetFileNameWithoutExtension(imagePath)
' Export as plain text
document.Export(
Path.Combine(outputDir, baseName & ".txt"),
FileExportFormatEnum.FEF_TextUnicodeDefaults,
Nothing
)
' Export as searchable PDF (requires PDF export params)
Dim pdfParams = _engine.CreatePDFExportParams()
pdfParams.Scenario = PDFExportScenarioEnum.PDES_Balanced
pdfParams.UseOriginalPaperSize = True
document.Export(
Path.Combine(outputDir, baseName & ".pdf"),
FileExportFormatEnum.FEF_PDF,
pdfParams
)
' Export as DOCX
Dim docxParams = _engine.CreateDOCXExportParams()
document.Export(
Path.Combine(outputDir, baseName & ".docx"),
FileExportFormatEnum.FEF_DOCX,
docxParams
)
Finally
document.Close()
End Try
End Sub
End Class
IronOCRのアプローチ:
using IronOcr;
public class OcrExporter
{
public void ExportToMultipleFormats(string imagePath, string outputDir)
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
string baseName = Path.GetFileNameWithoutExtension(imagePath);
// Plain text — direct property access
File.WriteAllText(
Path.Combine(outputDir, baseName + ".txt"),
result.Text
);
// Searchable PDF — one method call, no parameter objects
result.SaveAsSearchablePdf(
Path.Combine(outputDir, baseName + ".pdf")
);
// hOCR format — for document management systems
result.SaveAsHocrFile(
Path.Combine(outputDir, baseName + ".hocr")
);
}
}
using IronOcr;
public class OcrExporter
{
public void ExportToMultipleFormats(string imagePath, string outputDir)
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
string baseName = Path.GetFileNameWithoutExtension(imagePath);
// Plain text — direct property access
File.WriteAllText(
Path.Combine(outputDir, baseName + ".txt"),
result.Text
);
// Searchable PDF — one method call, no parameter objects
result.SaveAsSearchablePdf(
Path.Combine(outputDir, baseName + ".pdf")
);
// hOCR format — for document management systems
result.SaveAsHocrFile(
Path.Combine(outputDir, baseName + ".hocr")
);
}
}
Imports IronOcr
Imports System.IO
Public Class OcrExporter
Public Sub ExportToMultipleFormats(imagePath As String, outputDir As String)
Dim ocr As New IronTesseract()
Dim result = ocr.Read(imagePath)
Dim baseName As String = Path.GetFileNameWithoutExtension(imagePath)
' Plain text — direct property access
File.WriteAllText(
Path.Combine(outputDir, baseName & ".txt"),
result.Text
)
' Searchable PDF — one method call, no parameter objects
result.SaveAsSearchablePdf(
Path.Combine(outputDir, baseName & ".pdf")
)
' hOCR format — for document management systems
result.SaveAsHocrFile(
Path.Combine(outputDir, baseName & ".hocr")
)
End Sub
End Class
IronOCR の OcrResult は .Text を直接公開し、パラメータオブジェクトや形式列挙型なしで出力メソッドを提供します。 SaveAsSearchablePdf コールは PDF エクスポートを 1 行で処理しますが、ABBYY の 3 ステップパラメータ/エクスポートシーケンスに比べて。 検索可能なPDFガイドには、ページ範囲のオプションと圧縮設定について記載されています。 hOCRエクスポートガイドでは、位置情報を考慮したOCR出力を処理するシステム向けのHOCRフォーマットについて説明しています。
ABBYY FineReader API からIronOCRへのマッピング リファレンス
| ABBYY FineReaderエンジン | IronOCR相当値 |
|---|---|
new EngineLoader() |
不要 |
loader.GetEngineObject(sdkPath, licensePath) |
new IronTesseract() |
engine.LoadPredefinedProfile("...") |
不要(内部処理) |
engine.CreateLanguageParams() |
不要 |
langParams.Languages.Add("French") |
ocr.Language = OcrLanguage.French |
langParams.Languages.Add("English") + langParams.Languages.Add("German") |
ocr.Language = OcrLanguage.English + OcrLanguage.German |
engine.CreateFRDocument() |
new OcrInput() |
engine.CreateFRDocumentFromImage(path, null) |
ocr.Read(path) |
document.AddImageFile(path, null, null) |
input.LoadImage(path) |
imageInfo.LoadImageFile(tiff) + frameCount ループ |
input.LoadImageFrames(tiff) |
engine.CreatePDFFile() の後に pdfFile.Open(path, null, null) |
input.LoadPdf(path) |
document.Process(null) |
ocr.Read(input) |
document.PlainText.Text |
result.Text |
frDocument.Pages[i].PlainText.Text |
result.Pages[i].Text |
page.Layout.Blocks + BlockTypeEnum.BT_Table チェック |
result.Pages + 単語の座標データ |
block.GetAsTableBlock() |
result.Pages[i].Lines (座標付き) |
engine.CreatePDFExportParams() |
不要 |
document.Export(path, FEF_PDF, params) |
result.SaveAsSearchablePdf(path) |
document.Export(path, FEF_TextUnicodeDefaults, null) |
File.WriteAllText(path, result.Text) |
engine.CreateDOCXExportParams() + エクスポート |
直接サポートされていません |
document.Close() |
using によって OcrInput 上で処理されます |
_engine.GetLicenseInfo().ExpirationDate |
IronOcr.License.IsValidLicense |
ライセンスファイル (ABBYY.lic, ABBYY.key) |
IronOcr.License.LicenseKey = "key" |
engine.CreateZone() + zone.SetBounds(x, y, w, h) |
new CropRectangle(x, y, width, height) |
一般的な移行の問題と解決策
問題1:SDK削除後にCOM登録エラーが発生する
ABBYY: プロジェクトリファレンスから FREngine.dll を削除した後でも、ビルドがまだ Could not load type 'FREngine.EngineLoader' または古い名前空間を維持するクラスからの COM インターロップエラーで失敗する可能性があります。
ソリューション: 参照を削除する前に、すべての FREngine と ABBYY.FineReader の使用を検索します。 特に IEngine フィールドを無効にするために IDisposable を実装するすべてのクラスは、その廃棄ロジックを OcrInput 上の using ブロックに置き換える必要があります:
// Before: explicit Close in finally
var document = _engine.CreateFRDocument();
try { document.Process(null); }
finally { document.Close(); }
// After: using pattern on OcrInput
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = new IronTesseract().Read(input);
// Before: explicit Close in finally
var document = _engine.CreateFRDocument();
try { document.Process(null); }
finally { document.Close(); }
// After: using pattern on OcrInput
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = new IronTesseract().Read(input);
Option Strict On
' Before: explicit Close in finally
Dim document = _engine.CreateFRDocument()
Try
document.Process(Nothing)
Finally
document.Close()
End Try
' After: using pattern on OcrInput
Using input As New OcrInput()
input.LoadImage(imagePath)
Dim result = New IronTesseract().Read(input)
End Using
問題2:認識プロファイルに相当するものがない
ABBYY: ABBYY 固有のプロファイルを使用した engine.LoadPredefinedProfile("DocumentConversion_Speed") または engine.LoadPredefinedProfile("FieldLevelRecognition") を呼び出すコードは、精度とスループットのバランスを取ります。 IronOCR に対応する Profile という名前のプロパティはありません。
ソリューション: IronOCR では IronTesseract.Configuration を通じて同じ取引オフを公開します。 速度の最適化のために、ocr.Configuration.TesseractVersion = TesseractVersion.Tesseract5 (デフォルト) を設定し、プリプロセスフィルタを減らします。 精度を最大限に高めるには、完全な前処理パイプラインを追加してください。
// Speed-optimized
var ocr = new IronTesseract();
// なし preprocessing — fastest path
var result = ocr.Read("clean-document.jpg");
// Accuracy-optimized for difficult inputs
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("degraded-scan.jpg");
input.Deskew();
input.DeNoise();
input.Contrast();
input.Binarize();
var result = ocr.Read(input);
// Speed-optimized
var ocr = new IronTesseract();
// なし preprocessing — fastest path
var result = ocr.Read("clean-document.jpg");
// Accuracy-optimized for difficult inputs
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("degraded-scan.jpg");
input.Deskew();
input.DeNoise();
input.Contrast();
input.Binarize();
var result = ocr.Read(input);
Imports IronTesseract
' Speed-optimized
Dim ocr As New IronTesseract()
' なし preprocessing — fastest path
Dim result = ocr.Read("clean-document.jpg")
' Accuracy-optimized for difficult inputs
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadImage("degraded-scan.jpg")
input.Deskew()
input.DeNoise()
input.Contrast()
input.Binarize()
Dim result = ocr.Read(input)
End Using
画像品質補正ガイドでは、どのフィルターがどの入力品質の問題に対処するのかを説明しています。 速度最適化ガイドでは、クリーンなドキュメントの処理時間を短縮するための設定プロパティについて説明します。
問題3:ライセンスファイルのデプロイ手順がCI/CDに残っている
ABBYY: ビルドパイプラインには通常 ABBYY.lic と ABBYY.key をセキュアストアからデプロイターゲットにコピーするステップが含まれています。 移行後、チームがこの手順を削除するのを忘れてしまうことがあり、存在しないパスを参照する無効なデプロイメントコードが残ってしまうことがあります。
解決策:ライセンスファイルのコピー手順を完全に削除する。 環境変数注入ステップに置き換えてください。
# Remove these CI/CD steps after migration:
# - name: Copy ABBYY license files
# run: |
# cp $SECRETS_PATH/ABBYY.lic $DEPLOY_PATH/License/
# cp $SECRETS_PATH/ABBYY.key $DEPLOY_PATH/License/
# Add this instead (environment variable injection):
# - name: Set IronOCR license
# env:
# IRONOCR_LICENSE_KEY: ${{secrets.IRONOCR_LICENSE}}
# Remove these CI/CD steps after migration:
# - name: Copy ABBYY license files
# run: |
# cp $SECRETS_PATH/ABBYY.lic $DEPLOY_PATH/License/
# cp $SECRETS_PATH/ABBYY.key $DEPLOY_PATH/License/
# Add this instead (environment variable injection):
# - name: Set IronOCR license
# env:
# IRONOCR_LICENSE_KEY: ${{secrets.IRONOCR_LICENSE}}
そしてアプリケーションの起動時には:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
?? throw new InvalidOperationException("IRONOCR_LICENSE_KEY not set");
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
?? throw new InvalidOperationException("IRONOCR_LICENSE_KEY not set");
Imports System
IronOcr.License.LicenseKey = If(Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY"), Throw New InvalidOperationException("IRONOCR_LICENSE_KEY not set"))
問題4:エンジンがねじ山に安全でない — 既存のロックコード
ABBYY: 複数のスレッドから ABBYY を呼び出すアプリケーションには通常、lock 文、または COM スレッディング問題を回避するためのスレッドローカルエンジンインスタンスが含まれています。 この同期コードは、ABBYYのスレッドモデルに固有のものです。
解決策: ABBYY呼び出しをラップしている同期コードをすべて削除してください。 IronOCR の IronTesseract はスレッドごとにインスタンス化するのに安全です:
// Remove all of this:
// private readonly SemaphoreSlim _engineLock = new SemaphoreSlim(1, 1);
// await _engineLock.WaitAsync();
// try { ... } finally { _engineLock.Release(); }
// Replace with:
Parallel.ForEach(documents, doc =>
{
var ocr = new IronTesseract(); // One per thread — no lock needed
results[doc.Id] = ocr.Read(doc.Path).Text;
});
// Remove all of this:
// private readonly SemaphoreSlim _engineLock = new SemaphoreSlim(1, 1);
// await _engineLock.WaitAsync();
// try { ... } finally { _engineLock.Release(); }
// Replace with:
Parallel.ForEach(documents, doc =>
{
var ocr = new IronTesseract(); // One per thread — no lock needed
results[doc.Id] = ocr.Read(doc.Path).Text;
});
Imports System.Threading.Tasks
Parallel.ForEach(documents, Sub(doc)
Dim ocr = New IronTesseract() ' One per thread — no lock needed
results(doc.Id) = ocr.Read(doc.Path).Text
End Sub)
問題 5: TIFF 用の CreateImageInfo / FrameCount パターン
ABBYY: フレーム列挙を内部的に処理するため、フレーム数を engine.CreateImageInfo() や imageInfo.LoadImageFile() を使用して TIFF ファイルから読み取るコードは IronOCR には直接的な対応関係がありません。
解決策:フレームカウントループを完全に削除する。
// Remove:
// var imageInfo = engine.CreateImageInfo();
// imageInfo.LoadImageFile(tiffPath);
// for (int i = 0; i < imageInfo.FrameCount; i++) { document.AddImageFile(...) }
// Replace with:
using var input = new OcrInput();
input.LoadImageFrames("multi-page-scan.tiff");
var result = new IronTesseract().Read(input);
// result.Pages contains one entry per TIFF frame
// Remove:
// var imageInfo = engine.CreateImageInfo();
// imageInfo.LoadImageFile(tiffPath);
// for (int i = 0; i < imageInfo.FrameCount; i++) { document.AddImageFile(...) }
// Replace with:
using var input = new OcrInput();
input.LoadImageFrames("multi-page-scan.tiff");
var result = new IronTesseract().Read(input);
// result.Pages contains one entry per TIFF frame
Imports IronOcr
' Remove:
' Dim imageInfo = engine.CreateImageInfo()
' imageInfo.LoadImageFile(tiffPath)
' For i As Integer = 0 To imageInfo.FrameCount - 1
' document.AddImageFile(...)
' Next
' Replace with:
Using input As New OcrInput()
input.LoadImageFrames("multi-page-scan.tiff")
Dim result = New IronTesseract().Read(input)
' result.Pages contains one entry per TIFF frame
End Using
問題6:DOCXエクスポートには直接的な同等の機能がない
ABBYY: document.Export(path, FileExportFormatEnum.FEF_DOCX, docxParams) は Word 文書を作成します。 IronOCRはDOCX形式の出力を直接生成しません。
解決策: IronOCRは、検索可能なPDFファイルと構造化されたテキストデータを生成します。 DOCX出力が必要なワークフローの場合、現実的な移行方法は、検索可能なPDFを作成して後続の処理で変換するか、Open XML SDKなどのライブラリを使用して構造化テキストを抽出してDOCXに書き込むことです。
// IronOCR to searchable PDF (closest equivalent)
var result = new IronTesseract().Read(inputPath);
result.SaveAsSearchablePdf(outputPath.Replace(".docx", ".pdf"));
// Or extract structured text for downstream DOCX generation
foreach (var paragraph in result.Paragraphs)
{
Console.WriteLine(paragraph.Text);
// Write to DOCX via Open XML SDK or similar
}
// IronOCR to searchable PDF (closest equivalent)
var result = new IronTesseract().Read(inputPath);
result.SaveAsSearchablePdf(outputPath.Replace(".docx", ".pdf"));
// Or extract structured text for downstream DOCX generation
foreach (var paragraph in result.Paragraphs)
{
Console.WriteLine(paragraph.Text);
// Write to DOCX via Open XML SDK or similar
}
Imports IronOcr
' IronOCR to searchable PDF (closest equivalent)
Dim result = New IronTesseract().Read(inputPath)
result.SaveAsSearchablePdf(outputPath.Replace(".docx", ".pdf"))
' Or extract structured text for downstream DOCX generation
For Each paragraph In result.Paragraphs
Console.WriteLine(paragraph.Text)
' Write to DOCX via Open XML SDK or similar
Next
読み取り結果ガイドでは、後続処理のために段落、行、単語、および文字レベルの座標データにアクセスする方法について説明します。
ABBYY FineReader移行チェックリスト
マイグレーション前のタスク
変更を加える前に、コードベースを監査してください。
# Find all files using ABBYY namespaces
grep -r "using FREngine" --include="*.cs" .
grep -r "using ABBYY" --include="*.cs" .
# Find engine lifecycle patterns
grep -r "EngineLoader\|GetEngineObject\|LoadPredefinedProfile" --include="*.cs" .
# Find document lifecycle patterns
grep -r "CreateFRDocument\|document\.Close\|AddImageFile\|document\.Process" --include="*.cs" .
# Find language configuration
grep -r "CreateLanguageParams\|langParams\.Languages" --include="*.cs" .
# Find export calls
grep -r "FileExportFormatEnum\|CreatePDFExportParams\|document\.Export" --include="*.cs" .
# Find license file references in CI/CD and deployment scripts
grep -r "ABBYY\.lic\|ABBYY\.key" .
# Find all files using ABBYY namespaces
grep -r "using FREngine" --include="*.cs" .
grep -r "using ABBYY" --include="*.cs" .
# Find engine lifecycle patterns
grep -r "EngineLoader\|GetEngineObject\|LoadPredefinedProfile" --include="*.cs" .
# Find document lifecycle patterns
grep -r "CreateFRDocument\|document\.Close\|AddImageFile\|document\.Process" --include="*.cs" .
# Find language configuration
grep -r "CreateLanguageParams\|langParams\.Languages" --include="*.cs" .
# Find export calls
grep -r "FileExportFormatEnum\|CreatePDFExportParams\|document\.Export" --include="*.cs" .
# Find license file references in CI/CD and deployment scripts
grep -r "ABBYY\.lic\|ABBYY\.key" .
すべての IEngine または IFRDocument フィールドを保持するクラスを文書化します。 使用されているエクスポート形式に注意してください。DOCX出力の場合は別の方法が必要です(上記の問題6を参照)。
コード更新タスク
- すべての
.csprojファイルからFREngine.dll参照を削除してください - ABBYY を使用した各プロジェクトで
dotnet add package IronOcrを実行します - アプリケーションの起動時に
IronOcr.License.LicenseKey = ...を追加します (Program.csまたはスタートアップクラス) - 非英語言語用に言語 NuGet パッケージをインストールします (
dotnet add package IronOcr.Languages.Frenchなど) - すべての
GetEngineObject、およびLoadPredefinedProfileコールを削除します - すべての
CreateLanguageParamsおよびlangParams.Languages.Addコールを削除します engine.CreateFRDocument()+document.AddImageFile()+document.Process()をnew IronTesseract().Read(path)に置き換えます- マルチフレーム TIFF ループを
input.LoadImageFrames(tiffPath)に置き換えます document.PlainText.Textをresult.Textに置き換えますfrDocument.Pages[i].PlainText.Textをresult.Pages[i].Textに置き換えますdocument.Export(..., FEF_PDF, pdfParams)をresult.SaveAsSearchablePdf(path)に置き換えます- すべての
document.Close()コールをOcrInput上のusingブロックに置き換えます SemaphoreSlimおよび ABBYY エンジンアクセスをシリアライズしたロックコードを削除しますengine.CreateZone()/zone.SetBounds()/page.Zones.Add()をinput.LoadImage()に渡されたnew CropRectangle(x, y, width, height)に置き換えます- CI/CDパイプラインからライセンスファイルのコピー手順を削除する
- Docker イメージの更新 — SDK インストール層を削除し、Linux ターゲット用に
libgdiplusを追加します
移行後のテスト
- 各文書タイプ(請求書、契約書、スキャンされたフォームなど)の代表的なサンプルを用いて、テキスト抽出結果の妥当性を検証する。
- 複数ページTIFF処理の結果、ABBYYが生成したフレームと同じページ数が返されることを確認します。
- ABBYYベースライン比較に使用した入力データと同じデータに対して、多言語文書をテストする
- 検索可能なPDF出力がAdobe ReaderおよびブラウザのPDFビューアでテキスト検索可能であることを確認する
- 並列バッチプロセッサを本番環境の同時実行レベルで実行し、例外が発生しないことを確認します。
- 既知の良好な文書で
result.Confidenceを確認し、品質ゲートの基準しきい値を確立します - ステージング展開環境における環境変数からのライセンスキー初期化のテスト
- ABBYY SDKボリュームマウントなしで、DockerイメージがOCRをビルドおよび実行できることを確認します。
- ライセンスファイルのコピー手順なしでCI/CDパイプラインが完了することを確認します。
- バッチプロセッサーでメモリプロファイラーを実行し、
OcrInputオブジェクトが漏れていないことを確認します (usingの配置を検証します)
IronOCRへの移行の主なメリット
導入の複雑さが桁違いに軽減されます。ABBYYの導入では、アプリケーションを起動する前に、SDKのインストール、ライセンスファイルの配置、ランタイムパスの設定、そしてファイルが正しいパスにあることの検証が必要でした。 IronOCRはNuGetの依存関係としてデプロイされます。 dotnet publish は OCR エンジンを含む自己完結型のアーティファクトを作成します。 DockerのデプロイガイドとAzureのセットアップガイドには、完全な構成手順が記載されており、どちらも1ページに収まっています。
COM インターロップがなくなりました。 COM レイヤーを削除することで、ランタイムの障害がなくなります: 新しいマシンでの COM 登録エラー、アパートメントスレッディングの不一致、RCW ライフサイクルバグ、および ABBYY 文書処理コール全体で必要とされる 15〜25 行の try/finally ボイラープレート。 コードベースが縮小する。 それによって誤差範囲も縮小する。
処理量の増加はもはや予算見直しのきっかけにはなりません。IronOCRの永久ライセンスは、文書処理量無制限に対応しています。 初年度に月間1万件の文書を処理するアプリケーションと、3年目に月間200万件の文書を処理するアプリケーションでは、OCRライセンス費用は同じです。ページごとのカウンターも、超過料金の請求書も、ボリュームティアの再交渉もありません。 ライセンスページにはすべてのティアが表示されています。2,999ドルのProfessionalライセンスは、10人の開発者が任意の数のデプロイメントターゲットで任意の処理量を処理することをカバーします。
クロスプラットフォーム展開により、新たなインフラストラクチャの選択肢が広がります。ABBYY COMレイヤーはWindowsを必要とします。 コスト削減や処理能力向上を理由に、文書処理をLinuxコンテナに移行したいと考えていたチームは、その試みを阻まれた。 IronOCRは、同じNuGetパッケージからWindows、Linux、macOSで全く同じように動作します。 ABBYYから移行することで、アプリケーションスタックのOCR層からWindowsの制約が取り除かれます。 Linux導入ガイドとAWS導入ガイドには、それぞれの環境における完全なセットアップ手順が記載されています。
インフラ整備なしで並列処理が可能になりました。ABBYYエンジンへのアクセスを直列化していたロック方式は廃止されました。 IronTesseract インスタンスは独立しています: スレッドごとに 1 つ起動し、ドキュメントバッチ全体で Parallel.ForEach を実行し、結果を取得します。 スループットは、追加のコードなしで利用可能なCPUコア数に応じて向上します。 マルチスレッド処理の例では、マルチコアハードウェアにおける実時間(ウォールクロック)の改善点を示しています。
言語設定はパッケージリファレンスです。ABBYY統合にドイツ語または日本語のOCRサポートを追加するには、データファイルを特定し、各ターゲットマシンの実行時パスにそれらを配置し、ファイルが見つからない場合のエラーを処理する必要がありました。 IronOCR では、dotnet add package IronOcr.Languages.German が言語パックをバージョン化された再現可能な NuGet 依存関係として追加します。 パッケージマネージャーは、すべてのビルドにデータが存在することを保証します。 カスタム言語パックガイドでは、特定のドメイン向けカスタム言語モデルのトレーニングと展開について解説しています。
よくある質問
なぜ ABBYY FineReader Engine から IronOCR に移行する必要があるのですか?
一般的な推進要因には、COM相互接続の複雑さの解消、ファイルベースのライセンス管理の置き換え、ページ単位の課金の回避、Docker/コンテナデプロイの有効化、標準的な.NETツールと統合するNuGetネイティブワークフローの採用などがあります。
ABBYY FineReader Engine から IronOcr に移行する際の主なコード変更は何ですか?
ABBYY FineReaderの初期化シーケンスをIronTesseractのインスタンス化に置き換え、COMのライフサイクル管理(明示的なCreate/Load/Closeパターン)を削除し、結果のプロパティ名を更新します。その結果、定型的な行が大幅に減りました。
IronOCRをインストールして移行を開始するにはどうすればいいですか?
パッケージマネージャーコンソールで'Install-Package IronOcr'を実行するか、CLIで'dotnet add package IronOcr'を実行してください。言語パックは別のパッケージです:例えばフランス語の場合は'dotnet add package IronOcr.Languages.French'となります。
IronOcr は、標準的なビジネス文書の ABBYY FineReader Engine の OCR 精度に匹敵しますか?
IronOCRは、請求書、契約書、領収書、入力フォームなどの標準的なビジネスコンテンツで高い精度を達成しています。画像前処理フィルター(傾き補正、ノイズ除去、コントラスト強調)により、劣化した入力の認識をさらに向上させます。
IronOcr では、ABBYY FineReader Engine が個別にインストールする言語データをどのように扱っていますか?
IronOCRの言語データはNuGetパッケージとして配布されます。'dotnet add package IronOcr.Languages.German'でドイツ語のサポートがインストールされます。手動でのファイル配置やディレクトリパスは必要ありません。
ABBYY FineReader Engine から IronOCR に移行するには、導入インフラストラクチャの変更が必要ですか?
IronOCR は、ABBYY FineReader Engine よりもインフラストラクチャの変更が少なくて済みます。SDK のバイナリ・パス、ライセンス・ファイルの配置、またはライセンス・サーバーの設定は必要ありません。NuGet パッケージには完全な OCR エンジンが含まれており、ライセンス・キーはアプリケーション・コードに設定された文字列です。
移行後のIronOCRライセンスの設定方法は?
アプリケーションの起動コードでIronOcr.License.LicenseKey = "YOUR-KEY "を割り当てます。DockerまたはKubernetesでは、キーを環境変数として保存し、スタートアップで読み込む。License.IsValidLicenseを使用して、トラフィックを受け入れる前にバリデーションを行う。
IronOCR は ABBYY FineReader と同じように PDF を処理できますか?
IronOCRはネイティブPDFもスキャンしたPDFも読み取ります。IronTesseractをインスタンス化し、ocr.Read(input)を呼び出し、入力はPDFパスまたはOcrPdfInputであり、OcrResultページを反復します。別のPDFレンダリングパイプラインは必要ありません。
IronOCRはどのように大量処理のスレッディングを処理するのですか?
IronTesseractはスレッド毎にインスタンス化しても安全です。Parallel.ForEachまたはタスクプールでスレッドごとにインスタンスを1つスピンアップし、OCRを同時に実行し、完了したら各インスタンスを破棄します。グローバルステートやロックは必要ありません。
IronOCRはテキスト抽出後、どのような出力形式をサポートしていますか?
IronOCRはテキスト、単語座標、信頼度スコア、ページ構造を含む構造化された結果を返します。エクスポートオプションには、プレーンテキスト、検索可能なPDF、下流処理用の構造化結果オブジェクトが含まれます。
IronOCR の価格は、ワークロードのスケーリングにおいて ABBYY FineReader Engine よりも予測可能ですか?
IronOCRは、定額制の永久ライセンスを採用しており、ページ単位やボリューム単位の料金はかかりません。10,000ページでも1,000万ページでも、ライセンスコストは一定です。ボリュームライセンスとチームライセンスのオプションはIronOCRの価格ページをご覧ください。
ABBYY FineReader Engine から IronOcr に移行した後、既存のテストはどうなりますか?
抽出されたテキストコンテンツをアサートするテストは、移行後もパスし続ける必要があります。APIコールパターンやCOMオブジェクトのライフサイクルを検証するテストは、IronOCRのシンプルな初期化と結果モデルを反映するように更新する必要があります。

