
Charlesw TesseractからIronOCRへの移行
このガイドは、.NET 開発者が charlesw/tesseract NuGet パッケージ (Tesseract) から IronOCR への移行手順を説明します。 移行の中心となる問題点は、charlesw ラッパーが課すネイティブバイナリ展開モデルとそのモデルが開発者に書かせるプラットフォーム依存コードです。CI で DllNotFoundException に苦しみ、Linux 上の Leptonica ライブラリパスと格闘し、OCR に関係ない OS 検出ブロックを書いたチームは、スイッチを切り替えた後に何が消えるかを正確に示すこのガイドに出会うでしょう。
Charlesw Tesseractから移行する理由
アーカイブされた charlesw/tesseract パッケージが新しいプロジェクトに問題を引き起こすのは、その API が悪いからではなく、それが要求する展開モデルが現代の .NET インフラストラクチャでは通用しない前提に基づいて設計されているためです。 移住の意思決定を左右する要因は以下のとおりです。
プラットフォームごとのネイティブバイナリ展開。 Tesseract NuGet パッケージは、Windows x64 用の tesseract50.dll、x86 用の別のビルド、Linux x64 用の libtesseract.so などのプラットフォーム固有のネイティブバイナリを出荷しています。これらのバイナリは、P/Invoke 呼び出しが成功するために、実行時に正しい場所に配置される必要があります。 開発者ワークステーションでは、SDKがそれらを自動的にコピーします。 Dockerコンテナ、ARM64ビルドエージェント、または非標準のアプリケーションルートを持つAzure App Serviceでは、それらは動作しません。 新しいデプロイメントターゲットはすべてデバッグセッションとなる。
Leptonicaは隠れた依存関係として存在します。Tesseractの画像読み込みはLeptonicaライブラリによって処理され、LeptonicaはTesseractバイナリとともに独自のネイティブDLLセットとして同梱されています。 Windows では、leptonica-1.82.0.dll が出力ディレクトリに存在している必要があります。 Linuxでは、Leptonica共有ライブラリはバンドルされているか、システムパッケージとしてインストールされている必要があります。 Debian ベースの Docker イメージで libleptonica-dev がないと、Pix.LoadFromFile() の際に役に立たないネイティブ例外が発生し、それを修正するには依存関係を解決するシステムパッケージを知る必要があります。
アプリケーションロジックにおけるプラットフォーム依存コード。 ネイティブバイナリのロードと tessdata パスの解決の組み合わせにより、RuntimeInformation.IsOSPlatform() チェック、コンテナコンテキストのための環境変数検出、およびターゲットにより異なるパス構築ロジックを書くことを開発者に強制します。 そのコードにはOCRのロジックは一切含まれていません。 これは、パッケージのバイナリ管理が不完全であるためにのみ存在する、デプロイメントのための仕組みである。
修復パスのないアーカイブ済みパッケージ。リポジトリは2021年からアーカイブされています。Linuxホスト上のシステムパッケージの更新によってLeptonica ABIが変更された場合、または新しい.NETランタイムによってネイティブバイナリの読み込み動作が変更された場合、更新するバージョンはありません。 選択肢は、ネイティブビルドパイプラインをフォークするか、ライブラリを置き換えるかのどちらかしかない。
Tesseract 4.1.1 エンジンのフリーズ。このパッケージは Tesseract 4.1.1 をラップしています。Tesseract 5 で書き直された LSTM モデルは、劣化文書に対して大幅に高い精度を実現します。 そのアップグレードはcharleswパッケージでは利用できません。ライブラリを切り替える必要があります。
標準パターンのない信頼性処理。 charlesw ラッパーは page.GetMeanConfidence() を 0 から 1 までの浮動小数点数として公開しますが、単語や文字レベルで信頼性しきい値を適用するには、iter.GetConfidence(PageIteratorLevel.Word) を使用したイテレータパターンが必要です。 標準的なフィルタリングAPIは存在しません。 各チームはそれぞれ異なる方法で独自のしきい値ロジックを実装している。
基本的な問題
charleswラッパーは、OCRを実行する前にプラットフォーム固有のネイティブバイナリ構成を必要とします。
// charlesw Tesseract: OS detection required just to find native DLLs
// DllNotFoundException on any platform where binaries do not resolve
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
Environment.SetEnvironmentVariable("LD_LIBRARY_PATH", "/app/lib");
}
var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default);
using var img = Pix.LoadFromFile(imagePath); // Requires leptonica native DLL
using var page = engine.Process(img);
return page.GetText();' charlesw Tesseract: OS detection required just to find native DLLs
' DllNotFoundException on any platform where binaries do not resolve
If RuntimeInformation.IsOSPlatform(OSPlatform.Linux) Then
Environment.SetEnvironmentVariable("LD_LIBRARY_PATH", "/app/lib")
End If
Dim engine = New TesseractEngine("./tessdata", "eng", EngineMode.Default)
Using img = Pix.LoadFromFile(imagePath) ' Requires leptonica native DLL
Using page = engine.Process(img)
Return page.GetText()
End Using
End Using' charlesw Tesseract: OS detection required just to find native DLLs
' DllNotFoundException on any platform where binaries do not resolve
If RuntimeInformation.IsOSPlatform(OSPlatform.Linux) Then
Environment.SetEnvironmentVariable("LD_LIBRARY_PATH", "/app/lib")
End If
Dim engine = New TesseractEngine("./tessdata", "eng", EngineMode.Default)
Using img = Pix.LoadFromFile(imagePath) ' Requires leptonica native DLL
Using page = engine.Process(img)
Return page.GetText()
End Using
End UsingIronOCRにはネイティブのバイナリ設定がありません。
// IronOCR: no path management, no OS detection, no leptonica dependency
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var text = new IronTesseract().Read(imagePath).Text;// IronOCR: no path management, no OS detection, no leptonica dependency
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var text = new IronTesseract().Read(imagePath).Text;IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim text As String = New IronTesseract().Read(imagePath).TextIronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim text As String = New IronTesseract().Read(imagePath).TextIronOCRとCharlesw Tesseract:機能比較
以下の表は、この移行を評価するチームにとって関連性の高い機能を示しています。
| フィーチャー | チャールズ・テッセラクト | IronOCR |
|---|---|---|
| メンテナンス状況 | アーカイブ済み(2021年以降更新なし) | 積極的なメンテナンス |
| Tesseractエンジンバージョン | 4.1.1(凍結) | 5(最新版、最適化済み) |
| ライセンス | アパッチ2.0(無料) | 商用 ($999–$2,999 永続) |
| NuGetのインストール | Tesseract | IronOcr |
| ネイティブバイナリ管理 | プラットフォームごとのDLLの手動展開 | バンドル済み、設定不要 |
| レプトニカ依存症 | leptonica-1.82.0.dll / libleptonica-dev が必要 | 該当なし(社内処理済み) |
| Tessdata管理 | 手動でのダウンロードと .csproj コピーエントリ | NuGet言語パック |
| プラットフォーム依存コード | 複数ターゲットへの展開に必要 | 不要 |
| Dockerデプロイメント | 明示的な tessdata COPY + Leptonica apt-get が必要 | 標準 for .NETコンテナ要件のみ |
| ARM64対応 | 未確認の投稿アーカイブ | バンドル化され、検証済み |
| 画像入力フォーマット | TIFF、PNG、BMP、JPG(Leptonica経由) | JPG、PNG、BMP、TIFF、GIFなど |
| 複数ページTIFF | 手動フレーム反復 | input.LoadImageFrames() |
| ネイティブPDF入力 | いいえ(二次図書館が必要) | はい |
| 検索可能なPDF出力 | なし | はい (result.SaveAsSearchablePdf()) |
| 組み込みの前処理 | None | 傾き補正、ノイズ除去、コントラスト補正、二値化、シャープ化、拡大縮小、膨張、収縮、反転 |
| 信頼度フィルタリングAPI | GetConfidence() を使用した手動イテレータ | result.Confidence, word.Confidence |
| 構造化された結果 | イテレータパターン (ResultIterator) | 直接収集(ページ、段落、行、単語) |
| バーコード読み取り | なし | はい(OCR処理中) |
| 地域ベースのOCR | なし | はい (CropRectangle) |
| スレッドセーフ。 | 発信者の責任 | 内蔵 |
| 125以上の言語パック | tessdataの手動ダウンロード | dotnet add package IronOcr.Languages.* |
| クロスプラットフォーム.NET | はい(.NET Standard 2.0) | はい(.NET Framework 4.6.2以降、 .NET 5/6/7/8/9) |
| セキュリティパッチの適用頻度 | なし(アーカイブ済み) | 定期リリース |
クイックスタート:Charlesw TesseractからIronOCRへの移行
ステップ 1: NuGet パッケージを置き換える
charlesw Tesseract パッケージを削除します。
dotnet remove package Tesseract
NuGetからIronOCRをインストールしてください。
ステップ 2: 名前空間の更新
// Before (charlesw Tesseract)
using Tesseract;
// After (IronOCR)
using IronOcr;// Before (charlesw Tesseract)
using Tesseract;
// After (IronOCR)
using IronOcr;' Before (charlesw Tesseract)
Imports Tesseract
' After (IronOCR)
Imports IronOcr' Before (charlesw Tesseract)
Imports Tesseract
' 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"IronOCRのライセンスページで無料トライアルライセンスをご利用いただけます。 このトライアルでは、出力から透かしが削除され、APIへのフルアクセスが可能になります。
コード移行の例
ネイティブバイナリパス構成の削除
charlesw/tesseractプロジェクトで最も一般的な初期化パターンは、tessdataパスを構築し、環境ごとにネイティブライブラリのロードを設定するファクトリまたはヘルパークラスを使用することです。 このコードは、ラッパーのデプロイメントモデルのためにのみ存在します。
Charlesw Tesseract アプローチ:
// A realistic factory found in production charlesw/Tesseract projects
public static class OcrEngineFactory
{
private static string GetTessDataPath()
{
// Different path per environment — all wrong until explicitly configured
if (Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER") == "true")
return "/app/tessdata"; // Docker
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
return Path.Combine(AppContext.BaseDirectory, "tessdata"); // Linux bare metal
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return "/usr/local/share/tessdata"; // macOS Homebrew install
return @".\tessdata"; // Windows dev machine
}
public static TesseractEngine Create(string language = "eng")
{
// If leptonica-1.82.0.dll is not in output directory: DllNotFoundException at this line
// If tessdata folder is missing: TesseractException at engine construction
return new TesseractEngine(GetTessDataPath(), language, EngineMode.Default);
}
}
// Call site
using var engine = OcrEngineFactory.Create();
using var img = Pix.LoadFromFile("invoice.jpg");
using var page = engine.Process(img);
Console.WriteLine(page.GetText());// A realistic factory found in production charlesw/Tesseract projects
public static class OcrEngineFactory
{
private static string GetTessDataPath()
{
// Different path per environment — all wrong until explicitly configured
if (Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER") == "true")
return "/app/tessdata"; // Docker
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
return Path.Combine(AppContext.BaseDirectory, "tessdata"); // Linux bare metal
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return "/usr/local/share/tessdata"; // macOS Homebrew install
return @".\tessdata"; // Windows dev machine
}
public static TesseractEngine Create(string language = "eng")
{
// If leptonica-1.82.0.dll is not in output directory: DllNotFoundException at this line
// If tessdata folder is missing: TesseractException at engine construction
return new TesseractEngine(GetTessDataPath(), language, EngineMode.Default);
}
}
// Call site
using var engine = OcrEngineFactory.Create();
using var img = Pix.LoadFromFile("invoice.jpg");
using var page = engine.Process(img);
Console.WriteLine(page.GetText());Public NotInheritable Class OcrEngineFactory
Private Sub New()
End Sub
Private Shared Function GetTessDataPath() As String
' Different path per environment — all wrong until explicitly configured
If Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER") = "true" Then
Return "/app/tessdata" ' Docker
End If
If RuntimeInformation.IsOSPlatform(OSPlatform.Linux) Then
Return Path.Combine(AppContext.BaseDirectory, "tessdata") ' Linux bare metal
End If
If RuntimeInformation.IsOSPlatform(OSPlatform.OSX) Then
Return "/usr/local/share/tessdata" ' macOS Homebrew install
End If
Return ".\tessdata" ' Windows dev machine
End Function
Public Shared Function Create(Optional language As String = "eng") As TesseractEngine
' If leptonica-1.82.0.dll is not in output directory: DllNotFoundException at this line
' If tessdata folder is missing: TesseractException at engine construction
Return New TesseractEngine(GetTessDataPath(), language, EngineMode.Default)
End Function
End Class
' Call site
Using engine = OcrEngineFactory.Create()
Using img = Pix.LoadFromFile("invoice.jpg")
Using page = engine.Process(img)
Console.WriteLine(page.GetText())
End Using
End Using
End UsingPublic NotInheritable Class OcrEngineFactory
Private Sub New()
End Sub
Private Shared Function GetTessDataPath() As String
' Different path per environment — all wrong until explicitly configured
If Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER") = "true" Then
Return "/app/tessdata" ' Docker
End If
If RuntimeInformation.IsOSPlatform(OSPlatform.Linux) Then
Return Path.Combine(AppContext.BaseDirectory, "tessdata") ' Linux bare metal
End If
If RuntimeInformation.IsOSPlatform(OSPlatform.OSX) Then
Return "/usr/local/share/tessdata" ' macOS Homebrew install
End If
Return ".\tessdata" ' Windows dev machine
End Function
Public Shared Function Create(Optional language As String = "eng") As TesseractEngine
' If leptonica-1.82.0.dll is not in output directory: DllNotFoundException at this line
' If tessdata folder is missing: TesseractException at engine construction
Return New TesseractEngine(GetTessDataPath(), language, EngineMode.Default)
End Function
End Class
' Call site
Using engine = OcrEngineFactory.Create()
Using img = Pix.LoadFromFile("invoice.jpg")
Using page = engine.Process(img)
Console.WriteLine(page.GetText())
End Using
End Using
End UsingIronOCRのアプローチ:
// IronOCR: no factory, no path logic, no OS detection
// Runs identically on Windows, Linux, macOS, and ARM64
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("invoice.jpg");
Console.WriteLine(result.Text);// IronOCR: no factory, no path logic, no OS detection
// Runs identically on Windows, Linux, macOS, and ARM64
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("invoice.jpg");
Console.WriteLine(result.Text);IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim result = New IronTesseract().Read("invoice.jpg")
Console.WriteLine(result.Text)IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim result = New IronTesseract().Read("invoice.jpg")
Console.WriteLine(result.Text)全 OcrEngineFactory クラスが削除されます。 プラットフォーム依存のパスロジック、DOTNET_RUNNING_IN_CONTAINER チェック、Leptonica DLL 依存はすべて一緒に消えます。 開発者ワークステーション、CIエージェント、Dockerコンテナ、クラウドVMなど、あらゆる環境で同じ2行のコードが実行されます。 IronTesseractのセットアップガイドには、デフォルト設定の調整が必要な場合の構成オプションが記載されていますが、ほとんどの導入環境では調整は不要です。
レプトニカ画像変換代替
charlesw ラッパーは Leptonica の Pix 型を画像表現として使用します。 OCR 前に画像を操作するコードはすべて Pix を経由して変換する必要があり、Leptonica ネイティブ DLL をロードして動作させる必要があります。 このパターンを OcrInput に置き換えることで、Leptonica 依存を完全に排除できます。
Charlesw Tesseract アプローチ:
// Pix is Leptonica's image type — requires leptonica native DLL
// Converting from System.Drawing.Bitmap requires a temp file round-trip
public string ProcessInMemoryImage(Bitmap bitmap)
{
// なし direct Bitmap → Pix conversion; must write to temp file
var tempPath = Path.Combine(Path.GetTempPath(), $"ocr_{Guid.NewGuid()}.png");
try
{
bitmap.Save(tempPath, System.Drawing.Imaging.ImageFormat.Png);
using var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default);
using var pix = Pix.LoadFromFile(tempPath); // Leptonica file I/O
using var page = engine.Process(pix);
return page.GetText();
}
finally
{
if (File.Exists(tempPath)) File.Delete(tempPath);
}
}// Pix is Leptonica's image type — requires leptonica native DLL
// Converting from System.Drawing.Bitmap requires a temp file round-trip
public string ProcessInMemoryImage(Bitmap bitmap)
{
// なし direct Bitmap → Pix conversion; must write to temp file
var tempPath = Path.Combine(Path.GetTempPath(), $"ocr_{Guid.NewGuid()}.png");
try
{
bitmap.Save(tempPath, System.Drawing.Imaging.ImageFormat.Png);
using var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default);
using var pix = Pix.LoadFromFile(tempPath); // Leptonica file I/O
using var page = engine.Process(pix);
return page.GetText();
}
finally
{
if (File.Exists(tempPath)) File.Delete(tempPath);
}
}Public Function ProcessInMemoryImage(bitmap As Bitmap) As String
' なし direct Bitmap → Pix conversion; must write to temp file
Dim tempPath = Path.Combine(Path.GetTempPath(), $"ocr_{Guid.NewGuid()}.png")
Try
bitmap.Save(tempPath, System.Drawing.Imaging.ImageFormat.Png)
Using engine = New TesseractEngine("./tessdata", "eng", EngineMode.Default)
Using pix = Pix.LoadFromFile(tempPath) ' Leptonica file I/O
Using page = engine.Process(pix)
Return page.GetText()
End Using
End Using
End Using
Finally
If File.Exists(tempPath) Then File.Delete(tempPath)
End Try
End FunctionPublic Function ProcessInMemoryImage(bitmap As Bitmap) As String
' なし direct Bitmap → Pix conversion; must write to temp file
Dim tempPath = Path.Combine(Path.GetTempPath(), $"ocr_{Guid.NewGuid()}.png")
Try
bitmap.Save(tempPath, System.Drawing.Imaging.ImageFormat.Png)
Using engine = New TesseractEngine("./tessdata", "eng", EngineMode.Default)
Using pix = Pix.LoadFromFile(tempPath) ' Leptonica file I/O
Using page = engine.Process(pix)
Return page.GetText()
End Using
End Using
End Using
Finally
If File.Exists(tempPath) Then File.Delete(tempPath)
End Try
End FunctionIronOCRのアプローチ:
// OcrInput accepts byte arrays and streams — no temp file, no Leptonica
public string ProcessInMemoryImage(byte[] imageBytes)
{
using var input = new OcrInput();
input.LoadImage(imageBytes); // Direct byte array loading
var result = new IronTesseract().Read(input);
return result.Text;
}
// Or from a stream — same pattern
public string ProcessFromStream(Stream imageStream)
{
using var input = new OcrInput();
input.LoadImage(imageStream);
var result = new IronTesseract().Read(input);
return result.Text;
}// OcrInput accepts byte arrays and streams — no temp file, no Leptonica
public string ProcessInMemoryImage(byte[] imageBytes)
{
using var input = new OcrInput();
input.LoadImage(imageBytes); // Direct byte array loading
var result = new IronTesseract().Read(input);
return result.Text;
}
// Or from a stream — same pattern
public string ProcessFromStream(Stream imageStream)
{
using var input = new OcrInput();
input.LoadImage(imageStream);
var result = new IronTesseract().Read(input);
return result.Text;
}' OcrInput accepts byte arrays and streams — no temp file, no Leptonica
Public Function ProcessInMemoryImage(imageBytes As Byte()) As String
Using input As New OcrInput()
input.LoadImage(imageBytes) ' Direct byte array loading
Dim result = (New IronTesseract()).Read(input)
Return result.Text
End Using
End Function
' Or from a stream — same pattern
Public Function ProcessFromStream(imageStream As Stream) As String
Using input As New OcrInput()
input.LoadImage(imageStream)
Dim result = (New IronTesseract()).Read(input)
Return result.Text
End Using
End Function' OcrInput accepts byte arrays and streams — no temp file, no Leptonica
Public Function ProcessInMemoryImage(imageBytes As Byte()) As String
Using input As New OcrInput()
input.LoadImage(imageBytes) ' Direct byte array loading
Dim result = (New IronTesseract()).Read(input)
Return result.Text
End Using
End Function
' Or from a stream — same pattern
Public Function ProcessFromStream(imageStream As Stream) As String
Using input As New OcrInput()
input.LoadImage(imageStream)
Dim result = (New IronTesseract()).Read(input)
Return result.Text
End Using
End Function一時ファイルの往復通信が終了します。 ディスクにファイルを書き込むことなく、変換のために Leptonica DLL を呼び出すことなく、クリーンアップのための finally ブロックもありません。画像入力ガイド と ストリーム入力ガイド には、URL やメモリマップファイルからの読み込みを含む、サポートされるすべての入力ソースが記載されています。
信頼度閾値フィルタリング
charlesw ラッパーは、信頼性を二つのレベルで公開しています: page.GetMeanConfidence() はページ全体用、iter.GetConfidence(PageIteratorLevel.Word) は個々の単語用です。 出力から信頼度の低い単語を除外するには、イテレータループを手動で管理する必要があります。 IronOCRは結果オブジェクトに信頼度を直接公開するため、しきい値ロジックをLINQ式として扱うことができます。
Charlesw Tesseract アプローチ:
// Word-level confidence filtering requires iterator boilerplate
public List<string> ExtractHighConfidenceWords(string imagePath, float minConfidence = 0.8f)
{
var highConfidenceWords = new List<string>();
using var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default);
using var img = Pix.LoadFromFile(imagePath);
using var page = engine.Process(img);
// Page-level confidence only: fine-grained requires the iterator
Console.WriteLine($"Page confidence: {page.GetMeanConfidence():P1}");
using var iter = page.GetIterator();
iter.Begin();
do
{
if (iter.IsAtBeginningOf(PageIteratorLevel.Word))
{
var wordText = iter.GetText(PageIteratorLevel.Word)?.Trim();
var wordConf = iter.GetConfidence(PageIteratorLevel.Word) / 100f; // Returns 0-100
if (!string.IsNullOrEmpty(wordText) && wordConf >= minConfidence)
highConfidenceWords.Add(wordText);
}
} while (iter.Next(PageIteratorLevel.Para, PageIteratorLevel.Word));
return highConfidenceWords;
}// Word-level confidence filtering requires iterator boilerplate
public List<string> ExtractHighConfidenceWords(string imagePath, float minConfidence = 0.8f)
{
var highConfidenceWords = new List<string>();
using var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default);
using var img = Pix.LoadFromFile(imagePath);
using var page = engine.Process(img);
// Page-level confidence only: fine-grained requires the iterator
Console.WriteLine($"Page confidence: {page.GetMeanConfidence():P1}");
using var iter = page.GetIterator();
iter.Begin();
do
{
if (iter.IsAtBeginningOf(PageIteratorLevel.Word))
{
var wordText = iter.GetText(PageIteratorLevel.Word)?.Trim();
var wordConf = iter.GetConfidence(PageIteratorLevel.Word) / 100f; // Returns 0-100
if (!string.IsNullOrEmpty(wordText) && wordConf >= minConfidence)
highConfidenceWords.Add(wordText);
}
} while (iter.Next(PageIteratorLevel.Para, PageIteratorLevel.Word));
return highConfidenceWords;
}Public Function ExtractHighConfidenceWords(imagePath As String, Optional minConfidence As Single = 0.8F) As List(Of String)
Dim highConfidenceWords As New List(Of String)()
Using engine = New TesseractEngine("./tessdata", "eng", EngineMode.Default)
Using img = Pix.LoadFromFile(imagePath)
Using page = engine.Process(img)
' Page-level confidence only: fine-grained requires the iterator
Console.WriteLine($"Page confidence: {page.GetMeanConfidence():P1}")
Using iter = page.GetIterator()
iter.Begin()
Do
If iter.IsAtBeginningOf(PageIteratorLevel.Word) Then
Dim wordText = iter.GetText(PageIteratorLevel.Word)?.Trim()
Dim wordConf = iter.GetConfidence(PageIteratorLevel.Word) / 100.0F ' Returns 0-100
If Not String.IsNullOrEmpty(wordText) And wordConf >= minConfidence Then
highConfidenceWords.Add(wordText)
End If
End If
Loop While iter.Next(PageIteratorLevel.Para, PageIteratorLevel.Word)
End Using
End Using
End Using
End Using
Return highConfidenceWords
End FunctionPublic Function ExtractHighConfidenceWords(imagePath As String, Optional minConfidence As Single = 0.8F) As List(Of String)
Dim highConfidenceWords As New List(Of String)()
Using engine = New TesseractEngine("./tessdata", "eng", EngineMode.Default)
Using img = Pix.LoadFromFile(imagePath)
Using page = engine.Process(img)
' Page-level confidence only: fine-grained requires the iterator
Console.WriteLine($"Page confidence: {page.GetMeanConfidence():P1}")
Using iter = page.GetIterator()
iter.Begin()
Do
If iter.IsAtBeginningOf(PageIteratorLevel.Word) Then
Dim wordText = iter.GetText(PageIteratorLevel.Word)?.Trim()
Dim wordConf = iter.GetConfidence(PageIteratorLevel.Word) / 100.0F ' Returns 0-100
If Not String.IsNullOrEmpty(wordText) And wordConf >= minConfidence Then
highConfidenceWords.Add(wordText)
End If
End If
Loop While iter.Next(PageIteratorLevel.Para, PageIteratorLevel.Word)
End Using
End Using
End Using
End Using
Return highConfidenceWords
End FunctionIronOCRのアプローチ:
// Confidence is a property on each result object — no iterator required
public List<string> ExtractHighConfidenceWords(string imagePath, double minConfidence = 80.0)
{
var result = new IronTesseract().Read(imagePath);
Console.WriteLine($"Page confidence: {result.Confidence}%");
// LINQ directly on the word collection — no iterator state management
return result.Pages
.SelectMany(p => p.Lines)
.SelectMany(l => l.Words)
.Where(w => w.Confidence >= minConfidence && !string.IsNullOrWhiteSpace(w.Text))
.Select(w => w.Text)
.ToList();
}// Confidence is a property on each result object — no iterator required
public List<string> ExtractHighConfidenceWords(string imagePath, double minConfidence = 80.0)
{
var result = new IronTesseract().Read(imagePath);
Console.WriteLine($"Page confidence: {result.Confidence}%");
// LINQ directly on the word collection — no iterator state management
return result.Pages
.SelectMany(p => p.Lines)
.SelectMany(l => l.Words)
.Where(w => w.Confidence >= minConfidence && !string.IsNullOrWhiteSpace(w.Text))
.Select(w => w.Text)
.ToList();
}Public Function ExtractHighConfidenceWords(imagePath As String, Optional minConfidence As Double = 80.0) As List(Of String)
Dim result = New IronTesseract().Read(imagePath)
Console.WriteLine($"Page confidence: {result.Confidence}%")
Return result.Pages _
.SelectMany(Function(p) p.Lines) _
.SelectMany(Function(l) l.Words) _
.Where(Function(w) w.Confidence >= minConfidence AndAlso Not String.IsNullOrWhiteSpace(w.Text)) _
.Select(Function(w) w.Text) _
.ToList()
End FunctionPublic Function ExtractHighConfidenceWords(imagePath As String, Optional minConfidence As Double = 80.0) As List(Of String)
Dim result = New IronTesseract().Read(imagePath)
Console.WriteLine($"Page confidence: {result.Confidence}%")
Return result.Pages _
.SelectMany(Function(p) p.Lines) _
.SelectMany(Function(l) l.Words) _
.Where(Function(w) w.Confidence >= minConfidence AndAlso Not String.IsNullOrWhiteSpace(w.Text)) _
.Select(Function(w) w.Text) _
.ToList()
End Functionイテレータの状態機械はなくなりました。 IronOCRにおける信頼度値は、常に0~100のスケールで表され、100で割る必要はありません。 信頼度スコアガイドでは、単語単位、行単位、ページ単位の信頼度アクセスパターンについて解説しています。 検索結果の閲覧ガイドでは、構造化された検索結果の階層構造全体をどのようにナビゲートするかを示しています。
複数ページTIFFファイルのバッチ処理
複数のフレームを含むTIFFファイルは、文書スキャンワークフローでよく使用されます。 charleswラッパーには、マルチフレームTIFFのサポート機能が組み込まれていません。 処理前に各フレームを手動で抽出する必要があります。 IronOCRは、単一の読み込み呼び出しでマルチフレームTIFFをネイティブに処理します。
Charlesw Tesseract アプローチ:
// charlesw/Tesseract has no multi-frame TIFF support
// Each frame must be extracted via System.Drawing before OCR can run
public string ProcessMultiFrameTiff(string tiffPath)
{
var fullText = new StringBuilder();
using var tiffImage = Image.FromFile(tiffPath);
var frameCount = tiffImage.GetFrameCount(FrameDimension.Page);
using var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default);
for (int i = 0; i < frameCount; i++)
{
tiffImage.SelectActiveFrame(FrameDimension.Page, i);
// Must save each frame as a temp file for Pix to load
var tempPath = Path.Combine(Path.GetTempPath(), $"tiff_frame_{i}.png");
try
{
tiffImage.Save(tempPath, System.Drawing.Imaging.ImageFormat.Png);
using var pix = Pix.LoadFromFile(tempPath);
using var page = engine.Process(pix);
fullText.AppendLine(page.GetText());
}
finally
{
if (File.Exists(tempPath)) File.Delete(tempPath);
}
}
return fullText.ToString();
}// charlesw/Tesseract has no multi-frame TIFF support
// Each frame must be extracted via System.Drawing before OCR can run
public string ProcessMultiFrameTiff(string tiffPath)
{
var fullText = new StringBuilder();
using var tiffImage = Image.FromFile(tiffPath);
var frameCount = tiffImage.GetFrameCount(FrameDimension.Page);
using var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default);
for (int i = 0; i < frameCount; i++)
{
tiffImage.SelectActiveFrame(FrameDimension.Page, i);
// Must save each frame as a temp file for Pix to load
var tempPath = Path.Combine(Path.GetTempPath(), $"tiff_frame_{i}.png");
try
{
tiffImage.Save(tempPath, System.Drawing.Imaging.ImageFormat.Png);
using var pix = Pix.LoadFromFile(tempPath);
using var page = engine.Process(pix);
fullText.AppendLine(page.GetText());
}
finally
{
if (File.Exists(tempPath)) File.Delete(tempPath);
}
}
return fullText.ToString();
}Public Function ProcessMultiFrameTiff(tiffPath As String) As String
Dim fullText = New StringBuilder()
Using tiffImage = Image.FromFile(tiffPath)
Dim frameCount = tiffImage.GetFrameCount(FrameDimension.Page)
Using engine = New TesseractEngine("./tessdata", "eng", EngineMode.Default)
For i As Integer = 0 To frameCount - 1
tiffImage.SelectActiveFrame(FrameDimension.Page, i)
' Must save each frame as a temp file for Pix to load
Dim tempPath = Path.Combine(Path.GetTempPath(), $"tiff_frame_{i}.png")
Try
tiffImage.Save(tempPath, System.Drawing.Imaging.ImageFormat.Png)
Using pix = Pix.LoadFromFile(tempPath)
Using page = engine.Process(pix)
fullText.AppendLine(page.GetText())
End Using
End Using
Finally
If File.Exists(tempPath) Then File.Delete(tempPath)
End Try
Next
End Using
End Using
Return fullText.ToString()
End FunctionPublic Function ProcessMultiFrameTiff(tiffPath As String) As String
Dim fullText = New StringBuilder()
Using tiffImage = Image.FromFile(tiffPath)
Dim frameCount = tiffImage.GetFrameCount(FrameDimension.Page)
Using engine = New TesseractEngine("./tessdata", "eng", EngineMode.Default)
For i As Integer = 0 To frameCount - 1
tiffImage.SelectActiveFrame(FrameDimension.Page, i)
' Must save each frame as a temp file for Pix to load
Dim tempPath = Path.Combine(Path.GetTempPath(), $"tiff_frame_{i}.png")
Try
tiffImage.Save(tempPath, System.Drawing.Imaging.ImageFormat.Png)
Using pix = Pix.LoadFromFile(tempPath)
Using page = engine.Process(pix)
fullText.AppendLine(page.GetText())
End Using
End Using
Finally
If File.Exists(tempPath) Then File.Delete(tempPath)
End Try
Next
End Using
End Using
Return fullText.ToString()
End FunctionIronOCRのアプローチ:
// LoadImageFrames handles multi-frame TIFFs natively — no frame extraction loop
public string ProcessMultiFrameTiff(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // All frames loaded in one call
var result = new IronTesseract().Read(input);
// Pages maps directly to TIFF frames
foreach (var page in result.Pages)
Console.WriteLine($"Frame {page.PageNumber}: {page.Words.Count()} words");
return result.Text;
}// LoadImageFrames handles multi-frame TIFFs natively — no frame extraction loop
public string ProcessMultiFrameTiff(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // All frames loaded in one call
var result = new IronTesseract().Read(input);
// Pages maps directly to TIFF frames
foreach (var page in result.Pages)
Console.WriteLine($"Frame {page.PageNumber}: {page.Words.Count()} words");
return result.Text;
}Public Function ProcessMultiFrameTiff(ByVal tiffPath As String) As String
Using input = New OcrInput()
input.LoadImageFrames(tiffPath) ' All frames loaded in one call
Dim result = (New IronTesseract()).Read(input)
' Pages maps directly to TIFF frames
For Each page In result.Pages
Console.WriteLine($"Frame {page.PageNumber}: {page.Words.Count()} words")
Next
Return result.Text
End Using
End FunctionPublic Function ProcessMultiFrameTiff(ByVal tiffPath As String) As String
Using input = New OcrInput()
input.LoadImageFrames(tiffPath) ' All frames loaded in one call
Dim result = (New IronTesseract()).Read(input)
' Pages maps directly to TIFF frames
For Each page In result.Pages
Console.WriteLine($"Frame {page.PageNumber}: {page.Words.Count()} words")
Next
Return result.Text
End Using
End Function一時ファイル抽出ループとフレームごとの破棄処理チェーンは削除されました。 フレームカウント検出が FrameDimension.Page 経由で消えます。 IronOCR は TIFF フレームを OcrResult.Pages にマッピングするため、フレームごとのテキストアクセスは追加のイテレーションロジックを必要としません。 TIFF/GIF入力ガイドでは、フレーム選択や部分的なTIFF処理に関する追加オプションについて説明しています。
検索可能なPDF生成
charleswラッパーはテキスト出力のみを生成します。 スキャンしたドキュメントを検索可能なPDFに変換することは、ドキュメント管理システムの一般的な要件であり、抽出されたテキストを元の画像ページにオーバーレイするために二次PDFライブラリ(IronPDF、PDFSharp、または類似)が必要です。 IronOCRは、二次的なライブラリを使用せずに、1回のメソッド呼び出しで検索可能なPDFを生成します。
Charlesw Tesseract アプローチ:
// charlesw/Tesseract produces text only.
// Creating a searchable PDF requires a second library and significant code.
// The pattern below is representative — actual implementation varies by PDF library.
public void CreateSearchablePdf(string imagePath, string outputPdfPath)
{
// Step 1: Extract text from image
string extractedText;
using var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default);
using var img = Pix.LoadFromFile(imagePath);
using var page = engine.Process(img);
extractedText = page.GetText();
// Step 2: Build a PDF with the image as background and text overlay
// Requires a separate PDF library (not shown — 50-100+ additional lines)
// The text layer must be positioned to match the original image layout
// Word-level coordinates from the iterator are needed for accurate alignment
throw new NotImplementedException(
"Searchable PDF generation requires a separate PDF library. " +
"Add PdfSharp, IronPDF, or similar, then implement text layer overlay.");
}// charlesw/Tesseract produces text only.
// Creating a searchable PDF requires a second library and significant code.
// The pattern below is representative — actual implementation varies by PDF library.
public void CreateSearchablePdf(string imagePath, string outputPdfPath)
{
// Step 1: Extract text from image
string extractedText;
using var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default);
using var img = Pix.LoadFromFile(imagePath);
using var page = engine.Process(img);
extractedText = page.GetText();
// Step 2: Build a PDF with the image as background and text overlay
// Requires a separate PDF library (not shown — 50-100+ additional lines)
// The text layer must be positioned to match the original image layout
// Word-level coordinates from the iterator are needed for accurate alignment
throw new NotImplementedException(
"Searchable PDF generation requires a separate PDF library. " +
"Add PdfSharp, IronPDF, or similar, then implement text layer overlay.");
}Public Sub CreateSearchablePdf(imagePath As String, outputPdfPath As String)
' Step 1: Extract text from image
Dim extractedText As String
Using engine = New TesseractEngine("./tessdata", "eng", EngineMode.Default)
Using img = Pix.LoadFromFile(imagePath)
Using page = engine.Process(img)
extractedText = page.GetText()
End Using
End Using
End Using
' Step 2: Build a PDF with the image as background and text overlay
' Requires a separate PDF library (not shown — 50-100+ additional lines)
' The text layer must be positioned to match the original image layout
' Word-level coordinates from the iterator are needed for accurate alignment
Throw New NotImplementedException("Searchable PDF generation requires a separate PDF library. " & "Add PdfSharp, IronPDF, or similar, then implement text layer overlay.")
End SubPublic Sub CreateSearchablePdf(imagePath As String, outputPdfPath As String)
' Step 1: Extract text from image
Dim extractedText As String
Using engine = New TesseractEngine("./tessdata", "eng", EngineMode.Default)
Using img = Pix.LoadFromFile(imagePath)
Using page = engine.Process(img)
extractedText = page.GetText()
End Using
End Using
End Using
' Step 2: Build a PDF with the image as background and text overlay
' Requires a separate PDF library (not shown — 50-100+ additional lines)
' The text layer must be positioned to match the original image layout
' Word-level coordinates from the iterator are needed for accurate alignment
Throw New NotImplementedException("Searchable PDF generation requires a separate PDF library. " & "Add PdfSharp, IronPDF, or similar, then implement text layer overlay.")
End SubIronOCRのアプローチ:
// SaveAsSearchablePdf produces a PDF/A-compatible searchable document
// なし secondary library, no text overlay code, no coordinate mapping
public void CreateSearchablePdf(string imagePath, string outputPdfPath)
{
var result = new IronTesseract().Read(imagePath);
result.SaveAsSearchablePdf(outputPdfPath);
Console.WriteLine($"Searchable PDF saved: {outputPdfPath}");
}
// Same API works for multi-page TIFF or existing PDF input
public void MakePdfSearchable(string scannedPdfPath, string outputPdfPath)
{
var result = new IronTesseract().Read(scannedPdfPath);
result.SaveAsSearchablePdf(outputPdfPath);
}// SaveAsSearchablePdf produces a PDF/A-compatible searchable document
// なし secondary library, no text overlay code, no coordinate mapping
public void CreateSearchablePdf(string imagePath, string outputPdfPath)
{
var result = new IronTesseract().Read(imagePath);
result.SaveAsSearchablePdf(outputPdfPath);
Console.WriteLine($"Searchable PDF saved: {outputPdfPath}");
}
// Same API works for multi-page TIFF or existing PDF input
public void MakePdfSearchable(string scannedPdfPath, string outputPdfPath)
{
var result = new IronTesseract().Read(scannedPdfPath);
result.SaveAsSearchablePdf(outputPdfPath);
}Public Sub CreateSearchablePdf(ByVal imagePath As String, ByVal outputPdfPath As String)
Dim result = New IronTesseract().Read(imagePath)
result.SaveAsSearchablePdf(outputPdfPath)
Console.WriteLine($"Searchable PDF saved: {outputPdfPath}")
End Sub
Public Sub MakePdfSearchable(ByVal scannedPdfPath As String, ByVal outputPdfPath As String)
Dim result = New IronTesseract().Read(scannedPdfPath)
result.SaveAsSearchablePdf(outputPdfPath)
End SubPublic Sub CreateSearchablePdf(ByVal imagePath As String, ByVal outputPdfPath As String)
Dim result = New IronTesseract().Read(imagePath)
result.SaveAsSearchablePdf(outputPdfPath)
Console.WriteLine($"Searchable PDF saved: {outputPdfPath}")
End Sub
Public Sub MakePdfSearchable(ByVal scannedPdfPath As String, ByVal outputPdfPath As String)
Dim result = New IronTesseract().Read(scannedPdfPath)
result.SaveAsSearchablePdf(outputPdfPath)
End SubSaveAsSearchablePdf() は OCR テキストを認識された単語に合わせて目に見えない層として埋め込み、ドキュメントを視覚的外観を変えることなく全文検索可能にします。 検索可能なPDF形式の操作ガイドでは、ページ範囲の選択方法と圧縮オプションについて解説しています。 検索可能なPDFサンプルページで、動作例をご覧いただけます。
チャールズ・テッセラクト API からIronOCRへのマッピング リファレンス
| チャールズ・テッセラクト | IronOCR相当値 |
|---|---|
new TesseractEngine(tessDataPath, "eng", EngineMode.Default) | new IronTesseract() |
Pix.LoadFromFile(imagePath) | input.LoadImage(imagePath) |
Pix.LoadFromMemory(bytes) | input.LoadImage(imageBytes) |
engine.Process(pix) | ocr.Read(input) |
page.GetText() | result.Text |
page.GetMeanConfidence() | result.Confidence (0–100 スケール) |
page.GetIterator() | result.Pages, result.Words (直接コレクション) |
iter.GetText(PageIteratorLevel.Word) | word.Text |
iter.GetConfidence(PageIteratorLevel.Word) | word.Confidence |
iter.TryGetBoundingBox(PageIteratorLevel.Word, out var b) | word.X, word.Y, word.Width, word.Height |
iter.GetText(PageIteratorLevel.Para) | paragraph.Text |
iter.IsAtBeginningOf(PageIteratorLevel.Block) | page.Paragraphs (直接イテレート) |
EngineMode.Default | 自動(Tesseract 5 LSTM デフォルト) |
EngineMode.TesseractOnly | ocr.Configuration.PageSegmentationMode |
手動 tessdata .traineddata ファイル | dotnet add package IronOcr.Languages.French |
TessDataPath 定数 + .csproj コピーエントリ | 該当なし - バンドル |
Leptonica DLL 経由の Pix.LoadFromFile() | input.LoadImage() - ネイティブ DLL は不要 |
プラットフォーム GetTessDataPath() メソッド | 該当なし - 削除済み |
leptonica-1.82.0.dll / libleptonica-dev | 該当なし - Leptonicaへの依存なし |
| TIFF の一時ファイルフレームの手動抽出 | input.LoadImageFrames(tiffPath) |
| 検索可能なPDF出力はありません | result.SaveAsSearchablePdf(outputPath) |
スレッドごとの new TesseractEngine() | 1つの IronTesseract - スレッドセーフ |
一般的な移行の問題と解決策
問題1:LeptonicaまたはTesseractバイナリでDllNotFoundExceptionが発生する
charlesw Tesseract: System.DllNotFoundException: Unable to load DLL 'leptonica-1.82.0': The specified module could not be found. Leptonica ネイティブ DLL が期待される場所にないときにこの例外が発生します。 これは、新しい Docker コンテナ、CI エージェント、または NuGet パッケージの runtimes/ フォルダーが正しくコピーされなかった任意の環境で一般的です。
解決策: Tesseract パッケージを削除します。 IronOcr をインストールします。 IronOCRはすべてのネイティブバイナリを内部的にバンドルしており、システムLeptonicaへのP/Invokeは行いません。 外部のLeptonica依存関係が存在しないため、例外は発生しません。
apt-get install libleptonica-dev は不要です。 ネイティブ DLL 用の <CopyToOutputDirectory> エントリはありません。
問題2:デプロイ後のTessdataパスエラー
charlesw Tesseract: Tesseract.TesseractException: Failed to initialise tesseract engine. これは TessDataPath が実行時に解決されないときに発生します。エラーなしでコンパイルされ、実行時にのみ失敗し、失敗パスは展開環境に依存します。
解決策: IronOCRにはtessdataパスの概念は存在しません。 定数を削除し、.csproj のCopyToOutputDirectory XML を削除し、それを構築するファクトリメソッドを削除します。 言語データはNuGetパッケージとして配布されます。
# Replace this manual tessdata file management:
# tessdata/eng.traineddata (15 MB, manually downloaded)
# tessdata/fra.traineddata (15 MB, manually downloaded)
# .csproj <CopyToOutputDirectory> entry
# With NuGet packages:
dotnet add package IronOcr.Languages.French
多言語ガイドでは、言語パッケージを追加した後に多言語認識を設定する方法を説明しています。
問題3:ベースイメージの更新時にコンテナのビルドが失敗する
charlesw Tesseract: Dockerfile は Leptonica ネイティブ依存を満たすために apt-get install -y libleptonica-dev を含みます。 ベースイメージがDebian BullseyeからBookwormに移行した場合、またはLeptonicaパッケージ名がディストリビューション間で変更された場合、ビルドはaptエラーで失敗します。 この問題を解決するには、新しいディストリビューションでどのパッケージ名を使用すべきかを知る必要があります。
解決策: Leptonica apt-get 行を完全に削除します。 Linux 上の IronOCR は、すでに System.Drawing を使用する .NET アプリケーションが必要とする標準的な libgdiplus パッケージのみ必要です:
# Before: Leptonica explicit install — breaks on base image updates
RUN apt-get update && apt-get install -y libleptonica-dev
# After: standard .NET Linux requirement only
RUN apt-get update && apt-get install -y libgdiplus
Dockerデプロイメントガイドには、一般的なベースイメージ用のテスト済みDockerfileテンプレートが掲載されています。 Charlesw固有のインフラストラクチャコードは必要ありません。
問題4:イテレータパターンが空ページまたは空白ページで途切れる
charlesw Tesseract: ResultIterator は null を特定のページセグメントから返し、ループ全体で明示的な null チェックを必要とします。null チェックを見落とすと、空白ページや認識可能なテキストがない画像で NullReferenceException を引き起こします。
解決策: IronOCRの結果コレクションは決してnullになりません。 空のページからは空のコレクションが返されます。 null参照ではなく、テキストコンテンツをチェックする。
// Before: null checks required at every iterator level
var wordText = iter.GetText(PageIteratorLevel.Word);
if (wordText != null && wordText.Trim().Length > 0)
results.Add(wordText.Trim());
// After: collection is safe to enumerate; check content as needed
foreach (var word in result.Pages.SelectMany(p => p.Lines).SelectMany(l => l.Words))
{
if (!string.IsNullOrWhiteSpace(word.Text))
results.Add(word.Text);
}// Before: null checks required at every iterator level
var wordText = iter.GetText(PageIteratorLevel.Word);
if (wordText != null && wordText.Trim().Length > 0)
results.Add(wordText.Trim());
// After: collection is safe to enumerate; check content as needed
foreach (var word in result.Pages.SelectMany(p => p.Lines).SelectMany(l => l.Words))
{
if (!string.IsNullOrWhiteSpace(word.Text))
results.Add(word.Text);
}' Before: null checks required at every iterator level
Dim wordText As String = iter.GetText(PageIteratorLevel.Word)
If wordText IsNot Nothing AndAlso wordText.Trim().Length > 0 Then
results.Add(wordText.Trim())
End If
' After: collection is safe to enumerate; check content as needed
For Each word In result.Pages.SelectMany(Function(p) p.Lines).SelectMany(Function(l) l.Words)
If Not String.IsNullOrWhiteSpace(word.Text) Then
results.Add(word.Text)
End If
Next' Before: null checks required at every iterator level
Dim wordText As String = iter.GetText(PageIteratorLevel.Word)
If wordText IsNot Nothing AndAlso wordText.Trim().Length > 0 Then
results.Add(wordText.Trim())
End If
' After: collection is safe to enumerate; check content as needed
For Each word In result.Pages.SelectMany(Function(p) p.Lines).SelectMany(Function(l) l.Words)
If Not String.IsNullOrWhiteSpace(word.Text) Then
results.Add(word.Text)
End If
Next問題5:負荷がかかった状態でのねじの安全違反
charlesw Tesseract: TesseractEngine はスレッドセーフではありません。 ASP.NETアプリケーションで、複数の同時リクエスト間で1つのインスタンスを共有すると、アクセス違反や結果の破損が発生します。 標準的な解決策は、スレッドごとに1つのエンジンを作成することですが、これはAPIからは明らかではなく、問題が発生した場合のエラーメッセージは難解なネイティブ例外です。
解決策: IronTesseract はスレッドセーフです。 1つのインスタンスが同時リクエストを処理できるか、または最大スループットのためには Parallel.ForEach 内で各スレッドごとに1つ作成するのが良いです。どちらのパターンも変更なしで機能します。
// Thread-safe parallel processing — IronTesseract handles concurrent access
var results = new System.Collections.Concurrent.ConcurrentBag<string>();
Parallel.ForEach(imageFiles, imagePath =>
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
results.Add(result.Text);
});// Thread-safe parallel processing — IronTesseract handles concurrent access
var results = new System.Collections.Concurrent.ConcurrentBag<string>();
Parallel.ForEach(imageFiles, imagePath =>
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
results.Add(result.Text);
});Dim results As New System.Collections.Concurrent.ConcurrentBag(Of String)()
Parallel.ForEach(imageFiles, Sub(imagePath)
Dim ocr = New IronTesseract()
Dim result = ocr.Read(imagePath)
results.Add(result.Text)
End Sub)Dim results As New System.Collections.Concurrent.ConcurrentBag(Of String)()
Parallel.ForEach(imageFiles, Sub(imagePath)
Dim ocr = New IronTesseract()
Dim result = ocr.Read(imagePath)
results.Add(result.Text)
End Sub)非同期OCRガイドでは、スレッドブロッキングが許容されないASP.NET Coreコントローラー向けの非同期パターンについて説明します。
問題6:実行時にARM64バイナリが見つからない
Charlesw Tesseract: AWS Graviton (Linux ARM64) または Apple Silicon CI エージェントでは、アーカイブされたパッケージに ARM64 ネイティブバイナリが含まれていない場合があります。 失敗はエンジン作成時の DllNotFoundException または BadImageFormatException であり、パッケージがサポートする経路を持たないプラットフォーム上での実行時エラーです。
解決策: IronOCRは、LinuxとmacOSの両方に対応した検証済みのARM64バイナリを提供しています。 コード変更なしでARM64環境にデプロイできます。 Linux導入ガイドとmacOS導入ガイドには、サポートされているランタイム識別子が記載されています。
チャールズ・テッセラクト 移行チェックリスト
移行前
コードベースを監査して、変更されるすべてのパターンを特定する。
# Find all references to Tesseract namespace (engine creation, Pix usage, iterator usage)
grep -rn "using Tesseract" --include="*.cs" .
# Find TesseractEngine instantiation points
grep -rn "TesseractEngine" --include="*.cs" .
# Find Pix usage (Leptonica image type)
grep -rn "Pix\." --include="*.cs" .
# Find tessdata path constants and methods
grep -rn "tessdata\|TessDataPath\|traineddata" --include="*.cs" .
# Find platform-conditional deployment code
grep -rn "IsOSPlatform\|DOTNET_RUNNING_IN_CONTAINER\|LD_LIBRARY_PATH" --include="*.cs" .
# Find iterator pattern usage
grep -rn "GetIterator\|ResultIterator\|PageIteratorLevel" --include="*.cs" .
# Find confidence calls
grep -rn "GetMeanConfidence\|GetConfidence" --include="*.cs" .
# Find .csproj tessdata copy entries
grep -rn "tessdata" --include="*.csproj" .
プロジェクトが対象とするデプロイ環境(Docker、Linux、ARM64、Azure、AWS)に注意してください。これらは、charlesw/tesseractが最も多くの設定を必要とする環境であり、 IronOCRはそれらの設定を不要にします。
コードの移行
- charlesw ラッパーをアンインストールするために
dotnet remove package Tesseractを実行します - IronOCR をインストールするために
dotnet add package IronOcrを実行します - アプリケーション起動時に
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";を追加します - すべての
using Tesseract;文をusing IronOcr;に置き換えます - tessdata パス定数と、環境ごとにパスを構築するメソッドを削除します。
- tessdata パス選択のために書かれたすべての
RuntimeInformation.IsOSPlatform()ブロックを削除します - tessdata ファイルの
<CopyToOutputDirectory>エントリをすべての.csprojファイルから削除します - ソース管理や展開アーティファクトストアから tessdata
.traineddataファイルを削除します - 以前に
.traineddataファイルとして展開された各言語のためにdotnet add package IronOcr.Languages.*を追加します TesseractEngine+Pix.LoadFromFile()+engine.Process()チェーンをnew IronTesseract().Read()に置き換えます- すべての
Pix.LoadFromFile()およびPix.LoadFromMemory()呼び出しをinput.LoadImage()に置き換えます - すべての
page.GetText()呼び出しをresult.Textに置き換えます - イテレータに基づいた単語/行抽出を
result.Pagesの直接コレクションアクセスに置き換えます iter.GetConfidence()しきい値ロジックをresult.Wordsまたはresult.Lines上の LINQ に置き換えます- Dockerfile およびデプロイメントスクリプトから
libleptonica-dev/leptonica-1.82.0.dllを削除します
移行後
コードの更新が完了したら、以下の点を確認してください。
- OCRはWindows上でネイティブDLLエラーなしで正常に動作します
- OCR は、
libgdiplusを超えてapt-getの変更なしに Docker Linux コンテナで成功裏に実行されます - OCRは、ARM64プラットフォームが展開マトリックスに含まれている場合、そのプラットフォーム上でテキスト出力を生成します。
- 複数ページのTIFFファイルは、最初のフレームだけでなく、すべてのフレームのテキストを返します。
- 信頼度フィルタリングは、以前のイテレータ実装と同じ、信頼度の高い単語の論理セットを返します。
- 言語固有のドキュメント(フランス語、ドイツ語など)は、言語NuGetパッケージのインストール後に正しく認識されます。
- 並列OCR処理は例外や出力の破損なく完了する
- 以前の実装ではテキストのみが返されていたところ、検索可能なPDF出力が生成されます。
- CI/CDパイプラインは、tessdataのダウンロード手順やLeptonicaのインストールコマンドなしでビルドされます。
- 前回の実装の検証に使用したのと同じ画像コーパスに対してスモークテストを実施する。
IronOCRへの移行の主なメリット
自己完結型デプロイメントモデル。移行後、OCRの依存関係は単一のNuGetパッケージ参照によって完全に記述されます。 ソース管理には tessdata ファイルがなく、CopyToOutputDirectory エントリがなく、ネイティブ DLL 配布手順もなく、Leptonica システムパッケージもありません。 以前は複数ステップのアーティファクト管理が必要だった CI/CD パイプラインが dotnet publish に簡略化されます。 charleswラッパーをサポートするために蓄積されていたデプロイメント関連のコードは、完全に削除されました。
条件ロジックなしでプラットフォーム間の互換性を実現。同じアプリケーションバイナリが、Windows x64、Linux x64、Linux ARM64、macOS x64、およびmacOS ARM64で変更なしに動作します。 AWS Graviton、Apple Silicon CI、Raspberry Piなど、ARM64デプロイメントターゲットを追加するチームは、新しいプラットフォーム検出コードを記述する必要はありません。 Linux導入ガイドとAWS導入ガイドは、テスト済みの構成を裏付けています。
Tesseract 5の精度向上:内蔵プリプロセス機能搭載。Tesseract 4.1.1からTesseract 5へのアップグレードにより、劣化文書の認識精度が向上しました。 IronOCRは、エンジンのアップグレードに加えて自動前処理機能を追加し、エンジンが各画像を処理する前に、傾き補正、ノイズ除去、コントラスト正規化、および二値化を適用します。 以前は許容できる精度基準を満たすためにカスタムの前処理パイプラインが必要だった文書も、追加のコードなしでその基準を満たすことができるようになった。 画像品質補正ガイドでは、デフォルト設定以上の調整が必要な場合の具体的な前処理オプションについて説明しています。
直接結果ナビゲーションはイテレータのボイラープレートを置き換えます。 charlesw イテレータパターン - GetIterator(), Begin(), Next(), IsAtBeginningOf(), null チェックなど - はシンプルなコレクションによって置き換えられます。 単語、行、段落、ページは、結果オブジェクトのプロパティです。 信頼度に基づくフィルタリングは、LINQ 式です。 以前は、単語レベルのデータを抽出するコードには、15~30行のイテレータ管理が必要だった。 IronOCRでいうと、2~3行に相当します。 OCR結果機能ページでは、構造化された出力モデル全体を要約しています。
二次ライブラリなしの検索可能な PDF 出力。 result.SaveAsSearchablePdf() は認識された単語に合わせたテキストレイヤーを持つ PDF を生成し、二次的な PDF ライブラリを必要としません。 検索可能なPDFを取り込む文書管理システムでは、別途PDF生成ステップは不要になりました。抽出されたテキストを提供する結果オブジェクトが検索可能なファイルも書き込むため、文書処理パイプラインは単一のライブラリ依存関係で済みます。
アクティブなメンテナンスとセキュリティパッチ適用範囲。IronOCRは、Tesseract 5モデルの改善、 .NETランタイム互換性の検証、および基盤となるC++エンジンのセキュリティパッチ適用範囲を追跡する定期的なアップデートを受け取ります。この依存関係は、アーカイブされたパッケージのようなリスクプロファイルを持たなくなったため、コンプライアンスレビューでセキュリティパッチパスの欠落に関する問題が指摘されることはありません。 .NET 10が2026年までに一般提供開始されるにつれて、 IronOCRのドキュメントハブは、回避策やフォークを必要とせずに、現在の互換性を反映したものとなるでしょう。
関連する記事


