Migrating from Charlesw Tesseract to IronOCR
MindeeからIronOCRへの移行により、文書処理が外部のクラウドサーバーから自社のインフラストラクチャに移行されるため、ページごとの課金、サードパーティへのデータ送信、および実行時のネットワーク可用性への依存が解消されます。 以下のセクションでは、パッケージの置き換え、名前空間の更新、および請求書ゾーンの抽出、複数ページの領収書PDFの処理、財務文書の前処理、検索可能なPDFアーカイブという4つの実用的なコード変更前後のシナリオについて説明します。
Mindeeから移行する理由
Mindeeは、文書を送信すると構造化されたJSONフィールドを受け取るという、現実的な問題を巧みに解決します。 財務書類を外部APIに送信することが許容され、かつ送信量が中程度にとどまるチームにとっては、迅速な結果が得られます。 移行のトリガーは通常、いくつかの条件のうちの1つが変化したときに発生します。
金融データがコンプライアンスの境界を越えます。 ミンディー の ParseAsync<InvoiceV4> はドキュメント全体を外部サーバーにアップロードします。 IBANコード、ルーティング番号、ベンダーEIN、および明細項目は、公共のインターネットを経由して送信され、お客様の管理下にないハードウェアで処理されます。 SOC 2 Type II認証は、Mindeeがセキュリティ対策を遵守していることを証明するものです。 それは、あなたの文書をセキュリティ境界内に保持しません。 コンプライアンス審査、新規顧客契約、またはデータ所在地の規制において、第三者によるクラウド処理が制限される場合、Mindeeのアーキテクチャは、抽出精度に関わらず、不適格とみなされる。
**ページあたりの料金は、利用状況に応じて変動します。**スタータープランでは、月額49ドルで月間1,000ページまで利用できます。 プロプランは月額499ドルで、月間5,000ページまで利用できます。 複数ページのPDFファイルは、すべてのページをカウントします。 開発実行回数は割り当て量にカウントされます。 月に4,000件の請求書を処理するチームの場合、月額499ドル(年間5,988ドル)を支払うことになり、この金額は毎年リセットされ、処理量の増加に応じて上昇します。 月間24,000ページという利用量であれば、Enterprise価格の交渉となります。 IronOCRの永久ライセンス(Professional版)は1,499ドルで、文書の容量に制限がなく、Mindee Proと比較して4ヶ月以内に費用を回収できます。
非同期ポーリングはスタック全体に伝播します。Mindeeのすべての操作はネットワークリクエストであるため、非同期です。この必須の非同期チェーンは、コントローラー、サービスレイヤー、ワーカークラスを通して上位に伝播します。 ネットワークが利用できない場合、またはMindeeのサービスに障害が発生した場合、連鎖的に障害が発生し、ローカルでの代替手段がなくなります。 IronOCR は同期処理を行い、ローカル CPU 作業を実行し、Task.Run を使用して非同期インタフェースが設計の一貫性のために必要な場合にラップします。
事前作成済みの API カタログには限界があります。 ミンディー は InvoiceV4, ReceiptV5, PassportV1 をカバーしており、他のドキュメントタイプの小さなカタログがあります。そのリスト外のドキュメントタイプには、エンタープライズプランのカスタムモデルトレーニングが必要であり、サンプル収集、ラベリング、トレーニング時間、API が利用可能になるまでのリードタイムが発生します。 貴社が必要とする新しい文書の種類ごとに、個別のベンダーとの契約が必要となります。 IronOCR は同じ IronTesseract().Read() コールを使用してどのドキュメントタイプも処理できます。 新しい文書タイプを追加するということは、抽出パターンを作成するということであり、研修契約を交渉するということではない。
APIキー管理により運用上の制約が緩和されます。Mindeeの認証情報は、サーバー側で安全に保管し、定期的に更新し、外部への漏洩から保護する必要があります。 ネットワーク送信ルールでは、MindeeエンドポイントへのHTTPS送信を許可する必要があります。 再試行ロジックは HttpRequestException と MindeeException を処理する必要があります。これは避けられないネットワーク障害です。 Mindeeを削除すると、その運用上のあらゆる側面が失われます。認証情報も、送信ルールも、ネットワークI/Oを囲む再試行ラッパーもなくなります。
**エアギャップ環境および制限された環境は対象外です。**政府請負業者、防衛関連下請け業者、厳格なインターネット発信制限のある医療ネットワーク、および信頼性の高いインターネット接続のない施設に導入された産業システムは、設計どおりにMindeeを使用することはできません。 IronOCRは、アウトバウンドアクセスなしのDockerコンテナ、送信制限付きのAzure Functions、および完全にエアギャップされた環境のいずれにおいても、全く同じように動作します。 実行時にクラウドへの依存はありません。デプロイメントティアの詳細については、 IronOCRのライセンスページをご覧ください。
基本的な問題
Mindeeが処理するすべての財務文書は、あなたが制御できないネットワーク境界を越えます。
// Mindee: invoice with bank details leaves your infrastructure on this line
var response = await _client.ParseAsync<InvoiceV4>(new LocalInputSource("invoice.pdf"));
// IBAN, routing number, EIN, line items — all transmitted to ミンディー cloud
// IronOCR: same invoice, same result, zero data transmission
var result = new IronTesseract().Read("invoice.pdf");
// All processing local — bank details never leave your machine
IronOCRとMindee:機能比較
以下の表は、移行の意思決定を左右するアーキテクチャと機能の違いをまとめたものです。
| フィーチャー | ミンディー | IronOCR |
|---|---|---|
| 処理場所 | Mindeeクラウドサーバー | ローカルマシン / インフラストラクチャ |
| 実行時にインターネット接続が必要 | いつも | 一度もない |
| データ伝送 | 通話ごとに完全なドキュメントがアップロードされます | None |
| 文書ごとの費用 | はい(ページ単位、プラン別) | いいえ(永久ライセンス) |
| 開始価格 | 月額49ドル(1,000ページ) | $999 ワンタイム (Lite) |
| オフライン/エアギャップ対応 | サポートされていません | フルサポート |
| オンプレミス展開 | 不可 | 唯一の選択肢 |
| 請求書の解析 | 事前作成済みの構造化 API (InvoiceV4) | OCR + 領域ベースまたは正規表現による抽出 |
| レシート解析 | 事前作成済みの構造化 API (ReceiptV5) | OCR + 抽出パターン |
| 任意の文書タイプ | カスタムトレーニングなしではサポートされません | 完全サポート、あらゆる文書 |
| カスタムモデルトレーニング | Enterprise向けプラン+リードタイム | 不要 |
| API呼び出しパターン | 非同期処理は必須です(ネットワークI/O)。 | 同期(非同期はオプション) |
| 律速段階 | プラン依存 | None |
| PDF入力 | はい | はい(ネイティブ、変換なし) |
| 複数ページPDFの処理 | はい | はい |
| 検索可能なPDF出力 | なし | はい (result.SaveAsSearchablePdf()) |
| 画像前処理 | 露出していない | 傾き補正、ノイズ除去、コントラスト調整、二値化、シャープ化、スケール調整 |
| 領域ベースの抽出 | 応答としてフィールド座標 | 入力時に CropRectangle |
| 125以上の言語に対応 | 制限的 | はい(バンドル版、tessdata管理なし) |
| バーコード読み取り | なし | はい(OCRと同時) |
| 構造化された単語座標 | なし | はい(ページ数、段落数、行数、単語数) |
| スレッドセーフ。 | 該当なし(呼び出しごとのネットワークI/O) | 内蔵 |
| クロスプラットフォーム | クラウド(プラットフォーム非依存) | Windows、Linux、macOS、Docker、Azure、AWS |
| .NET互換性 | .NET Standard 2.0以降 | .NET Framework 4.6.2以降、 .NET 5/6/7/8/9 |
| 商業サポート | はい | はい |
クイックスタート:MindeeからIronOCRへの移行
ステップ 1: NuGet パッケージを置き換える
Mindeeパッケージを削除してください。
dotnet remove package Mindee
NuGetからIronOCRをインストールしてください。
ステップ 2: 名前空間の更新
Mindee名前空間ブロックをIronOCR名前空間に置き換えてください。
// Before (Mindee)
using Mindee;
using Mindee.Input;
using Mindee.Product.Invoice;
using Mindee.Product.Receipt;
using Mindee.Product.FinancialDocument;
// After (IronOCR)
using IronOcr;
ステップ 3: ライセンスの初期化
アプリケーションの起動時(最初のOCR呼び出し前)にライセンスキーを追加してください。
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"コード移行の例
文書ゾーンを使用した請求書フィールドの抽出
Mindeeは、サーバー側のモデルが請求書番号、日付、合計金額が通常表示される場所を把握しているため、既知の場所に事前に解析されたフィールドを返します。 IronOCR の同等の機能では、ドキュメントの特定のゾーンを読み取り、Mindee の抽出の空間認識を一致させつつ、ドキュメントを送信せずに CropRectangle を使用します。
Mindeeのアプローチ:
using Mindee;
using Mindee.Input;
using Mindee.Product.Invoice;
public class MindeeZoneExtractor
{
private readonly MindeeClient _client;
public MindeeZoneExtractor(string apiKey)
{
_client = new MindeeClient(apiKey);
}
public async Task<(string invoiceNumber, string supplierName, decimal? total)>
ExtractKeyFieldsAsync(string filePath)
{
// Document transmitted to ミンディー — spatial field detection happens server-side
var inputSource = new LocalInputSource(filePath);
var response = await _client.ParseAsync<InvoiceV4>(inputSource);
var prediction = response.Document.Inference.Prediction;
// Fields arrive pre-parsed; no zone configuration required on your end
return (
prediction.InvoiceNumber?.Value,
prediction.SupplierName?.Value,
prediction.TotalAmount?.Value
);
}
}
IronOCRのアプローチ:
using IronOcr;
using System.Text.RegularExpressions;
public class IronOcrZoneExtractor
{
private readonly IronTesseract _ocr = new IronTesseract();
public (string invoiceNumber, string supplierName, string total)
ExtractKeyFields(string filePath)
{
// Zone 1: supplier name — top 12% of document, full width
var headerRegion = new CropRectangle(0, 0, 850, 120);
using var headerInput = new OcrInput();
headerInput.LoadImage(filePath, headerRegion);
var supplierResult = _ocr.Read(headerInput);
// Zone 2: invoice number and date — upper-right quadrant
var metaRegion = new CropRectangle(450, 80, 400, 100);
using var metaInput = new OcrInput();
metaInput.LoadImage(filePath, metaRegion);
var metaResult = _ocr.Read(metaInput);
// Zone 3: totals block — bottom-right 20%
var totalsRegion = new CropRectangle(500, 800, 350, 200);
using var totalsInput = new OcrInput();
totalsInput.LoadImage(filePath, totalsRegion);
var totalsResult = _ocr.Read(totalsInput);
var invoiceNumber = Regex.Match(
metaResult.Text,
@"Invoice\s*#?\s*:?\s*([A-Z0-9\-]+)",
RegexOptions.IgnoreCase).Groups[1].Value;
var total = Regex.Match(
totalsResult.Text,
@"Total\s*(?:Due)?\s*:?\s*\$?([\d,]+\.?\d*)",
RegexOptions.IgnoreCase).Groups[1].Value;
// Supplier name is the first substantial text line in the header zone
var supplierName = supplierResult.Text
.Split('\n')
.FirstOrDefault(l => l.Trim().Length > 4)
?.Trim();
return (invoiceNumber, supplierName, total);
}
}
ゾーンベースOCRは、Mindeeの空間フィールド検出の意図と一致しています。つまり、ページ全体に正規表現を適用するのではなく、文書の特定の領域を読み取るということです。 CropRectangle(x, y, width, height) はピクセル座標を取るので、調整には代表的なサンプル請求書と比較する必要があります。地域別 OCR ガイド は座標の測定とゾーン重複戦略をカバーしています。 レイアウトが変動する請求書には、result.Text 上での完全ページの正規表現とゾーン抽出を組み合わせることで、最も信頼性の高い結果が得られます。
複数ページにわたる経費報告書(PDF形式)の処理
MindeeはPDFファイルを受け取り、各ページを個別の文書単位として処理し、ページごとに構造化された結果を返します。 IronOCR はネイティブに OcrInput.LoadPdf() を通じて複数ページの PDF を読み取り、単一の呼び出しですべてのページを処理し、ページごとのコンテンツと単語の座標を含む result.Pages を返します。
Mindeeのアプローチ:
using Mindee;
using Mindee.Input;
using Mindee.Product.Receipt;
public class MindeeExpenseReportProcessor
{
private readonly MindeeClient _client;
public MindeeExpenseReportProcessor(string apiKey)
{
_client = new MindeeClient(apiKey);
}
public async Task<List<ExpenseSummary>> ProcessExpenseReportAsync(string pdfPath)
{
var summaries = new List<ExpenseSummary>();
// ミンディー processes per-document; each page of a PDF is a separate upload
// For a 10-page expense report: 10 API calls, 10 pages billed
var inputSource = new LocalInputSource(pdfPath);
var response = await _client.ParseAsync<ReceiptV5>(inputSource);
var prediction = response.Document.Inference.Prediction;
summaries.Add(new ExpenseSummary
{
MerchantName = prediction.SupplierName?.Value,
Date = prediction.Date?.Value?.ToString(),
TotalAmount = prediction.TotalAmount?.Value ?? 0m,
Category = prediction.Category?.Value
});
return summaries;
}
}
IronOCRのアプローチ:
using IronOcr;
using System.Text.RegularExpressions;
public class IronOcrExpenseReportProcessor
{
private readonly IronTesseract _ocr = new IronTesseract();
public List<ExpenseSummary> ProcessExpenseReport(string pdfPath)
{
// Load entire multi-page PDF in one call — no per-page upload
using var input = new OcrInput();
input.LoadPdf(pdfPath); // all pages loaded locally
var result = _ocr.Read(input);
var summaries = new List<ExpenseSummary>();
// Each page maps to one receipt in the expense report
foreach (var page in result.Pages)
{
var pageText = page.Text;
// Skip pages without receipt content
if (!pageText.Contains("Total", StringComparison.OrdinalIgnoreCase))
continue;
var merchantName = page.Lines
.FirstOrDefault(l => l.Text.Trim().Length > 4)
?.Text.Trim();
var totalMatch = Regex.Match(
pageText,
@"(?:Total|Amount\s+Due|Grand\s+Total)\s*:?\s*\$?([\d,]+\.\d{2})",
RegexOptions.IgnoreCase);
var dateMatch = Regex.Match(
pageText,
@"\b(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})\b");
var categoryMatch = Regex.Match(
pageText,
@"(?:Category|Dept|Department)\s*:?\s*(.+)",
RegexOptions.IgnoreCase);
summaries.Add(new ExpenseSummary
{
PageNumber = page.PageNumber,
MerchantName = merchantName,
Date = dateMatch.Success ? dateMatch.Value : null,
TotalAmount = totalMatch.Success
? decimal.Parse(totalMatch.Groups[1].Value.Replace(",", ""))
: 0m,
Category = categoryMatch.Success
? categoryMatch.Groups[1].Value.Trim()
: null,
Confidence = page.Words.Any()
? page.Words.Average(w => (double)w.Confidence)
: 0
});
}
return summaries;
}
}
public class ExpenseSummary
{
public int PageNumber { get; set; }
public string MerchantName { get; set; }
public string Date { get; set; }
public decimal TotalAmount { get; set; }
public string Category { get; set; }
public double Confidence { get; set; }
}
Mindeeを使用して10ページの経費報告書PDFを処理すると、10回のAPI呼び出しと10ページの課金対象ページが生成されます。 IronOCRは、ページごとのコストやページごとのネットワーク往復通信なしに、同じファイルを1回のローカル呼び出しで処理します。 result.Pages コレクションは、ページごとのテキスト、単語レベルのバウンディングボックス、レイアウトを認識するためのライン位置を提供します。 パスワードで保護されたPDFファイルの読み込みとページ範囲の選択については、 PDF入力ガイドを参照してください。また、単語座標の操作については、構造化された読み取り結果ガイドを参照してください。
スキャン済み請求書の前処理パイプライン
Mindeeのクラウド処理では、フィールド抽出を実行する前に独自の画像正規化処理が適用されます。 その正規化の品質は不透明です。正規化を制御することも、どのような前処理が実行されたかを把握することもできません。 スキャンした請求書に歪み、影、またはコントラストの低さがある場合、Mindeeは信頼度スコアを低く表示し、提出前にスキャンを改善する仕組みはありません。 IronOCRは前処理パイプラインを明示的に公開しているため、認識を実行する前に、特定の文書に必要な修正を正確に適用できます。
Mindeeのアプローチ:
using Mindee;
using Mindee.Input;
using Mindee.Product.Invoice;
public class MindeeScanProcessor
{
private readonly MindeeClient _client;
public MindeeScanProcessor(string apiKey)
{
_client = new MindeeClient(apiKey);
}
public async Task<ScanResult> ProcessScannedInvoiceAsync(string scanPath)
{
// ミンディー applies internal normalization — no developer control over preprocessing
var inputSource = new LocalInputSource(scanPath);
var response = await _client.ParseAsync<InvoiceV4>(inputSource);
var prediction = response.Document.Inference.Prediction;
return new ScanResult
{
InvoiceNumber = prediction.InvoiceNumber?.Value,
Total = prediction.TotalAmount?.Value,
// Per-field confidence: only proxy for scan quality
InvoiceNumberConfidence = prediction.InvoiceNumber?.Confidence,
TotalConfidence = prediction.TotalAmount?.Confidence
};
}
}
IronOCRのアプローチ:
using IronOcr;
using System.Text.RegularExpressions;
public class IronOcrScanProcessor
{
private readonly IronTesseract _ocr = new IronTesseract();
public ScanResult ProcessScannedInvoice(string scanPath)
{
// First pass: read without preprocessing
var quickResult = _ocr.Read(scanPath);
OcrResult result;
if (quickResult.Confidence < 75)
{
// Low confidence — apply full preprocessing pipeline
using var input = new OcrInput();
input.LoadImage(scanPath);
input.Deskew(); // correct page rotation up to ±45 degrees
input.DeNoise(); // remove scanner artifacts and speckle
input.Contrast(); // normalize contrast for faded or overexposed scans
input.Sharpen(); // sharpen blurred text edges
result = _ocr.Read(input);
}
else
{
result = quickResult;
}
var invoiceNumber = Regex.Match(
result.Text,
@"Invoice\s*(?:Number|#|No\.?)?\s*:?\s*([A-Z0-9\-]+)",
RegexOptions.IgnoreCase).Groups[1].Value;
var total = Regex.Match(
result.Text,
@"(?:Total|Amount\s+Due)\s*:?\s*\$?([\d,]+\.\d{2})",
RegexOptions.IgnoreCase).Groups[1].Value;
return new ScanResult
{
InvoiceNumber = invoiceNumber,
Total = total,
DocumentConfidence = result.Confidence,
PreprocessingApplied = quickResult.Confidence < 75
};
}
}
public class ScanResult
{
public string InvoiceNumber { get; set; }
public string Total { get; set; }
public double DocumentConfidence { get; set; }
public bool PreprocessingApplied { get; set; }
}
2段階のパターン(まず高速読み取りを行い、信頼度の低いデータに対して前処理を行う)により、クリーンなスキャンデータに対する完全な前処理のオーバーヘッドを回避できます。 アカウント支払いで一般的なスキャン品質のシナリオ(わずかに歪んだフラットベッドスキャン、ファクスのアーティファクト、コピーされた請求書)には、Deskew() と DeNoise() が一緒になって認識精度のほとんどを回復します。 画像品質補正ガイドには、利用可能なすべてのフィルターが記載されており、さまざまなスキャン欠陥に対してどの組み合わせが最適かに関するガイダンスが提供されています。 画像方向補正ガイドでは、上下逆さまにスキャンされた文書の自動回転について説明しています。
財務文書の検索可能なPDFアーカイブ
MindeeはPDF出力を生成しません。 そのアーキテクチャは入力専用です。ドキュメントを送信すると、JSONフィールドが返されます。 スキャンした請求書を検索可能な形式でアーカイブするチームは、そのパイプラインをMindeeによる抽出とは別に維持する必要があります。 IronOCRは、フィールド抽出に使用した認識パスから直接検索可能なPDFを生成します。追加のライブラリや2段階の処理は不要です。
Mindeeのアプローチ:
using Mindee;
using Mindee.Input;
using Mindee.Product.Invoice;
public class MindeeArchiveProcessor
{
private readonly MindeeClient _client;
public MindeeArchiveProcessor(string apiKey)
{
_client = new MindeeClient(apiKey);
}
public async Task ArchiveInvoiceAsync(string scanPath, string archivePath)
{
// Extract fields via ミンディー (document transmitted to cloud)
var inputSource = new LocalInputSource(scanPath);
var response = await _client.ParseAsync<InvoiceV4>(inputSource);
var prediction = response.Document.Inference.Prediction;
// ミンディー returns no PDF output — searchable PDF requires a separate library
// (e.g., iTextSharp, PdfSharp, or a separate OCR library)
// The scan at scanPath is the only PDF available for archiving;
// it remains image-only with no embedded text layer
Console.WriteLine($"Fields extracted. Searchable PDF: not available from Mindee.");
Console.WriteLine($"Invoice: {prediction.InvoiceNumber?.Value}");
}
}
IronOCRのアプローチ:
using IronOcr;
using System.Text.RegularExpressions;
public class IronOcrArchiveProcessor
{
private readonly IronTesseract _ocr = new IronTesseract();
public ArchiveResult ArchiveInvoice(string scanPath, string archiveOutputPath)
{
// Apply preprocessing before recognition and archiving
using var input = new OcrInput();
input.LoadPdf(scanPath); // works with both PDF scans and image files
input.Deskew();
input.DeNoise();
var result = _ocr.Read(input);
// Extract fields from the same recognition pass
var invoiceNumber = Regex.Match(
result.Text,
@"Invoice\s*#?\s*:?\s*([A-Z0-9\-]+)",
RegexOptions.IgnoreCase).Groups[1].Value;
var vendor = result.Pages.FirstOrDefault()?.Lines
.FirstOrDefault(l => l.Text.Trim().Length > 4)
?.Text.Trim();
var totalMatch = Regex.Match(
result.Text,
@"Total\s*(?:Due)?\s*:?\s*\$?([\d,]+\.\d{2})",
RegexOptions.IgnoreCase);
// Write searchable PDF to archive in one call — no separate library needed
result.SaveAsSearchablePdf(archiveOutputPath);
return new ArchiveResult
{
InvoiceNumber = invoiceNumber,
VendorName = vendor,
Total = totalMatch.Success ? totalMatch.Groups[1].Value : null,
ArchivePath = archiveOutputPath,
DocumentConfidence = result.Confidence,
PageCount = result.Pages.Count()
};
}
}
public class ArchiveResult
{
public string InvoiceNumber { get; set; }
public string VendorName { get; set; }
public string Total { get; set; }
public string ArchivePath { get; set; }
public double DocumentConfidence { get; set; }
public int PageCount { get; set; }
}
result.SaveAsSearchablePdf() は出力 PDF に認識されたテキストレイヤーを直接埋め込み、任意の PDF ビューアーや企業コンテンツ管理システムで各単語を選択可能かつ検索可能にします。 フィールド抽出とアーカイブは同じパスで実行されます --- 単一の _ocr.Read(input) 呼び出しでパターンマッチング用の result.Text とアーカイブ用の result.SaveAsSearchablePdf() の両方を提供します。サーチャブル PDF ガイド は HOCR エクスポートを含む出力オプションをカバーし、PDF OCR ユースケースページ はドキュメントアーカイブ用パイプラインのための生産展開パターンをカバーしています。
FinancialDocumentV1カスタムエンドポイントの削除
Mindee の FinancialDocumentV1 API は請求書とレシートの両方をドキュメントタイプを自動検出して処理します。 これを利用するチームは、分岐ロジックを排除できる反面、クラウドへの依存度が高まるという代償を払うことになる。つまり、ドキュメントの種類に関係なく、すべてのドキュメントがアップロードされる。 IronOCRに置き換えると、単語レベルの位置情報データを使用して文書構造を検出し、クラウドへの呼び出しなしに抽出ロジックをルーティングします。
Mindeeのアプローチ:
using Mindee;
using Mindee.Input;
using Mindee.Product.FinancialDocument;
public class MindeeFinancialRouter
{
private readonly MindeeClient _client;
public MindeeFinancialRouter(string apiKey)
{
_client = new MindeeClient(apiKey);
}
public async Task<FinancialSummary> ProcessFinancialDocumentAsync(string filePath)
{
// All financial documents uploaded for auto-detection and parsing
var inputSource = new LocalInputSource(filePath);
var response = await _client.ParseAsync<FinancialDocumentV1>(inputSource);
var prediction = response.Document.Inference.Prediction;
return new FinancialSummary
{
DocumentType = prediction.DocumentType?.Value, // "INVOICE" or "RECEIPT"
InvoiceNumber = prediction.InvoiceNumber?.Value,
Date = prediction.Date?.Value?.ToString(),
TotalAmount = prediction.TotalAmount?.Value,
SupplierName = prediction.SupplierName?.Value,
CustomerName = prediction.CustomerName?.Value
};
}
}
IronOCRのアプローチ:
using IronOcr;
using System.Text.RegularExpressions;
public class IronOcrFinancialRouter
{
private readonly IronTesseract _ocr = new IronTesseract();
public FinancialSummary ProcessFinancialDocument(string filePath)
{
var result = _ocr.Read(filePath);
var text = result.Text;
// Detect document type using structural keywords from word data
var isInvoice = DetectInvoice(result);
if (isInvoice)
{
return new FinancialSummary
{
DocumentType = "INVOICE",
InvoiceNumber = Regex.Match(text,
@"Invoice\s*#?\s*:?\s*([A-Z0-9\-]+)",
RegexOptions.IgnoreCase).Groups[1].Value,
Date = Regex.Match(text,
@"(?:Invoice\s+)?Date\s*:?\s*(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})",
RegexOptions.IgnoreCase).Groups[1].Value,
TotalAmount = ParseAmount(text,
@"Total\s*(?:Due|Amount)?\s*:?\s*\$?([\d,]+\.\d{2})"),
SupplierName = ExtractTopLine(result),
CustomerName = Regex.Match(text,
@"Bill\s+To\s*:?\s*\n?(.+)",
RegexOptions.IgnoreCase).Groups[1].Value.Trim()
};
}
else
{
// Receipt structure: merchant at top, no "Bill To" section
return new FinancialSummary
{
DocumentType = "RECEIPT",
Date = Regex.Match(text,
@"\b(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})\b").Value,
TotalAmount = ParseAmount(text,
@"(?:Total|Amount\s+Due|Grand\s+Total)\s*:?\s*\$?([\d,]+\.\d{2})"),
SupplierName = ExtractTopLine(result)
};
}
}
private bool DetectInvoice(OcrResult result)
{
// Invoice indicators: "Invoice", "Bill To", "Due Date", line items with quantities
var text = result.Text;
var invoiceSignals = new[] { "Invoice", "Bill To", "Due Date", "Purchase Order" };
return invoiceSignals.Any(s =>
text.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0);
}
private string ExtractTopLine(OcrResult result)
{
return result.Pages
.FirstOrDefault()
?.Lines
.FirstOrDefault(l => l.Text.Trim().Length > 4)
?.Text.Trim();
}
private decimal? ParseAmount(string text, string pattern)
{
var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase);
if (match.Success &&
decimal.TryParse(match.Groups[1].Value.Replace(",", ""), out var val))
return val;
return null;
}
}
public class FinancialSummary
{
public string DocumentType { get; set; }
public string InvoiceNumber { get; set; }
public string Date { get; set; }
public decimal? TotalAmount { get; set; }
public string SupplierName { get; set; }
public string CustomerName { get; set; }
}
ドキュメントタイプ検出ロジックは、Mindee のサーバー側の DocumentType フィールドを result.Text に対する単純なキーワードスキャンで置き換えます。 ほとんどの財務書類において、"請求書"または"請求先"という表記があれば、それが請求書であることは明確に識別できます。 不在はレシートと識別します。result.Pages の単語レベルポジションデータにより、エッジケースを含むドキュメントセットがある場合に、より高度なレイアウトベースの検出が可能になります。 結果の読み取りガイドは、Words, Lines, Paragraphs 及びその境界座標を含む完全なオブジェクトモデルを説明しています。
ミンディー APIからIronOCRへのマッピングリファレンス
| ミンディー | IronOCR相当値 |
|---|---|
new MindeeClient(apiKey) | new IronTesseract() — API キーは不要 |
new LocalInputSource(filePath) | new OcrInput() + input.LoadImage(filePath) もしくは ocr.Read(filePath) |
_client.ParseAsync<InvoiceV4>(input) | _ocr.Read(filePath) + 正規表現抽出 (result.Text 上) |
_client.ParseAsync<ReceiptV5>(input) | _ocr.Read(filePath) + レシート抽出パターン |
_client.ParseAsync<PassportV1>(input) | _ocr.Read(filePath) + MRZ ゾーン抽出 (CropRectangle 経由) |
_client.ParseAsync<FinancialDocumentV1>(input) | _ocr.Read(filePath) + ドキュメントタイプ検出 (result.Text 上) |
response.Document.Inference.Prediction | result.Text, result.Pages, result.Lines, result.Words |
prediction.InvoiceNumber?.Value | Regex.Match(result.Text, @"Invoice\s*#?\s*:?\s*([A-Z0-9\-]+)") |
prediction.SupplierName?.Value | 最初のページのトップライン (result.Pages[0].Lines.First() 経由) |
prediction.TotalAmount?.Value | Regex.Match(result.Text, @"Total\s*:?\s*\$?([\d,]+\.\d{2})") |
prediction.LineItems | result.Lines フィルタリング (項目毎の正規表現パターンにより) |
prediction.SupplierPaymentDetails[].Iban | Regex.Match(result.Text, @"IBAN\s*:?\s*([\w\s]+)") |
prediction.InvoiceDate?.Value | Regex.Match(result.Text, @"Date\s*:?\s*(\d{1,2}[/-]\d{1,2}[/-]\d{4})") |
field.Confidence | result.Confidence (ドキュメントレベル) または word.Confidence (単語ごと) |
catch (MindeeException ex) | 同等のものはありません — 認識時にネットワークエラーは発生しません |
await Task.Delay(100) (レート制限) | 必須ではありません - レート制限はありません |
input.LoadPdf(path) | ocrInput.LoadPdf(path) — 同じ操作、完全にローカル |
| 設定内のAPIキー | ライセンスキー (via IronOcr.License.LicenseKey = "...") — 回転は不要 |
一般的な移行の問題と解決策
問題1:切り抜き矩形の座標がドキュメントのレイアウトと一致しない
**Mindee:**空間フィールドの座標はサーバー側で処理されます。Mindeeのモデルは、開発者による座標設定なしに、さまざまな請求書レイアウトに対応します。
**解決策:**ベンダーから入手した文書の代表的なサンプルを用いて、ゾーン座標を測定してください。 任意の画像エディタでサンプル請求書を開いてピクセル寸法を読み取り、それに応じて CropRectangle(x, y, width, height) を定義します。 レイアウトが可変な請求書の場合は、まず文書全体を読み、ワード位置データを使用してゾーンを動的に特定してください。
var result = _ocr.Read("invoice.jpg");
var allWords = result.Pages.First().Words;
// Find the word "Total" and read the region 50px to its right
var totalLabel = allWords.FirstOrDefault(w =>
w.Text.Equals("Total", StringComparison.OrdinalIgnoreCase));
if (totalLabel != null)
{
var valueRegion = new CropRectangle(
totalLabel.X + totalLabel.Width + 5,
totalLabel.Y - 5,
200,
totalLabel.Height + 10);
using var valueInput = new OcrInput();
valueInput.LoadImage("invoice.jpg", valueRegion);
var valueResult = _ocr.Read(valueInput);
Console.WriteLine($"Total: {valueResult.Text.Trim()}");
}
領域ベースのOCRの例では、この動的なゾーンパターンを完全な動作例として示しています。
問題2:Mindeeを削除すると非同期呼び出しサイトが壊れる
Mindee: MindeeのAPIインターフェース全体は、ネットワークI/Oであるため非同期です。 コントローラーとサービスメソッドは、await _client.ParseAsync<t>() をベースにしており、呼び出しチェーン全体で非同期です。
解決策: IronOCR の Read() メソッドは同期しています。 既存の非同期呼び出しサイトは、同期呼び出しを Task.Run でラッピングすることで即座に動作します。
// Existing async signature preserved for callers
public async Task<string> ExtractInvoiceNumberAsync(string filePath)
{
return await Task.Run(() =>
{
var result = new IronTesseract().Read(filePath);
return Regex.Match(result.Text,
@"Invoice\s*#?\s*:?\s*([A-Z0-9\-]+)",
RegexOptions.IgnoreCase).Groups[1].Value;
});
}
高スループットの ASP.NET シナリオのために、Task.Run ラッパーとネイティブ非同期パスの間を選択する前に、非同期 OCR ガイドを確認してください。
問題3:低品質スキャンでは抽出精度が低下する
Mindee: Mindeeの前処理は内部で自動的に行われます。 スキャン品質の問題は、再提出前に開発者による介入ができない場合、フィールド信頼度スコアを低下させます。
**解決策:**認識前にIronOCRの前処理パイプラインを適用してください。 アカウント支払いのワークフローで見られる標準的なフラットベッドスキャン欠陥に対して、Deskew() と DeNoise() の組み合わせでほとんどの精度を回復します:
using var input = new OcrInput();
input.LoadImage("faded-invoice.jpg");
input.Deskew();
input.DeNoise();
input.Contrast(); // for faded or low-contrast thermal paper receipts
input.Binarize(); // convert to clean black-and-white before recognition
var result = new IronTesseract().Read(input);
深刻に劣化したスキャンの場合、input.DeepCleanBackgroundNoise() はより重い背景除去を適用します。 画像品質補正ガイドにはしきい値が記載されており、フィルターウィザードは、特定の文書サンプルに対してどのフィルターが精度を向上させるかを特定するのに役立ちます。
問題4:APIキーとネットワーク出力設定が削除されました
Mindee: アプリ設定に保存された API キー、api.mindee.net に対するアウトバウンド HTTPS を許可するネットワーク外部行動規則、HttpRequestException をラッピングする再試行ロジック、全てが必要なインフラです。
解決策: 3つすべてをまとめて取り除きます。 ミンディー APIキーのエントリが設定から削除されました。 ネットワーク送信ルールは取り消されました。 try/catch (HttpRequestException) ブロックは削除されます。 残りの認証情報は、起動時に一度だけ設定されるIronOCRライセンスキーのみです。
// appsettings.json: remove "Mindee:ApiKey"
// Firewall: revoke egress rule for api.mindee.net
// Startup.cs or Program.cs:
IronOcr.License.LicenseKey = Configuration["IronOcr:LicenseKey"];
// なし rotation schedule, no secure storage beyond standard config secret management
第5号:複数ページPDFのページ数に応じた課金
Mindee: ミンディー にアップロードされた 12 ページのベンダー ステートメントは、月次割り当てに対して 12 ページを消費します。 プロプランの超過料金では、月間上限に近い場合、その1つの文書につき1.20ドルの追加料金が発生します。
解決策: IronOCRは複数ページのPDFをページごとのコストなしで処理します。ドキュメント全体を読み込み、ページを順に処理します。
using var input = new OcrInput();
input.LoadPdf("vendor-statement.pdf"); // 12 pages — no billing unit increment
var result = new IronTesseract().Read(input);
foreach (var page in result.Pages)
Console.WriteLine($"Page {page.PageNumber}: {page.Text.Length} characters recognized");
問題6:FinancialDocumentV1の依存関係を単一のパターンで置き換えることはできません
Mindee: FinancialDocumentV1 はドキュメントタイプを自動的に検出し、呼び出しコードがドキュメントタイプで分岐することなくフィールドを抽出します。
**解決策:**キーワードの存在を利用した軽量な検出器を構築する。 財務書類は、請求書("請求書"、"請求先"、"支払期日"のいずれかを含む)と領収書("領収書"、"ありがとうございます"のいずれかを含むか、請求書であることを示す表示がない)に明確に区分されます。 2つの抽出方法と3行検出器を組み合わせることで、整形式文書における精度を損なうことなく、Mindee API呼び出しを置き換えることができます。
var result = _ocr.Read(filePath);
var summary = result.Text.IndexOf("Invoice", StringComparison.OrdinalIgnoreCase) >= 0
? ExtractInvoiceFields(result)
: ExtractReceiptFields(result);
Mindee移住チェックリスト
移行前
コードベース全体におけるMindeeの使用状況を監査する:
grep -r "using Mindee" --include="*.cs" .
grep -r "MindeeClient\|ParseAsync\|InvoiceV4\|ReceiptV5\|PassportV1\|FinancialDocumentV1" --include="*.cs" .
grep -r "LocalInputSource\|MindeeException" --include="*.cs" .
grep -r "Mindee:ApiKey\|mindee_api_key\|MINDEE_API_KEY" --include="*.json" --include="*.env" --include="*.yml" .
在庫調査結果:
- 一意の呼び出しサイトを
ParseAsync<t>を使ってカウントする - 使用されているドキュメントタイプを記録する (
InvoiceV4,ReceiptV5,PassportV1,FinancialDocumentV1) Mindee呼び出しから伝播するすべての非同期メソッドチェーンを特定します。 - 設定ファイルとシークレット保管庫内のAPIキー参照箇所を特定する Mindeeネットワーク呼び出しをラップする再試行ロジックを特定します。 Mindeeエンドポイントへの送信トラフィックを許可するネットワーク出力ルールを特定する
コードの移行
- プロジェクトファイルから
MindeeNuGet パッケージを削除する dotnet add package IronOcrを実行して IronOCR をインストールする- アプリケーションの起動時に
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"を追加する - すべての
using Mindee,using Mindee.Input,using Mindee.Product.*ディレクティブを削除する MindeeClientコンストラクター注入をIronTesseractインスタンス化または DI 登録に置き換える- 各
ParseAsync<InvoiceV4>呼び出しを_ocr.Read(filePath)と請求書抽出方法に置き換える - 各
ParseAsync<ReceiptV5>呼び出しを_ocr.Read(filePath)とレシート抽出方法に置き換える - 各
ParseAsync<PassportV1>呼び出しをCropRectangleを使用したゾーンベースの OCR で MRZ 領域に置き換える - 各
ParseAsync<FinancialDocumentV1>呼び出しをドキュメントタイプ検出器 + ルーティングパターンに置き換える - すべての
await Task.Delay(...)レート制限遵守ラインを削除する - すべての
catch (MindeeException)およびcatch (HttpRequestException)ブロックを OCR パスから削除する - スキャンされたドキュメントパスに対して
OcrInputプリプロセス呼び出しを追加する (Deskew,DeNoise,Contrast) - 任意のドキュメントアーカイブパスに
result.SaveAsSearchablePdf(archivePath)を追加する - ミンディー APIキーをすべての設定ファイルとシークレット保管庫から削除する
- ネットワークモックを使用せずに同期的に実行されるように統合テストを更新する
移行後
- 各文書タイプを網羅する20~30件の文書サンプルセットを用いて、抽出精度を検証する。
- 代表的なクリーンスキャンで
result.Confidence値が 80 以上であることを確認します。 以下の場合は、前処理を適用してください。 - 歪んだおよび劣化したスキャンサンプルに対してプリプロセスパイプライン (
Deskew,DeNoise) をテストします - 複数ページの PDF が正しい
result.Pages.Countとページごとのコンテンツを生成することを確認します。 result.SaveAsSearchablePdf()出力が Adobe Acrobat およびシステム PDF ビューアで検索可能であることを確認します- ベンダーセットのドキュメントレイアウトの変動に対してすべてのゾーンベースの抽出 (
CropRectangle) をテストします Task.Delay呼び出しなくバッチ処理を実行し、スレッドの問題がないことを確認します- アプリケーションがインターネットへの外部アクセスなしで正しく起動および実行されることを確認します。
- 各エントリポイントで最初の
_ocr.Read()呼び出しの前に、ライセンスキーの初期化が実行されることを確認します。 - 設定ファイル、環境変数、またはシークレットにMindee APIキーへの参照が残っていないことを確認してください。
IronOCRへの移行の主なメリット
**財務書類に関する完全なデータ主権を確保します。**移行後、ベンダーのIBANコード、ルーティング番号、顧客のEIN、明細項目は、お客様のインフラストラクチャから外部に送信されることはありません。 コンプライアンスの適用範囲は、貴社組織のみを対象とします。 第三者AIベンダーとの処理契約は、監査証跡から削除されます。 GLBA、CMMC、FedRAMP、またはデータ所在地の要件に基づいて業務を行うチームは、外部クラウドへの送信に必要なコンプライアンス審査を受けることなく、財務文書を処理できます。
ボリュームに関係なく予測可能なコスト。 $999 の IronOCR Lite ライセンスでは、1 人の開発者が無制限のドキュメント処理を行うことが可能です。 月間500件の請求書から月間50,000件の請求書に規模を拡大しても、追加費用は一切かかりません。 年末処理の急増、バッチ修正実行、開発テストサイクルは、請求カウンターを増加させません。 月間5,000ページを処理するチームの場合、3年間の総所有コストは約17,964ドル(Mindee Pro)から1,499ドル~2,999ドル(IronOCR永久ライセンス)に減少します。 料金プランの詳細については、 IronOCRの製品ページをご覧ください。
抽出ロジックは永久に所有できます。IronOCRの抽出パターンは、リポジトリ内のプレーンなC#コードです。 これらは、外部ベンダーのリリーススケジュールやAPIのバージョン管理の決定とは無関係に、バージョン管理され、レビュー可能で、テスト可能で、デプロイ可能です。 ミンディー が InvoiceV5 をリリースし、InvoiceV4 を非推奨にするとき、それはベンダーが課す移行期限です。 抽出パターンを維持するとき、それは git commit の改善です。
**実環境でのスキャン品質を確保するための前処理管理。**買掛金処理部門は、数十社、数百社ものベンダーから請求書を受け取りますが、それぞれのベンダーは異なる機器と設定でスキャンされています。 IronOCR のプリプロセスパイプライン — Deskew(), DeNoise(), Contrast(), Sharpen(), Binarize() — は、それぞれのスキャンカテゴリで発見される特定の欠陥に対処します。 感熱レシートの写真は、フラットベッドスキャナーで複数ページをスキャンした請求書とは異なる欠陥を抱えているため、それぞれの処理経路に適したフィルターを適用する必要があります。 Mindeeのプリプロセスは目に見えず、設定変更もできません。 フィルタ一覧の詳細は、前処理機能のページをご覧ください。
ワンステップでの検索可能な PDF アーカイブ。 IronOCR で処理されたすべての請求書は result.SaveAsSearchablePdf() を使って検索可能な PDF としてアーカイブできます。 認識されたテキストレイヤーは出力ファイルに埋め込まれるため、文書管理システム、SharePointライブラリ、およびEnterpriseコンテンツリポジトリにおいて、すべての単語を全文検索することが可能になります。 MindeeはPDF出力機能を一切提供していません。 検索可能なアーカイブを実現するには、従来は別のライブラリ、別の処理工程、および追加のメンテナンスが必要だった。 移行後、抽出とアーカイブは _ocr.Read(input) 呼び出しに統合されます。
アーキテクチャの変更なしに、あらゆる環境にデプロイ可能。IronOCRは、同じNuGetパッケージと初期化手順を使用して、Windows、Linux、macOS、Docker、Azure App Service、AWS Lambda、Google Cloud Runにデプロイできます。 エアギャップ環境、工場現場システム、政府のセキュリティ施設はすべて、変更を加えることなく機能します。 Docker導入ガイド、 Azureガイド、およびAWSガイドでは、各プラットフォームの環境固有の設定について説明しています。
