
Syncfusion OCRからIronOCRへの移行|IronOCR
このガイドでは、スキャンした文書やPDFからテキストを抽出する必要がある.NET開発者向けに、Syncfusion OCR ProcessorからIronOCR for .NETへの完全な移行手順を解説します。 特定の設定変更、コードの書き換え、デプロイのクリーンアップを行うために、Syncfusion.PDF.OCR.Net.Core を、tessdataファイル管理と全てのSyncfusionのOCRデプロイに必要なTesseractのバイナリパスの設定を排除することに特化している IronOcr NuGetパッケージに置き換える必要があります。
Syncfusion OCRからの移行理由
Syncfusion OCRは、1,600のコンポーネントからなるSuiteに組み込まれたTesseractラッパーです。テキスト抽出のみを必要とするチームにとって、このアーキテクチャはセットアップ、デプロイ、メンテナンス、ライセンスのあらゆる段階で負担となります。
tessdataフォルダーは全ての環境に従います。 各開発者の作業環境、CIランナー、ステージングサーバー、および本番コンテナは、アプリケーションが使用する各言語の .traineddata ファイルを含むtessdataディレクトリを必要とします。 英語版のみの場合、標準モデルでは23 MB、最良のLSTMモデルでは94 MBとなります。 5言語対応のアプリケーションは、デプロイメントアーティファクトごとに100~500 MBの容量増加をもたらします。 そのフォルダーはOCRProcessorコンストラクタが期待する正確なパスでなければならず、でないとアプリケーションは起動時に即座にエラーをスローします。これは一度限りのセットアップではなく、新しい環境が構築されるたびに繰り返し発生する運用コストです。
Tesseractバイナリパス設定は環境をまたいで破壊されます。 OCRProcessor コンストラクターは、各ターゲットプラットフォームで正しく解決されなければならないtessdataディレクトリへのパスを必要とします。 Windows開発者マシンで動作するパス(@"tessdata/")は、デプロイパイプラインがそのフォルダーを明示的にコピーしない限り、Linuxコンテナで失敗します。 Dockerイメージのビルドには COPY tessdata/ /app/tessdata/ レイヤーを含む必要があります。 CIパイプラインでは、tessdataのダウンロードをスクリプト化する必要があります。 エアギャップ環境では、バイナリファイルの配布を NuGet パッケージの復元とは別個に管理する必要があります。 各環境において、パス不一致が発生する新たな要因が生じ、その結果、目に見えないOCRエラーや実行時例外が発生する可能性があります。
画像入力のためのPDF中心のアーキテクチャは変換のオーバーヘッドを課します。 Syncfusionの OCRProcessor は画像ファイルではなく PdfLoadedDocument オブジェクトを受け入れます。 JPGからテキストを抽出するには、PdfDocument を作成し、ページを追加し、画像をそこに描画し、MemoryStream に保存し、PdfLoadedDocument として再読み込みし、そこからOCRを実行します。これはテキスト認識ステップの前に9回の操作を含み、画像最初のOCRワークフローごとに実行オーバーヘッドとコードの複雑さを加えます。
Suiteライセンスでは、成長に伴うコンプライアンス要件が発生します。Syncfusionのコミュニティライセンスでは、開発者数が5名未満、従業員数が10名未満、年間売上高が100万ドル未満、かつ外部からの総資金調達額が300万ドル未満であることが求められます。これらの条件はすべて同時に満たす必要があります。 いずれかの閾値を超えた場合、ライセンスは直ちに無効となり、開発者1人あたり年間995~1,595ドルの商用版へのアップグレードが必要となります。 Syncfusion OCRを3年間商用利用している5人の開発者チームは、IronOCR Professionalが2,999ドルの1回払い提供しているのと同じテキスト抽出機能に対して、14,925ドル~23,925ドルを支払っています。
組み込みの前処理機能がないため、画質が劣化したスキャン画像には外部依存関係が生じます。Tesseractは、前処理を行わない場合、回転した画像、ノイズの多い画像、またはコントラストの低い画像では、不十分な結果しか得られません。 Syncfusionは、前処理APIを公開していません。 傾き補正、ノイズ除去、またはコントラスト補正が必要な開発者は、別途画像処理ライブラリ(System.Drawing、SkiaSharp、ImageSharp)を追加し、フィルターを実装した上で、OCR処理を開始する前にその出力をPDFラウンドトリップに組み込む必要があります。 これはサードパーティの依存関係であり、IronOCRが組み込みメソッドとして提供する機能を実現するために、20~40行の追加コードが必要となります。
OCRだけが必要ですが、全スイートがライセンスされています。 Syncfusionは、実際に使用されている機能にかかわらず、Syncfusion.Compression.Net.Core、およびその他の推移的依存関係を取り入れます。 特定の文書処理サービスを構築するチームにとって、テキスト抽出とは無関係なコンポーネントを含むその依存関係グラフは、ビルド時間、コンテナイメージのサイズ、およびライセンス費用において大きな負担となります。
基本的な問題
Syncfusion OCR を使用するには、OCR 呼び出しを行う前に tessdata ファイルシステムのパスを設定する必要があります:
// Syncfusion: tessdata path required — fails in any environment where this path is wrong
private const string TessDataPath = @"tessdata/";
using var document = new PdfLoadedDocument("scanned-invoice.pdf");
using var processor = new OCRProcessor(TessDataPath); // throws if path does not resolve
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
var text = new StringBuilder();
foreach (PdfLoadedPage page in document.Pages)
text.AppendLine(page.ExtractText());' Syncfusion: tessdata path required — fails in any environment where this path is wrong
Private Const TessDataPath As String = "tessdata/"
Using document As New PdfLoadedDocument("scanned-invoice.pdf")
Using processor As New OCRProcessor(TessDataPath) ' throws if path does not resolve
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Dim text As New StringBuilder()
For Each page As PdfLoadedPage In document.Pages
text.AppendLine(page.ExtractText())
Next
End Using
End Using' Syncfusion: tessdata path required — fails in any environment where this path is wrong
Private Const TessDataPath As String = "tessdata/"
Using document As New PdfLoadedDocument("scanned-invoice.pdf")
Using processor As New OCRProcessor(TessDataPath) ' throws if path does not resolve
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Dim text As New StringBuilder()
For Each page As PdfLoadedPage In document.Pages
text.AppendLine(page.ExtractText())
Next
End Using
End UsingIronOCRはパス設定を必要としません。 言語データはパッケージに同梱されています:
// IronOCR: no tessdata path, no path configuration, no folder to deploy
var text = new IronTesseract().Read("scanned-invoice.pdf").Text;// IronOCR: no tessdata path, no path configuration, no folder to deploy
var text = new IronTesseract().Read("scanned-invoice.pdf").Text;' IronOCR: no tessdata path, no path configuration, no folder to deploy
Dim text = New IronTesseract().Read("scanned-invoice.pdf").Text' IronOCR: no tessdata path, no path configuration, no folder to deploy
Dim text = New IronTesseract().Read("scanned-invoice.pdf").TextIronOCR 対 Syncfusion OCR:機能比較
以下の表は、Syncfusion OCRからの移行を検討しているチームにとって最も重要な機能についてまとめたものです。
| フィーチャー | Syncfusion OCR | IronOCR |
|---|---|---|
| NuGetパッケージ。 | Syncfusion.PDF.OCR.Net.Core (スイート) | IronOcr (スタンドアロン) |
| tessdata 必須 | はい - 手動でのダウンロードとパス設定 | いいえ - 内部でバンドルされています |
| ダイレクトイメージOCR | いいえ - PDFへの変換と元への変換が必要です | はい - LoadImage() または直接パス |
| PDFの直接OCR | はい - 主要な入力モデル | はい - 最高水準のサポート |
| 自動前処理 | いいえ - 外部ライブラリが必要です | はい - 傾き補正、ノイズ除去、コントラスト調整、二値化 |
| 検索可能なPDF出力 | はい - PerformOCR() の後に保存 | はい - result.SaveAsSearchablePdf() |
| 対応言語 | 60件以上(tessdataからの手動ダウンロード) | NuGet言語パッケージ経由で125以上 |
| 多言語同時配信 | はい - Languages enum のビットワイズフラグ | はい - AddSecondaryLanguage() |
| 地域ベースのOCR | なし | はい - CropRectangle |
| バーコード読み取り | なし | はい - ocr.Configuration.ReadBarCodes = true |
| 構造化された出力 | ページは page.ExtractText() 経由のみ | ページ、段落、行、単語、座標付き文字 |
| 信頼度スコアリング | なし | はい - result.Confidence と各単語のスコア |
| hOCRエクスポート | なし | はい |
| ストリーム入力 | PDFストリーム経由のみ | 画像およびPDFのダイレクトストリーム入力 |
| スレッドセーフティ | スレッドセーフであることは明記されていない | フル - スレッドごとに IronTesseract インスタンス |
| クロスプラットフォーム。 | はい - ただし、tessdataは各プラットフォームで正しく解決される必要があります | はい - 単一の NuGet パッケージ、パス設定不要 |
| Dockerデプロイメント | 画像内のtessdataレイヤーが必要です | 単一パッケージ、追加レイヤーなし |
| ライセンスモデル | 年間Suiteサブスクリプション($995~$1,595/開発者/年) | 永続 (Lite $999, Pro $1,499, Enterprise $2,999) |
| コミュニティライセンスの制限事項 | 監査権限付きの売上高、従業員数、資金調達額の上限 | 無料トライアルに関する制限はありません |
| OCRエンジン | Tesseract 5 (標準ラッパー) | 精度が向上した最適化版 Tesseract 5 |
クイックスタート:Syncfusion OCR から IronOCR への移行
ステップ 1: NuGet パッケージを置き換える
Syncfusion OCRおよびOCR機能のみを目的として導入されたその他のSyncfusionパッケージは削除してください:
dotnet remove package Syncfusion.PDF.OCR.Net.Core
dotnet remove package Syncfusion.Pdf.Net.Core
dotnet remove package Syncfusion.Compression.Net.Core
NuGetからIronOCRをインストールしてください。
ステップ 2: 名前空間の更新
Syncfusion ネームスペースのインポートを、単一の IronOCR ネームスペースに置き換えてください:
// Before (Syncfusion)
using Syncfusion.OCRProcessor;
using Syncfusion.PDF;
using Syncfusion.Pdf.Parsing;
// After (IronOCR)
using IronOcr;// Before (Syncfusion)
using Syncfusion.OCRProcessor;
using Syncfusion.PDF;
using Syncfusion.Pdf.Parsing;
// After (IronOCR)
using IronOcr;' Before (Syncfusion)
Imports Syncfusion.OCRProcessor
Imports Syncfusion.PDF
Imports Syncfusion.Pdf.Parsing
' After (IronOCR)
Imports IronOcr' Before (Syncfusion)
Imports Syncfusion.OCRProcessor
Imports Syncfusion.PDF
Imports Syncfusion.Pdf.Parsing
' After (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 = "YOUR-LICENSE-KEY"Suiteの登録は不要です。 コミュニティライセンスの適格性チェックは不要です。 キーは、静的プロパティに割り当てられたプレーンな文字列です。
コード移行の例
Tessdataのパス削除とOCRの初期化
Syncfusionコードベースは通常、ディレクトリが存在し、必要な .traineddata ファイルがOCRを試行する前に存在することをチェックするtessdata検証ロジックを含みます。 このガードコードが存在するのは、tessdata ファイルが欠落しているとランタイム例外が発生するためです。また、言語ファイルの欠落による本番環境でのインシデントは頻繁に発生するため、チームは予防的なチェックを実装しています。
SyncfusionのOCRアプローチ:
using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class DocumentOcrService
{
// Path hardcoded — different on every deployment target
private const string TessDataPath = @"tessdata/";
private bool ValidateTessdataBeforeUse(string languageCode)
{
// Guard required because missing files cause runtime exceptions
if (!Directory.Exists(TessDataPath))
throw new InvalidOperationException(
"tessdata directory not found. Download from github.com/tesseract-ocr/tessdata_best");
string filePath = Path.Combine(TessDataPath, $"{languageCode}.traineddata");
if (!File.Exists(filePath))
throw new InvalidOperationException(
$"{languageCode}.traineddata not found — file must be downloaded manually");
return true;
}
public string ExtractText(string pdfPath, string languageCode = "eng")
{
ValidateTessdataBeforeUse(languageCode); // defensive check before every call
using var document = new PdfLoadedDocument(pdfPath);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
var sb = new StringBuilder();
foreach (PdfLoadedPage page in document.Pages)
sb.AppendLine(page.ExtractText());
return sb.ToString();
}
}using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class DocumentOcrService
{
// Path hardcoded — different on every deployment target
private const string TessDataPath = @"tessdata/";
private bool ValidateTessdataBeforeUse(string languageCode)
{
// Guard required because missing files cause runtime exceptions
if (!Directory.Exists(TessDataPath))
throw new InvalidOperationException(
"tessdata directory not found. Download from github.com/tesseract-ocr/tessdata_best");
string filePath = Path.Combine(TessDataPath, $"{languageCode}.traineddata");
if (!File.Exists(filePath))
throw new InvalidOperationException(
$"{languageCode}.traineddata not found — file must be downloaded manually");
return true;
}
public string ExtractText(string pdfPath, string languageCode = "eng")
{
ValidateTessdataBeforeUse(languageCode); // defensive check before every call
using var document = new PdfLoadedDocument(pdfPath);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
var sb = new StringBuilder();
foreach (PdfLoadedPage page in document.Pages)
sb.AppendLine(page.ExtractText());
return sb.ToString();
}
}Imports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class DocumentOcrService
' Path hardcoded — different on every deployment target
Private Const TessDataPath As String = "tessdata/"
Private Function ValidateTessdataBeforeUse(languageCode As String) As Boolean
' Guard required because missing files cause runtime exceptions
If Not Directory.Exists(TessDataPath) Then
Throw New InvalidOperationException("tessdata directory not found. Download from github.com/tesseract-ocr/tessdata_best")
End If
Dim filePath As String = Path.Combine(TessDataPath, $"{languageCode}.traineddata")
If Not File.Exists(filePath) Then
Throw New InvalidOperationException($"{languageCode}.traineddata not found — file must be downloaded manually")
End If
Return True
End Function
Public Function ExtractText(pdfPath As String, Optional languageCode As String = "eng") As String
ValidateTessdataBeforeUse(languageCode) ' defensive check before every call
Using document = New PdfLoadedDocument(pdfPath)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Dim sb = New StringBuilder()
For Each page As PdfLoadedPage In document.Pages
sb.AppendLine(page.ExtractText())
Next
Return sb.ToString()
End Using
End Using
End Function
End ClassImports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class DocumentOcrService
' Path hardcoded — different on every deployment target
Private Const TessDataPath As String = "tessdata/"
Private Function ValidateTessdataBeforeUse(languageCode As String) As Boolean
' Guard required because missing files cause runtime exceptions
If Not Directory.Exists(TessDataPath) Then
Throw New InvalidOperationException("tessdata directory not found. Download from github.com/tesseract-ocr/tessdata_best")
End If
Dim filePath As String = Path.Combine(TessDataPath, $"{languageCode}.traineddata")
If Not File.Exists(filePath) Then
Throw New InvalidOperationException($"{languageCode}.traineddata not found — file must be downloaded manually")
End If
Return True
End Function
Public Function ExtractText(pdfPath As String, Optional languageCode As String = "eng") As String
ValidateTessdataBeforeUse(languageCode) ' defensive check before every call
Using document = New PdfLoadedDocument(pdfPath)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Dim sb = New StringBuilder()
For Each page As PdfLoadedPage In document.Pages
sb.AppendLine(page.ExtractText())
Next
Return sb.ToString()
End Using
End Using
End Function
End ClassIronOCRのアプローチ:
using IronOcr;
public class DocumentOcrService
{
// なし tessdata path — no validation logic — no defensive checks
public string ExtractText(string pdfPath)
{
return new IronTesseract().Read(pdfPath).Text;
}
}using IronOcr;
public class DocumentOcrService
{
// なし tessdata path — no validation logic — no defensive checks
public string ExtractText(string pdfPath)
{
return new IronTesseract().Read(pdfPath).Text;
}
}Imports IronOcr
Public Class DocumentOcrService
' なし tessdata path — no validation logic — no defensive checks
Public Function ExtractText(pdfPath As String) As String
Return New IronTesseract().Read(pdfPath).Text
End Function
End ClassImports IronOcr
Public Class DocumentOcrService
' なし tessdata path — no validation logic — no defensive checks
Public Function ExtractText(pdfPath As String) As String
Return New IronTesseract().Read(pdfPath).Text
End Function
End Class全ての ValidateTessdataBeforeUse メソッドと TessDataPath 定数は削除されます。 tessdata フォルダーをコピーするデプロイメントパイプラインのステップは削除されました。 CIスクリプトでダウンロードされた .traineddata ファイルは削除されました。 tessdataをコンテナイメージにコピーするDockerfileのレイヤーが削除されました。 そのコードを置き換える必要はありません。単に、もはや必要ないからです。 IronTesseractのセットアップガイドでは、デフォルト設定以外の構成が必要な場合に利用可能な、すべての初期化オプションについて解説しています。
検索可能なPDF生成パイプライン
Syncfusionの検索可能なPDF出力は、読み込まれたドキュメントで PerformOCR() を呼び出すことで動作し、それによりその場に見えないテキストレイヤーを追加し、そして修正されたドキュメントをストリームに保存します。 このパターンでは、入力と出力の2つのストリームを管理する必要があり、OCR処理と保存処理は、同じ可変ドキュメントオブジェクトに対して行われる別々の操作となります。
SyncfusionのOCRアプローチ:
using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class SearchablePdfService
{
private const string TessDataPath = @"tessdata/";
public void ConvertToSearchable(string inputPdfPath, string outputPdfPath)
{
// Load document — mutable: PerformOCR modifies it in place
using var document = new PdfLoadedDocument(inputPdfPath);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
// Step 1: OCR modifies the document object
processor.PerformOCR(document);
// Step 2: Save the modified document to a separate output file
using var outputStream = new FileStream(outputPdfPath, FileMode.Create, FileAccess.Write);
document.Save(outputStream);
}
public byte[] ConvertToSearchableBytes(string inputPdfPath)
{
using var document = new PdfLoadedDocument(inputPdfPath);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
using var outputStream = new MemoryStream();
document.Save(outputStream);
return outputStream.ToArray();
}
}using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class SearchablePdfService
{
private const string TessDataPath = @"tessdata/";
public void ConvertToSearchable(string inputPdfPath, string outputPdfPath)
{
// Load document — mutable: PerformOCR modifies it in place
using var document = new PdfLoadedDocument(inputPdfPath);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
// Step 1: OCR modifies the document object
processor.PerformOCR(document);
// Step 2: Save the modified document to a separate output file
using var outputStream = new FileStream(outputPdfPath, FileMode.Create, FileAccess.Write);
document.Save(outputStream);
}
public byte[] ConvertToSearchableBytes(string inputPdfPath)
{
using var document = new PdfLoadedDocument(inputPdfPath);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
using var outputStream = new MemoryStream();
document.Save(outputStream);
return outputStream.ToArray();
}
}Imports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class SearchablePdfService
Private Const TessDataPath As String = "tessdata/"
Public Sub ConvertToSearchable(inputPdfPath As String, outputPdfPath As String)
' Load document — mutable: PerformOCR modifies it in place
Using document = New PdfLoadedDocument(inputPdfPath)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
' Step 1: OCR modifies the document object
processor.PerformOCR(document)
' Step 2: Save the modified document to a separate output file
Using outputStream = New FileStream(outputPdfPath, FileMode.Create, FileAccess.Write)
document.Save(outputStream)
End Using
End Using
End Using
End Sub
Public Function ConvertToSearchableBytes(inputPdfPath As String) As Byte()
Using document = New PdfLoadedDocument(inputPdfPath)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Using outputStream = New MemoryStream()
document.Save(outputStream)
Return outputStream.ToArray()
End Using
End Using
End Using
End Function
End ClassImports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class SearchablePdfService
Private Const TessDataPath As String = "tessdata/"
Public Sub ConvertToSearchable(inputPdfPath As String, outputPdfPath As String)
' Load document — mutable: PerformOCR modifies it in place
Using document = New PdfLoadedDocument(inputPdfPath)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
' Step 1: OCR modifies the document object
processor.PerformOCR(document)
' Step 2: Save the modified document to a separate output file
Using outputStream = New FileStream(outputPdfPath, FileMode.Create, FileAccess.Write)
document.Save(outputStream)
End Using
End Using
End Using
End Sub
Public Function ConvertToSearchableBytes(inputPdfPath As String) As Byte()
Using document = New PdfLoadedDocument(inputPdfPath)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Using outputStream = New MemoryStream()
document.Save(outputStream)
Return outputStream.ToArray()
End Using
End Using
End Using
End Function
End ClassIronOCRのアプローチ:
using IronOcr;
public class SearchablePdfService
{
public void ConvertToSearchable(string inputPdfPath, string outputPdfPath)
{
var result = new IronTesseract().Read(inputPdfPath);
result.SaveAsSearchablePdf(outputPdfPath);
}
public byte[] ConvertToSearchableBytes(string inputPdfPath)
{
using var input = new OcrInput();
input.LoadPdf(inputPdfPath);
var result = new IronTesseract().Read(input);
// SaveAsSearchablePdf also accepts a MemoryStream
using var ms = new MemoryStream();
result.SaveAsSearchablePdf(ms);
return ms.ToArray();
}
}using IronOcr;
public class SearchablePdfService
{
public void ConvertToSearchable(string inputPdfPath, string outputPdfPath)
{
var result = new IronTesseract().Read(inputPdfPath);
result.SaveAsSearchablePdf(outputPdfPath);
}
public byte[] ConvertToSearchableBytes(string inputPdfPath)
{
using var input = new OcrInput();
input.LoadPdf(inputPdfPath);
var result = new IronTesseract().Read(input);
// SaveAsSearchablePdf also accepts a MemoryStream
using var ms = new MemoryStream();
result.SaveAsSearchablePdf(ms);
return ms.ToArray();
}
}Imports IronOcr
Public Class SearchablePdfService
Public Sub ConvertToSearchable(inputPdfPath As String, outputPdfPath As String)
Dim result = New IronTesseract().Read(inputPdfPath)
result.SaveAsSearchablePdf(outputPdfPath)
End Sub
Public Function ConvertToSearchableBytes(inputPdfPath As String) As Byte()
Using input As New OcrInput()
input.LoadPdf(inputPdfPath)
Dim result = New IronTesseract().Read(input)
' SaveAsSearchablePdf also accepts a MemoryStream
Using ms As New MemoryStream()
result.SaveAsSearchablePdf(ms)
Return ms.ToArray()
End Using
End Using
End Function
End ClassImports IronOcr
Public Class SearchablePdfService
Public Sub ConvertToSearchable(inputPdfPath As String, outputPdfPath As String)
Dim result = New IronTesseract().Read(inputPdfPath)
result.SaveAsSearchablePdf(outputPdfPath)
End Sub
Public Function ConvertToSearchableBytes(inputPdfPath As String) As Byte()
Using input As New OcrInput()
input.LoadPdf(inputPdfPath)
Dim result = New IronTesseract().Read(input)
' SaveAsSearchablePdf also accepts a MemoryStream
Using ms As New MemoryStream()
result.SaveAsSearchablePdf(ms)
Return ms.ToArray()
End Using
End Using
End Function
End ClassSyncfusionが使用する変更可能なドキュメントモデルは、読み込まれたドキュメントを保存する前にその場で変更する PerformOCR() から、IronOCRの不変の読み取り後出力パターンに置き換えられます。 OcrResult オブジェクトは認識されたテキストを保持し、それが検索可能なPDFに保存され、プレーンテキストとしてエクスポートされ、または構造化データとして走査されることができます、全て同じ結果からです。 検索可能なPDF形式のハウツーガイドおよびサンプルでは、PDF/A準拠設定を含む追加の出力オプションについて解説しています。
ストリームベースのPDF OCRパイプライン
HTTPアップロード、メッセージキュー、またはBlobストレージ経由でPDFドキュメントを受け取る生産環境のサービスは、通常、ファイルパスではなくストリームを扱います。 SyncfusionはPdfLoadedDocumentを通じてストリームを受け入れますが、tessdataパス制約はまだ適用されます - tessdataフォルダーはストリームが処理されるサーバー上に存在しなければなりません。
SyncfusionのOCRアプローチ:
using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class StreamOcrService
{
private const string TessDataPath = @"tessdata/";
public string ExtractFromStream(Stream pdfStream)
{
// Stream input works, but tessdata path constraint remains
using var document = new PdfLoadedDocument(pdfStream);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
var sb = new StringBuilder();
foreach (PdfLoadedPage page in document.Pages)
sb.AppendLine(page.ExtractText());
return sb.ToString();
}
public async Task<string> ExtractFromStreamAsync(Stream pdfStream)
{
// なし native async — must wrap in Task.Run
return await Task.Run(() => ExtractFromStream(pdfStream));
}
}using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class StreamOcrService
{
private const string TessDataPath = @"tessdata/";
public string ExtractFromStream(Stream pdfStream)
{
// Stream input works, but tessdata path constraint remains
using var document = new PdfLoadedDocument(pdfStream);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
var sb = new StringBuilder();
foreach (PdfLoadedPage page in document.Pages)
sb.AppendLine(page.ExtractText());
return sb.ToString();
}
public async Task<string> ExtractFromStreamAsync(Stream pdfStream)
{
// なし native async — must wrap in Task.Run
return await Task.Run(() => ExtractFromStream(pdfStream));
}
}Imports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class StreamOcrService
Private Const TessDataPath As String = "tessdata/"
Public Function ExtractFromStream(ByVal pdfStream As Stream) As String
' Stream input works, but tessdata path constraint remains
Using document = New PdfLoadedDocument(pdfStream)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Dim sb = New StringBuilder()
For Each page As PdfLoadedPage In document.Pages
sb.AppendLine(page.ExtractText())
Next
Return sb.ToString()
End Using
End Using
End Function
Public Async Function ExtractFromStreamAsync(ByVal pdfStream As Stream) As Task(Of String)
' なし native async — must wrap in Task.Run
Return Await Task.Run(Function() ExtractFromStream(pdfStream))
End Function
End ClassImports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class StreamOcrService
Private Const TessDataPath As String = "tessdata/"
Public Function ExtractFromStream(ByVal pdfStream As Stream) As String
' Stream input works, but tessdata path constraint remains
Using document = New PdfLoadedDocument(pdfStream)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Dim sb = New StringBuilder()
For Each page As PdfLoadedPage In document.Pages
sb.AppendLine(page.ExtractText())
Next
Return sb.ToString()
End Using
End Using
End Function
Public Async Function ExtractFromStreamAsync(ByVal pdfStream As Stream) As Task(Of String)
' なし native async — must wrap in Task.Run
Return Await Task.Run(Function() ExtractFromStream(pdfStream))
End Function
End ClassIronOCRのアプローチ:
using IronOcr;
public class StreamOcrService
{
public string ExtractFromStream(Stream pdfStream)
{
using var input = new OcrInput();
input.LoadPdf(pdfStream); // accepts Stream directly
return new IronTesseract().Read(input).Text;
}
public async Task<string> ExtractFromStreamAsync(Stream pdfStream)
{
using var input = new OcrInput();
input.LoadPdf(pdfStream);
var ocr = new IronTesseract();
var result = await ocr.ReadAsync(input); // native async support
return result.Text;
}
}using IronOcr;
public class StreamOcrService
{
public string ExtractFromStream(Stream pdfStream)
{
using var input = new OcrInput();
input.LoadPdf(pdfStream); // accepts Stream directly
return new IronTesseract().Read(input).Text;
}
public async Task<string> ExtractFromStreamAsync(Stream pdfStream)
{
using var input = new OcrInput();
input.LoadPdf(pdfStream);
var ocr = new IronTesseract();
var result = await ocr.ReadAsync(input); // native async support
return result.Text;
}
}Imports IronOcr
Public Class StreamOcrService
Public Function ExtractFromStream(pdfStream As Stream) As String
Using input = New OcrInput()
input.LoadPdf(pdfStream) ' accepts Stream directly
Return New IronTesseract().Read(input).Text
End Using
End Function
Public Async Function ExtractFromStreamAsync(pdfStream As Stream) As Task(Of String)
Using input = New OcrInput()
input.LoadPdf(pdfStream)
Dim ocr = New IronTesseract()
Dim result = Await ocr.ReadAsync(input) ' native async support
Return result.Text
End Using
End Function
End ClassImports IronOcr
Public Class StreamOcrService
Public Function ExtractFromStream(pdfStream As Stream) As String
Using input = New OcrInput()
input.LoadPdf(pdfStream) ' accepts Stream directly
Return New IronTesseract().Read(input).Text
End Using
End Function
Public Async Function ExtractFromStreamAsync(pdfStream As Stream) As Task(Of String)
Using input = New OcrInput()
input.LoadPdf(pdfStream)
Dim ocr = New IronTesseract()
Dim result = Await ocr.ReadAsync(input) ' native async support
Return result.Text
End Using
End Function
End ClassStreamを直接受け入れ、中間ファイルの書き込みは必要ありません。 IronOCRはTask.Run()ラッパーを必要としません。 Web API コントローラー、Azure Functions、およびその他の非同期サービスパターンにおいて、これはAPIとして直接適合します。 ストリーム入力ガイドでは、画像ストリームや複数ページのTIFFストリームを含む、すべてのストリーム読み込みオプションについて解説しています。 非同期OCRガイドでは、長時間実行されるドキュメントバッチ処理におけるキャンセルトークンのサポートと進行状況コールバックについて解説しています。
構造化された段落およびWORDの抽出
Syncfusionのテキスト抽出モデルは2つのレベルを提供します: result.Text 経由の完全なドキュメントの連結されたテキスト、および page.ExtractText() 反復によるページごとのテキスト サブページ構造はありません。つまり、WORDの座標、段落の境界、トークンごとの信頼度スコアは存在しません。 位置情報に基づいて特定のフィールドを特定したり、信頼度の低いトークンをフィルタリングしたりする必要があるアプリケーションは、連結された文字列に対して独自の解析ロジックを実装する必要があります。
SyncfusionのOCRアプローチ:
using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class StructuredExtractionService
{
private const string TessDataPath = @"tessdata/";
public Dictionary<int, string> ExtractPerPage(string pdfPath)
{
var pageTexts = new Dictionary<int, string>();
using var document = new PdfLoadedDocument(pdfPath);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
// Page-level is the finest granularity available
int pageNum = 1;
foreach (PdfLoadedPage page in document.Pages)
{
pageTexts[pageNum] = page.ExtractText();
pageNum++;
}
return pageTexts;
// なし word coordinates, no paragraph boundaries, no per-token confidence
}
}using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class StructuredExtractionService
{
private const string TessDataPath = @"tessdata/";
public Dictionary<int, string> ExtractPerPage(string pdfPath)
{
var pageTexts = new Dictionary<int, string>();
using var document = new PdfLoadedDocument(pdfPath);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
// Page-level is the finest granularity available
int pageNum = 1;
foreach (PdfLoadedPage page in document.Pages)
{
pageTexts[pageNum] = page.ExtractText();
pageNum++;
}
return pageTexts;
// なし word coordinates, no paragraph boundaries, no per-token confidence
}
}Imports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class StructuredExtractionService
Private Const TessDataPath As String = "tessdata/"
Public Function ExtractPerPage(pdfPath As String) As Dictionary(Of Integer, String)
Dim pageTexts = New Dictionary(Of Integer, String)()
Using document = New PdfLoadedDocument(pdfPath)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
' Page-level is the finest granularity available
Dim pageNum As Integer = 1
For Each page As PdfLoadedPage In document.Pages
pageTexts(pageNum) = page.ExtractText()
pageNum += 1
Next
End Using
End Using
Return pageTexts
' なし word coordinates, no paragraph boundaries, no per-token confidence
End Function
End ClassImports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class StructuredExtractionService
Private Const TessDataPath As String = "tessdata/"
Public Function ExtractPerPage(pdfPath As String) As Dictionary(Of Integer, String)
Dim pageTexts = New Dictionary(Of Integer, String)()
Using document = New PdfLoadedDocument(pdfPath)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
' Page-level is the finest granularity available
Dim pageNum As Integer = 1
For Each page As PdfLoadedPage In document.Pages
pageTexts(pageNum) = page.ExtractText()
pageNum += 1
Next
End Using
End Using
Return pageTexts
' なし word coordinates, no paragraph boundaries, no per-token confidence
End Function
End ClassIronOCRのアプローチ:
using IronOcr;
public class StructuredExtractionService
{
public void ExtractWithStructure(string pdfPath)
{
var result = new IronTesseract().Read(pdfPath);
Console.WriteLine($"Overall confidence: {result.Confidence}%");
foreach (var page in result.Pages)
{
Console.WriteLine($"Page {page.PageNumber}: {page.Words.Length} words");
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($" Paragraph at ({paragraph.X}, {paragraph.Y}):");
Console.WriteLine($" {paragraph.Text}");
}
}
}
public IEnumerable<string> ExtractHighConfidenceWords(string pdfPath, int minConfidence = 80)
{
var result = new IronTesseract().Read(pdfPath);
// Per-word confidence filtering — not possible with Syncfusion's page-level model
return result.Pages
.SelectMany(p => p.Words)
.Where(w => w.Confidence >= minConfidence)
.Select(w => w.Text);
}
}using IronOcr;
public class StructuredExtractionService
{
public void ExtractWithStructure(string pdfPath)
{
var result = new IronTesseract().Read(pdfPath);
Console.WriteLine($"Overall confidence: {result.Confidence}%");
foreach (var page in result.Pages)
{
Console.WriteLine($"Page {page.PageNumber}: {page.Words.Length} words");
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($" Paragraph at ({paragraph.X}, {paragraph.Y}):");
Console.WriteLine($" {paragraph.Text}");
}
}
}
public IEnumerable<string> ExtractHighConfidenceWords(string pdfPath, int minConfidence = 80)
{
var result = new IronTesseract().Read(pdfPath);
// Per-word confidence filtering — not possible with Syncfusion's page-level model
return result.Pages
.SelectMany(p => p.Words)
.Where(w => w.Confidence >= minConfidence)
.Select(w => w.Text);
}
}Imports IronOcr
Public Class StructuredExtractionService
Public Sub ExtractWithStructure(pdfPath As String)
Dim result = New IronTesseract().Read(pdfPath)
Console.WriteLine($"Overall confidence: {result.Confidence}%")
For Each page In result.Pages
Console.WriteLine($"Page {page.PageNumber}: {page.Words.Length} words")
For Each paragraph In page.Paragraphs
Console.WriteLine($" Paragraph at ({paragraph.X}, {paragraph.Y}):")
Console.WriteLine($" {paragraph.Text}")
Next
Next
End Sub
Public Function ExtractHighConfidenceWords(pdfPath As String, Optional minConfidence As Integer = 80) As IEnumerable(Of String)
Dim result = New IronTesseract().Read(pdfPath)
' Per-word confidence filtering — not possible with Syncfusion's page-level model
Return result.Pages _
.SelectMany(Function(p) p.Words) _
.Where(Function(w) w.Confidence >= minConfidence) _
.Select(Function(w) w.Text)
End Function
End ClassImports IronOcr
Public Class StructuredExtractionService
Public Sub ExtractWithStructure(pdfPath As String)
Dim result = New IronTesseract().Read(pdfPath)
Console.WriteLine($"Overall confidence: {result.Confidence}%")
For Each page In result.Pages
Console.WriteLine($"Page {page.PageNumber}: {page.Words.Length} words")
For Each paragraph In page.Paragraphs
Console.WriteLine($" Paragraph at ({paragraph.X}, {paragraph.Y}):")
Console.WriteLine($" {paragraph.Text}")
Next
Next
End Sub
Public Function ExtractHighConfidenceWords(pdfPath As String, Optional minConfidence As Integer = 80) As IEnumerable(Of String)
Dim result = New IronTesseract().Read(pdfPath)
' Per-word confidence filtering — not possible with Syncfusion's page-level model
Return result.Pages _
.SelectMany(Function(p) p.Words) _
.Where(Function(w) w.Confidence >= minConfidence) _
.Select(Function(w) w.Text)
End Function
End Class構造化された出力モデルでは、段落、行、WORD、および文字が、バウンディングボックスの座標と個別の信頼度スコアとともに表示されます。 これは、請求書のフィールド抽出、フォームの解析、ドキュメントの分類など、テキストの内容と同様に、ページ上のテキストの位置を把握することが重要なワークフローにおいて特に有用です。 "Read Results Guide"および"OcrResult API"リファレンスには、オブジェクトグラフ全体が記載されています。
並列実行によるドキュメントの一括処理
大規模なOCRサービスは、数十から数百のドキュメントを同時に処理します。 Syncfusionは OCRProcessor をスレッドセーフとして文書化していないため、順次処理を強制したり、開発者が独自のインスタンスプールを実装する必要があります。 IronOCRインスタンスは、スレッドごとに生成することができ、Parallel.ForEach またはPLINQと直接使用できます、追加の同期は不要です。
SyncfusionのOCRアプローチ:
using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class BatchOcrService
{
private const string TessDataPath = @"tessdata/";
public Dictionary<string, string> ProcessBatch(IEnumerable<string> pdfPaths)
{
var results = new Dictionary<string, string>();
// Sequential processing — OCRProcessor thread safety not guaranteed
foreach (var path in pdfPaths)
{
using var document = new PdfLoadedDocument(path);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
var sb = new StringBuilder();
foreach (PdfLoadedPage page in document.Pages)
sb.AppendLine(page.ExtractText());
results[path] = sb.ToString();
}
return results;
}
}using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class BatchOcrService
{
private const string TessDataPath = @"tessdata/";
public Dictionary<string, string> ProcessBatch(IEnumerable<string> pdfPaths)
{
var results = new Dictionary<string, string>();
// Sequential processing — OCRProcessor thread safety not guaranteed
foreach (var path in pdfPaths)
{
using var document = new PdfLoadedDocument(path);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
var sb = new StringBuilder();
foreach (PdfLoadedPage page in document.Pages)
sb.AppendLine(page.ExtractText());
results[path] = sb.ToString();
}
return results;
}
}Imports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class BatchOcrService
Private Const TessDataPath As String = "tessdata/"
Public Function ProcessBatch(pdfPaths As IEnumerable(Of String)) As Dictionary(Of String, String)
Dim results = New Dictionary(Of String, String)()
' Sequential processing — OCRProcessor thread safety not guaranteed
For Each path In pdfPaths
Using document = New PdfLoadedDocument(path)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Dim sb = New StringBuilder()
For Each page As PdfLoadedPage In document.Pages
sb.AppendLine(page.ExtractText())
Next
results(path) = sb.ToString()
End Using
End Using
Next
Return results
End Function
End ClassImports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class BatchOcrService
Private Const TessDataPath As String = "tessdata/"
Public Function ProcessBatch(pdfPaths As IEnumerable(Of String)) As Dictionary(Of String, String)
Dim results = New Dictionary(Of String, String)()
' Sequential processing — OCRProcessor thread safety not guaranteed
For Each path In pdfPaths
Using document = New PdfLoadedDocument(path)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Dim sb = New StringBuilder()
For Each page As PdfLoadedPage In document.Pages
sb.AppendLine(page.ExtractText())
Next
results(path) = sb.ToString()
End Using
End Using
Next
Return results
End Function
End ClassIronOCRのアプローチ:
using IronOcr;
public class BatchOcrService
{
public Dictionary<string, string> ProcessBatch(IEnumerable<string> pdfPaths)
{
var results = new ConcurrentDictionary<string, string>();
// Parallel processing — IronTesseract is safe per-thread
Parallel.ForEach(pdfPaths, pdfPath =>
{
var ocr = new IronTesseract(); // one instance per thread
var text = ocr.Read(pdfPath).Text;
results[pdfPath] = text;
});
return new Dictionary<string, string>(results);
}
}using IronOcr;
public class BatchOcrService
{
public Dictionary<string, string> ProcessBatch(IEnumerable<string> pdfPaths)
{
var results = new ConcurrentDictionary<string, string>();
// Parallel processing — IronTesseract is safe per-thread
Parallel.ForEach(pdfPaths, pdfPath =>
{
var ocr = new IronTesseract(); // one instance per thread
var text = ocr.Read(pdfPath).Text;
results[pdfPath] = text;
});
return new Dictionary<string, string>(results);
}
}Imports IronOcr
Imports System.Collections.Concurrent
Public Class BatchOcrService
Public Function ProcessBatch(pdfPaths As IEnumerable(Of String)) As Dictionary(Of String, String)
Dim results = New ConcurrentDictionary(Of String, String)()
' Parallel processing — IronTesseract is safe per-thread
Parallel.ForEach(pdfPaths, Sub(pdfPath)
Dim ocr = New IronTesseract() ' one instance per thread
Dim text = ocr.Read(pdfPath).Text
results(pdfPath) = text
End Sub)
Return New Dictionary(Of String, String)(results)
End Function
End ClassImports IronOcr
Imports System.Collections.Concurrent
Public Class BatchOcrService
Public Function ProcessBatch(pdfPaths As IEnumerable(Of String)) As Dictionary(Of String, String)
Dim results = New ConcurrentDictionary(Of String, String)()
' Parallel processing — IronTesseract is safe per-thread
Parallel.ForEach(pdfPaths, Sub(pdfPath)
Dim ocr = New IronTesseract() ' one instance per thread
Dim text = ocr.Read(pdfPath).Text
results(pdfPath) = text
End Sub)
Return New Dictionary(Of String, String)(results)
End Function
End Classパラレル処理のために、スレッドごとにIronTesseractインスタンスを作成することが文書化されたパターンです。 共有状態なし、ロック競合なし、インスタンスプーリングのインフラも不要です。 マルチスレッドの例では、一般的なドキュメントのバッチサイズに対するスループットのベンチマークを示しており、速度最適化ガイドでは、レイテンシに敏感なワークロード向けのエンジン設定オプションについて解説しています。
##Syncfusion OCRAPI から IronOCR へのマッピングリファレンス
| Syncfusion OCR | IronOCR相当値 | ノート |
|---|---|---|
Syncfusion.PDF.OCR.Net.Core | IronOcr | NuGet パッケージを置き換える |
Syncfusion.OCRProcessor | IronOcr | 単一のネームスペース |
Syncfusion.Pdf | 取り除く | もう必要ありません |
Syncfusion.Pdf.Parsing | 取り除く | もう必要ありません |
SyncfusionLicenseProvider.RegisterLicense() | IronOcr.License.LicenseKey = | 文字列の代入、Suiteの登録なし |
new OCRProcessor(tessdataPath) | new IronTesseract() | パス引数なし |
PdfLoadedDocument(filePath) | パスを ocr.Read(path) へ直接渡す | または OcrInput を LoadPdf() と使用 |
PdfLoadedDocument(stream) | input.LoadPdf(stream) | ストリームのサポートは直接的です |
processor.Settings.Language = Languages.English | ocr.Language = OcrLanguage.English | OcrLanguage enum |
Languages.English | Languages.French | ocr.Language = OcrLanguage.English; ocr.AddSecondaryLanguage(OcrLanguage.French) | 加算パターンがビット単位のフラグに取って代わる |
processor.PerformOCR(document) | ocr.Read(input) | OcrResult を直接返します |
page.ExtractText() | result.Text または result.Pages[i].Text | 全文の翻訳にはループ処理は不要です |
document.Pages 反復 | result.Pages[] 配列 | 段落、単語、文字数を含む |
OCR後の document.Save(outputStream) | result.SaveAsSearchablePdf(path) | 専用のメソッド |
| Tessdataの検証ロジック | 完全に削除 | 検証用のデータはありません |
| 手動の tessdata パス定数 | 完全に削除 | IronOCRによる翻訳は不要 |
PdfBitmap 画像からPDFへの変換 | input.LoadImage(imagePath) | 画像OCRのためのPDFの往復処理は不要 |
| 前処理APIなし | input.Deskew(), input.DeNoise(), input.Contrast() | OcrInput に組み込まれています |
一般的な移行の問題と解決策
問題 1: パッケージを切り替えた後に Tessdata ディレクトリが見つからない
Syncfusion OCR: tessdata ディレクトリの検証チェックは、起動時または呼び出しごとに実行されるガードとして実装されています。 Syncfusionを削除し、IronOCRをインストールした後、この検証コードはまだコンパイルできます(これは System.IO を使用しており、Syncfusionの名前空間は使用していません)が、もはや存在しない操作にガードを張ります。 そのまま放置すると、将来の開発者を混乱させる可能性のあるデッドコードとなります。
解決策:tessdataの検証ロジックをすべて完全に削除してください。 TessDataPath 定数、すべての Directory.Exists(TessDataPath) チェック、すべての File.Exists(Path.Combine(TessDataPath, ...)) チェック、および開始時の検証メソッドを削除します。 IronOCRは、欠落しているtessdataが存在しないため、tessdata関連の例外をスローしません:
// Delete these entirely — they have no equivalent in IronOCR
// private const string TessDataPath = @"tessdata/";
// private bool ValidateTessdata() { ... }
// The only error handling needed after migration:
try
{
return new IronTesseract().Read(pdfPath).Text;
}
catch (FileNotFoundException)
{
throw new ArgumentException($"PDF file not found: {pdfPath}");
}// Delete these entirely — they have no equivalent in IronOCR
// private const string TessDataPath = @"tessdata/";
// private bool ValidateTessdata() { ... }
// The only error handling needed after migration:
try
{
return new IronTesseract().Read(pdfPath).Text;
}
catch (FileNotFoundException)
{
throw new ArgumentException($"PDF file not found: {pdfPath}");
}Try
Return New IronTesseract().Read(pdfPath).Text
Catch ex As FileNotFoundException
Throw New ArgumentException($"PDF file not found: {pdfPath}")
End TryTry
Return New IronTesseract().Read(pdfPath).Text
Catch ex As FileNotFoundException
Throw New ArgumentException($"PDF file not found: {pdfPath}")
End Try課題 2: 実行時に言語ファイルが利用できない
Syncfusion OCR: 言語 .traineddata ファイルはファイルシステムアーティファクトとしてデプロイされ、.csproj において CopyToOutputDirectory としてマークされ、ビルドシステムによってコピーされました。 プロジェクトからtessdataフォルダを削除した後、言語関連のCIステップと .csproj エントリはまだ削除されたファイルを参照しているかもしれず、ビルド警告やパイプラインの失敗を引き起こします。
ソリューション: 全CIパイプラインの定義から .csproj ファイルに関連するすべてのtessdataエントリを削除します。 代わりに、言語パックを NuGet パッケージとしてインストールしてください:
# Languages install as NuGet packages — no manual file management
dotnet add package IronOcr.Languages.French
dotnet add package IronOcr.Languages.German
dotnet add package IronOcr.Languages.ChineseSimplified
// Language configuration after migration
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.French;
ocr.AddSecondaryLanguage(OcrLanguage.German);
var result = ocr.Read("multilingual-report.pdf");// Language configuration after migration
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.French;
ocr.AddSecondaryLanguage(OcrLanguage.German);
var result = ocr.Read("multilingual-report.pdf");' Language configuration after migration
Dim ocr As New IronTesseract()
ocr.Language = OcrLanguage.French
ocr.AddSecondaryLanguage(OcrLanguage.German)
Dim result = ocr.Read("multilingual-report.pdf")' Language configuration after migration
Dim ocr As New IronTesseract()
ocr.Language = OcrLanguage.French
ocr.AddSecondaryLanguage(OcrLanguage.German)
Dim result = ocr.Read("multilingual-report.pdf")複数言語ガイドは、言語パックのインストールと、全125以上のサポートされる言語の OcrLanguage enum 値をカバーします。
課題 3: 検索可能な PDF 出力のバイト順が異なる
Syncfusion OCR: 検索可能なPDFは、PerformOCR() がドキュメントを変更した後に document.Save(stream) を呼び出して生成されます。 バイト配列の downstream 側の利用者のうち、Syncfusion 独自の PDF 構造、メタデータフィールド、またはプロデューサー文字列を想定して作成されたものがある可能性があります。
ソリューション: IronOCRの SaveAsSearchablePdf() がテキストレイヤー付きの標準PDFを生成します。 互換性を確認するため、下流のシステム(PDFビューア、検索インデックス、アーカイブシステムなど)で出力結果のテストを行ってください。 バイト単位で完全に同一の出力が求められる場合、テキストの抽出可能性(生のバイトではなく)を比較する移行テストが適切な受け入れ基準となります:
// Verify the searchable PDF contains the expected text
var result = new IronTesseract().Read("scanned.pdf");
result.SaveAsSearchablePdf("output-searchable.pdf");
// Validation: confirm text layer is present and readable
var verificationText = new IronTesseract().Read("output-searchable.pdf").Text;
Assert.True(verificationText.Contains("expected content"));// Verify the searchable PDF contains the expected text
var result = new IronTesseract().Read("scanned.pdf");
result.SaveAsSearchablePdf("output-searchable.pdf");
// Validation: confirm text layer is present and readable
var verificationText = new IronTesseract().Read("output-searchable.pdf").Text;
Assert.True(verificationText.Contains("expected content"));' Verify the searchable PDF contains the expected text
Dim result = New IronTesseract().Read("scanned.pdf")
result.SaveAsSearchablePdf("output-searchable.pdf")
' Validation: confirm text layer is present and readable
Dim verificationText = New IronTesseract().Read("output-searchable.pdf").Text
Assert.True(verificationText.Contains("expected content"))' Verify the searchable PDF contains the expected text
Dim result = New IronTesseract().Read("scanned.pdf")
result.SaveAsSearchablePdf("output-searchable.pdf")
' Validation: confirm text layer is present and readable
Dim verificationText = New IronTesseract().Read("output-searchable.pdf").Text
Assert.True(verificationText.Contains("expected content"))課題 4: 移行を試みた後に Docker イメージのサイズが増加する
Syncfusion OCR: 一部のチームは、テスト中の予防措置として、Docker イメージ内に tessdata ファイルを残したまま移行を試みます。 その結果、画像内にtessdataレイヤーとIronOCRパッケージの両方が含まれることになり、画像サイズが不必要に大きくなってしまいます。
ソリューション: Dockerfileからtessdata COPY レイヤーを削除し、移行されたイメージをビルドする前に削除します。 IronOCR パッケージはスタンドアロン型です。 Docker デプロイメントガイドでは、Alpine、Debian、および Ubuntu 向けの検証済みのベースイメージと設定を提供しています:
# 取り除く this layer entirely after migration
# COPY tessdata/ /app/tessdata/
# IronOCR requires only the standard .NET runtime
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "YourService.dll"]
課題 5: 2 段階の PerformOCR / ExtractText パターンに直接対応するものが存在しない
Syncfusion OCR: ある呼び出しコードは、メソッド間で PdfLoadedDocument 参照を渡し、一つのメソッドが PerformOCR() を呼び出し、別のメソッドが ExtractText() を呼び出し、ドキュメントオブジェクトの状態変化に依存しています。 このパターンはIronOCRには存在しません。なぜなら Read() が自己完結型の結果オブジェクトを返すからです。
ソリューション: 任意の分割されたOCR/抽出パターンを、ファイルパスまたはストリームを受け入れ、OcrResult を返す単一のメソッドにリファクタリングします。 結果オブジェクトには、テキスト、ページ、段落、信頼度、そして検索可能なPDFとして保存する機能など、あらゆる情報が含まれています:
// Replace split PerformOCR / ExtractText pattern
public OcrResult ProcessDocument(string pdfPath)
{
// One call, immutable result, all data available
return new IronTesseract().Read(pdfPath);
}
// Callers decide what they need from the result
var result = service.ProcessDocument("contract.pdf");
var fullText = result.Text;
var confidence = result.Confidence;
result.SaveAsSearchablePdf("contract-searchable.pdf");// Replace split PerformOCR / ExtractText pattern
public OcrResult ProcessDocument(string pdfPath)
{
// One call, immutable result, all data available
return new IronTesseract().Read(pdfPath);
}
// Callers decide what they need from the result
var result = service.ProcessDocument("contract.pdf");
var fullText = result.Text;
var confidence = result.Confidence;
result.SaveAsSearchablePdf("contract-searchable.pdf");Public Function ProcessDocument(pdfPath As String) As OcrResult
' One call, immutable result, all data available
Return New IronTesseract().Read(pdfPath)
End Function
' Callers decide what they need from the result
Dim result = service.ProcessDocument("contract.pdf")
Dim fullText = result.Text
Dim confidence = result.Confidence
result.SaveAsSearchablePdf("contract-searchable.pdf")Public Function ProcessDocument(pdfPath As String) As OcrResult
' One call, immutable result, all data available
Return New IronTesseract().Read(pdfPath)
End Function
' Callers decide what they need from the result
Dim result = service.ProcessDocument("contract.pdf")
Dim fullText = result.Text
Dim confidence = result.Confidence
result.SaveAsSearchablePdf("contract-searchable.pdf")課題 6: 移行後もコミュニティライセンス登録コードが残る
Syncfusion OCR: アプリケーション起動時に Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense() 呼び出しがスイートライセンスを登録します。 この呼び出しは通常、Startup.cs、または静的イニシャライザ内にあります。 Syncfusion パッケージを削除した後、この行によりコンパイルエラーが発生します。
ソリューション: SyncfusionLicenseProvider.RegisterLicense() 呼び出しを削除し、IronOCRライセンスの初期化で置き換えます。 また、コミュニティライセンスの適用条件に関するロジック、コンプライアンス文書の参照、および収益や従業員数の基準に関するコメントはすべて削除してください。これらの概念はいずれもIronOCRには適用されません。
// 取り除く (causes compile error after package removal)
// Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("SYNCFUSION-KEY");
// Add at application startup
IronOcr.License.LicenseKey = "YOUR-IRONOCR-KEY";// 取り除く (causes compile error after package removal)
// Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("SYNCFUSION-KEY");
// Add at application startup
IronOcr.License.LicenseKey = "YOUR-IRONOCR-KEY";' 取り除く (causes compile error after package removal)
' Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("SYNCFUSION-KEY");
' Add at application startup
IronOcr.License.LicenseKey = "YOUR-IRONOCR-KEY"' 取り除く (causes compile error after package removal)
' Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("SYNCFUSION-KEY");
' Add at application startup
IronOcr.License.LicenseKey = "YOUR-IRONOCR-KEY"##Syncfusion OCR移行チェックリスト
移行前
変更を加える前に、コードベースを監査して、Syncfusion OCRの使用箇所をすべて特定してください:
# Find all Syncfusion namespace imports
grep -r "using Syncfusion" --include="*.cs" .
# Find OCRProcessor usage
grep -r "OCRProcessor\|PerformOCR\|PdfLoadedDocument\|ExtractText" --include="*.cs" .
# Find tessdata path references
grep -r "TessDataPath\|tessdata\|traineddata" --include="*.cs" .
# Find Syncfusion license registration
grep -r "SyncfusionLicenseProvider\|RegisterLicense" --include="*.cs" .
# Find csproj tessdata copy rules
grep -r "tessdata\|traineddata" --include="*.csproj" .
# Find Dockerfile tessdata layers
grep -r "tessdata" Dockerfile* docker-compose*.yml .
コードを書く前に、結果を確認してください。 どのファイルにOCR呼び出しが含まれているか、どのファイルにtessdataの検証が含まれているか、またどのパイプライン定義がtessdataフォルダを参照しているかを確認してください。
コードの移行
- すべての
.csprojファイルからSyncfusion.Pdf.Net.Core、および関連パッケージを削除します。 - 各OCRを実行するプロジェクトで
dotnet add package IronOcrを実行します。 - 非英語の言語を使用する場合は、NuGetを通じて言語パックをインストールします:
dotnet add package IronOcr.Languages.[Language]。 - すべてのサービスクラスから
private const string TessDataPath定数を削除します。 - すべてのtessdata検証メソッド(
ValidateTessdata()(および類似のガード))を削除します。 - アプリケーションの起動時に
SyncfusionLicenseProvider.RegisterLicense()をIronOcr.License.LicenseKey = "YOUR-KEY"で置き換えます。 using Syncfusion.OCRProcessor;を置き換えてください using Syncfusion.PDF; using Syncfusion.Pdf.Parsing;withusing IronOcr;`.- 各
new OCRProcessor(TessDataPath)初期化をnew IronTesseract()で置き換えます。 PdfLoadedDocument + processor.PerformOCR() + page.ExtractText()チェーンをocr.Read(path).Textで置き換えます。- Syncfusionのビット単位の言語フラグ(
Languages.English | 言語.フランス語 plusocr.AddSecondaryLanguage()` 呼び出し。 PerformOCR()の後にdocument.Save(stream)をresult.SaveAsSearchablePdf(path)で置き換え、検索可能なPDF出力を生成します。- 画像からPDFへの変換のラウンドトリップを直接
input.LoadImage(imagePath)またはocr.Read(imagePath)に置き換えます。 - すべての
.csprojファイルからtessdataCopyToOutputDirectoryエントリを削除します。 - すべての CI/CD パイプライン定義から tessdata のダウンロード手順を削除してください。
- すべてのDockerfileからtessdata
COPYレイヤーを削除します。
移行後
- 移行前に使用したのと同じサンプル文書に対し、PDF OCRが期待通りのテキストコンテンツを生成することを確認してください。
- 画像(JPG、PNG、BMP)のOCR機能が、PDFへの変換手順を必要とせずに動作することを確認してください。
- インストール済みの NuGet 言語パックを使用して、多言語ドキュメントが正しく認識されることを確認してください。
- 生成されたファイルをPDFビューアで開き、テキストの選択や検索が正常に機能することを確認して、検索可能なPDF出力をテストしてください。
- 更新された Dockerfile から構築した新しい Docker コンテナでアプリケーションを実行し、tessdata に関連する起動エラーが発生しないことを確認してください。
- アプリケーションが
Syncfusion.Licensing呼び出しや任意のSyncfusion名前空間参照なしに開始することを確認します。 result.Confidenceが妥当な値を返すことを確認します(通常クリーンドキュメントでは80〜99%)以做到OCRエンジンがアクティブであることを確認します。- 並列OCR呼び出しを実行し、スレッド関連の例外や結果の破損がないことを確認することで、並列バッチ処理をテストします。
- 移行前後の低品質または回転されたスキャン画像におけるテキスト抽出精度を比較し、自動前処理パイプラインによる改善を確認してください。
IronOCRへの移行の主なメリット
デプロイの複雑さが1つのNuGetパッケージに減少します。 マイグレーション後には、各環境 - 開発者の作業環境、CIランナー、ステージングコンテナ、プロダクションサーバー - が必要なのは1つ: ビルドシステムによって復元された IronOcr NuGetパッケージだけです。 tessdataフォルダがありません。 設定するファイルシステムパスはありません。 言語ファイルのダウンロードスクリプトは不要です。100~500 MBのバイナリデータを含むDockerfileレイヤーも不要です。 コンテナイメージはより軽量になり、CIパイプラインはよりシンプルになり、手動での介入なしに初回ビルドで新しい環境が正しくプロビジョニングされます。
ライセンス費用は予測可能かつ非継続的なものとなります。開発者ごとの年次更新サイクルに代わり、一度きりの永続ライセンス購入となります。 IronOCR Professional(2,999ドル)を購入した5名の開発者チームは、1年間のアップデートを含め、IronOCRライブラリを無期限に所有できます。 監視すべき収益の閾値も、追跡すべき従業員数の制限も、監査に関する規定も、維持すべきコンプライアンス文書も存在しません。 事業拡大に伴う出来事(新規契約者の獲得、大型契約の締結、資金調達ラウンドなど)は、ライセンスの見直しを引き起こしません。
OCRパイプラインは外部依存なしで劣化したドキュメントを処理します。 deskew、ノイズ除去、コントラスト強化、2値化、および解像度スケーリングは OcrInput のメソッドとして利用可能です。 別途画像処理ライブラリは必要ありません。 以前は System.Drawing や SkiaSharp を使用した前処理が必要だった、わずかに回転している、スキャナーノイズがある、またはコントラストが低いドキュメントも、同じ IronOCR 呼び出し内で処理できるようになりました。 画像品質補正ガイドおよび前処理機能のページには、利用可能なすべてのフィルターと、それらが認識精度に与える影響が記載されています。
構造化された出力はフィールドレベルのドキュメントインテリジェンスを可能にします。 OcrResult オブジェクトは、ページ、段落、行、単語、文字、バウンディングボックスの座標、およびトークンごとの信頼スコアを含む完全なドキュメント構造を公開します。 以前は連結されたテキスト文字列を解析してフィールドの境界を特定していたアプリケーションでも、代わりに段落やWORDの座標データを直接利用できるようになります。 請求書処理、フォーム抽出、およびドキュメント分類のワークフローでは、Syncfusionのページレベルモデルでは提供できない空間情報にアクセスできます。PDF OCRのユースケースページでは、一般的なドキュメントインテリジェンスのパターンについて解説しています。
並列バッチ処理はインフラなしでスケールします。 スレッドごとの IronTesseract インスタンスを作成することが完全なスレディング戦略です - インスタンスプールなし、セマフォ管理なし、順次処理制約なし。 1時間に500のドキュメントを処理するバッチサービスは Parallel.ForEach と1行の同期処理で利用可能なCPUコアを飽和させることができます。 自己完結型のエンジンアーキテクチャにより、各スレッドは共有される可変状態を持たずに独立して動作します。
バイナリファイルの管理を必要とせず、125以上の言語に対応しています。すべての言語パックは、標準のパッケージマネージャーを通じてNuGetパッケージとしてインストールされます。 バージョン管理、更新の取得、および依存関係の解決は、他のすべてのプロジェクト依存関係を管理するツールと同じツールによって処理されます。 サービスに日本語またはアラビア語のOCRを追加するには、GitHubリポジトリからの手動ダウンロードおよびデプロイパイプラインの更新ではなく、1つの dotnet add package コマンドが必要です。 言語インデックスには、サポートされているすべてのスクリプトとインストールコマンドが記載されています。
関連する記事


