VeryfiからIronOCRへの移行|IronOCR
このガイドでは、.NET開発者がVeryfiのクラウド文書処理APIを、ローカルOCRライブラリであるIronOCRに置き換える手順を解説します。 本資料では、パッケージの入れ替え、名前空間のクリーンアップ、および ベリーフィ を中心に構築される最も一般的なパターンに焦点を当てた 4 つの完全なコード移行例(クライアントの初期化、領域ベースのフィールド抽出、構造化データを用いた経費の分類、Webhook の置換)を扱っています。 比較記事を事前に読む必要はありません。
Veryfiからの移行理由
財務文書は、Veryfiのパイプラインを一方通行で流れます。つまり、お客様のインフラからVeryfiのインフラへと送られるのです。 そのアーキテクチャ上の事実が、ほとんどの移行の原動力となっています。 以下は、チームが移行を決断する具体的な課題です。
ドキュメントの呼び出しが行われるたびに、機密性の高い財務データがサードパーティのサーバーに送信されます。領収書には、カードの末尾4桁および取引先情報が含まれています。 請求書には、銀行口座番号、銀行コード、およびベンダーの納税者番号が記載されています。 銀行取引明細書には、取引履歴がすべて記載されています。 Veryfiでは、すべてのapi.veryfi.comにアップロードされ、Veryfiのインフラで処理され、JSONが返されます。 そのデータに対する制御権は、HTTPリクエストが送信された瞬間に失われます。
4つの資格情報が必要であり、すべての環境で同期が必要です。 apiKeyを必要とします。これらは、設定に保存し、スケジュールで更新し、CI/CDパイプラインに挿入し、漏洩を監査するための4つの別々の秘密です。 たった1件の認証情報の漏洩により、アプリケーション全体で処理されるすべてのドキュメントの認証が破綻します。 IronOCRには、1つのライセンスキー文字列が必要です。
文書ごとの料金は上限なく累積します。領収書は1通あたり約0.05~0.15ドル、請求書は0.10~0.25ドル、銀行取引明細書は0.15~0.30ドルです。月間50,000通の場合、従量課金制では月額5,000~15,000ドルとなり、2年目や3年目になっても料金は減額されません。 IronOCR Professionalライセンス(2,999ドル)は、無制限のドキュメントを永久に利用可能です。Veryfiの月額5,000ドルの利用料に対する元は3週間未満で回収できます。
APIは非同期専用です。なぜなら、基礎となる作業がリモートで行われるためです。しかし、ProcessDocumentAsyncは処理が長時間かかるため非同期ではありません。 これは非同期処理である。なぜなら、ドキュメントはサーバーへ送信され、他のリクエストの後ろにキューに入れられ、推論を完了し、ネットワーク経由でレスポンスを返す必要があるからだ。 レイテンシは非決定論的です。 HTTP 429(レート制限)にはリトライロジックが必要です。 HTTP 402 支払いエラーが発生すると、バッチ処理が完全に停止します。 VeryfiのインフラでHTTP 500エラーが発生すると、ワークフローも同時に停止してしまいます。
Veryfiのドキュメント対象範囲は、経費精算書類の境界で終了します。学習済みのモデルは、領収書、請求書、小切手、銀行取引明細書、W-2フォーム、名刺について、構造化されたフィールドを確実に返します。 このリスト以外のもの(一般的なビジネス文書、契約書、医療記録、出荷書類、カスタム社内フォームなど)については、翻訳品質が低下するか、有料のカスタムモデルトレーニングが必要となります。 経費管理の自動化にVeryfiを導入した組織は、通常6~12ヶ月以内に、Veryfiが対応していない種類の文書に対して他のチームがOCRを必要としていることに気づきます。
Veryfiの独自のJSONスキーマは、すべての抽出ロジックを1つのベンダーに結びつけます。 response.LineItemsを読み取るコードのすべての行は、Veryfi専用です。 ベンダーを変更する、あるいはローカルのOCRに切り替えるということは、抽出ロジックをすべて一から書き直すことを意味します。
基本的な問題
// Veryfi: financial data leaves your infrastructure on every call
var client = new VeryfiClient(clientId, clientSecret, username, apiKey); // 4 secrets
var bytes = File.ReadAllBytes("invoice-with-routing-number.pdf");
var response = await client.ProcessDocumentAsync(bytes); // bank details transmitted
var routingNumber = response.BankAccount?.RoutingNumber; // arrived via ベリーフィ cloudDim client As New VeryfiClient(clientId, clientSecret, username, apiKey)
Dim bytes = File.ReadAllBytes("invoice-with-routing-number.pdf")
Dim response = Await client.ProcessDocumentAsync(bytes)
Dim routingNumber = response.BankAccount?.RoutingNumber// IronOCR: routing numbers never leave your server
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; // 1 key
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf("invoice-with-routing-number.pdf"); // processed locally
var result = ocr.Read(input);
var routingNumber = Regex.Match(result.Text, @"Routing\s*#?\s*:?\s*(\d{9})").Groups[1].Value;IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY" ' 1 key
Dim ocr = New IronTesseract()
Using input = New OcrInput()
input.LoadPdf("invoice-with-routing-number.pdf") ' processed locally
Dim result = ocr.Read(input)
Dim routingNumber = Regex.Match(result.Text, "Routing\s*#?\s*:?\s*(\d{9})").Groups(1).Value
End UsingIronOCR 対 Veryfi:機能比較
以下の表は、技術的な評価を支援するために、両製品の機能を対比したものです。
| フィーチャー | ベリーフィ | IronOCR |
|---|---|---|
| 処理場所 | Veryfiクラウドサーバー | インフラストラクチャ |
| 展開モデル | クラウドAPIのみ | オンプレミス、Docker、Azure、AWS、Linux |
| オフラインサポート | なし | はい |
| インターネット接続が必要です | はい(すべての文書) | なし |
| データはインフラストラクチャを離れる | はい(すべての通話で) | 一度もない |
| BAAなしのHIPAA準拠 | なし | はい |
| エアギャップ環境のサポート | 不可 | 完全サポート |
| 価格設定モデル | 文書1件あたり(0.05ドル~0.30ドル) | 永続ライセンス ($999–$2,399) |
| 必要な資格 | 4 (clientId、clientSecret、username、apiKey) | ライセンスキー1つ |
| 同期API | いいえ(非同期のみ) | はい |
| 律速段階 | はい (HTTP 429) | None |
| 文書の範囲 | 領収書、請求書、小切手、銀行取引明細書、W-2フォーム、名刺 | あらゆる文書タイプ |
| カスタムドキュメントタイプ | 有償モデルトレーニングが必要 | 正規表現やパターン抽出によるレイアウト |
| PDF入力 | はい(バイト単位のアップロード) | はい(ネイティブ、地元) |
| 検索可能なPDF出力 | なし | はい (result.SaveAsSearchablePdf()) |
| 地域ベースのOCR | なし | はい (CropRectangle) |
| バーコード読み取り | なし | はい(同じOCR処理) |
| 構造化された結果へのアクセス | 事前解析済みのJSONフィールド | 座標付きのページ、段落、行、単語 |
| 信頼度スコア | フィールド単位(独自仕様) | 単語ごとおよび全体として (result.Confidence) |
| 125以上の言語に対応 | 制限的 | はい(NuGet言語パック) |
| スレッドセーフな並列処理 | HTTPの同時接続数制限が適用されます | フル (スレッドごとに1つのIronTesseract) |
| モックを使用しないユニットテスト | HTTPモック機能が必要 | 直接的な現地検査 |
クイックスタート:Veryfi から IronOCR への移行
ステップ 1: NuGet パッケージを置き換える
Veryfi SDK を削除してください:
dotnet remove package Veryfi
NuGetからIronOCRをインストールしてください。
ステップ 2: 名前空間の更新
Veryfi ネームスペースを IronOCR ネームスペースに置き換えてください:
// Before (Veryfi)
using Veryfi;
using Veryfi.Models;
// After (IronOCR)
using IronOcr;
using System.Text.RegularExpressions;' Before (Veryfi)
Imports Veryfi
Imports Veryfi.Models
' After (IronOCR)
Imports IronOcr
Imports System.Text.RegularExpressionsステップ 3: ライセンスの初期化
アプリケーションの起動時、OCR呼び出しの前に、以下の行を1回追加してください:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"コード移行の例
ドキュメント処理クライアントの代替
Veryfiのサービスは、VeryfiClientのコンストラクタ注入を中心に構築されています。 4つの認証情報を用いるコンストラクタは、依存性注入を行う上で自然な接点となりますが、管理およびローテーションが必要な4つのシークレットが生成されてしまいます。 これを IronOCR に置き換えることで、認証情報を単一のライセンスキーに統合し、処理エンジンのインスタンス化をサービスクラス自体に移行します。
Veryfiのアプローチ:
using Veryfi;
using Microsoft.Extensions.Configuration;
public class ExpenseDocumentService
{
private readonly VeryfiClient _client;
// Four credentials injected — four secrets to manage, store, rotate
public ExpenseDocumentService(IConfiguration config)
{
_client = new VeryfiClient(
config["Veryfi:ClientId"], // secret 1
config["Veryfi:ClientSecret"], // secret 2
config["Veryfi:Username"], // secret 3
config["Veryfi:ApiKey"] // secret 4
);
}
public async Task<string> GetVendorNameAsync(string documentPath)
{
var bytes = File.ReadAllBytes(documentPath);
// Document uploaded to ベリーフィ on this call
var response = await _client.ProcessDocumentAsync(bytes);
return response.Vendor?.Name;
}
public async Task<decimal?> GetTotalAsync(string documentPath)
{
var bytes = File.ReadAllBytes(documentPath);
var response = await _client.ProcessDocumentAsync(bytes);
return response.Total;
}
}Imports Veryfi
Imports Microsoft.Extensions.Configuration
Public Class ExpenseDocumentService
Private ReadOnly _client As VeryfiClient
' Four credentials injected — four secrets to manage, store, rotate
Public Sub New(config As IConfiguration)
_client = New VeryfiClient(config("Veryfi:ClientId"), ' secret 1
config("Veryfi:ClientSecret"), ' secret 2
config("Veryfi:Username"), ' secret 3
config("Veryfi:ApiKey")) ' secret 4
End Sub
Public Async Function GetVendorNameAsync(documentPath As String) As Task(Of String)
Dim bytes = File.ReadAllBytes(documentPath)
' Document uploaded to ベリーフィ on this call
Dim response = Await _client.ProcessDocumentAsync(bytes)
Return response.Vendor?.Name
End Function
Public Async Function GetTotalAsync(documentPath As String) As Task(Of Decimal?)
Dim bytes = File.ReadAllBytes(documentPath)
Dim response = Await _client.ProcessDocumentAsync(bytes)
Return response.Total
End Function
End ClassIronOCRのアプローチ:
using IronOcr;
using System.Text.RegularExpressions;
public class ExpenseDocumentService
{
private readonly IronTesseract _ocr;
// One license key — set once at startup, not per-instance
public ExpenseDocumentService()
{
_ocr = new IronTesseract();
}
public string GetVendorName(string documentPath)
{
// All processing local — document bytes never leave this server
var result = _ocr.Read(documentPath);
// Vendor is typically the first non-whitespace line on a receipt
return result.Pages[0].Paragraphs
.OrderBy(p => p.Y)
.Select(p => p.Text.Trim())
.FirstOrDefault(t => t.Length > 3);
}
public decimal? GetTotal(string documentPath)
{
var result = _ocr.Read(documentPath);
var match = Regex.Match(result.Text,
@"(?:Total|Grand Total|Amount Due):?\s*\$?\s*([\d,]+\.\d{2})",
RegexOptions.IgnoreCase);
return match.Success
? decimal.Parse(match.Groups[1].Value.Replace(",", ""))
: (decimal?)null;
}
}Imports IronOcr
Imports System.Text.RegularExpressions
Public Class ExpenseDocumentService
Private ReadOnly _ocr As IronTesseract
' One license key — set once at startup, not per-instance
Public Sub New()
_ocr = New IronTesseract()
End Sub
Public Function GetVendorName(documentPath As String) As String
' All processing local — document bytes never leave this server
Dim result = _ocr.Read(documentPath)
' Vendor is typically the first non-whitespace line on a receipt
Return result.Pages(0).Paragraphs _
.OrderBy(Function(p) p.Y) _
.Select(Function(p) p.Text.Trim()) _
.FirstOrDefault(Function(t) t.Length > 3)
End Function
Public Function GetTotal(documentPath As String) As Decimal?
Dim result = _ocr.Read(documentPath)
Dim match = Regex.Match(result.Text, "(?:Total|Grand Total|Amount Due):?\s*\$?\s*([\d,]+\.\d{2})", RegexOptions.IgnoreCase)
Return If(match.Success, Decimal.Parse(match.Groups(1).Value.Replace(",", "")), CType(Nothing, Decimal?))
End Function
End Classこのコンストラクタの変更により、すべての環境から4つの設定記入が取り除かれます: appsettings.json、Dockerの秘密情報、Azure Key Vaultの参照、およびCI/CDパイプラインの変数。 同じスレッド上の複数の呼び出しに対し、IronTesseractインスタンスを再利用できます。 .NET Coreの依存性注入コンテナにおけるシングルトン登録パターンについては、IronTesseractのセットアップガイドを参照してください。
領域ベースのOCRによる領収書フィールドの抽出
Veryfiは、学習済みの機械学習モデルをドキュメント画像全体に適用し、事前に構造化されたJSONレスポンスを返すことで、領収書の各フィールドを抽出します。 IronOCRの同等品は、CropRectangleを使用した地域ベースのOCRであり、ベンダーのヘッダーゾーンや総計のフッターゾーンなど、領収書画像の特定のゾーンを対象とします。全ページを走査し、出力からパターンを探すのではありません。 既知のレイアウトでは処理が高速になり、対象領域が明確に定義されている場合は精度が高まります。
Veryfiのアプローチ:
using Veryfi;
public class ReceiptFieldExtractor
{
private readonly VeryfiClient _client;
public ReceiptFieldExtractor(VeryfiClient client)
{
_client = client;
}
public async Task<(string Vendor, decimal? Total, decimal? Tax)>
ExtractReceiptFieldsAsync(string imagePath)
{
var bytes = File.ReadAllBytes(imagePath);
// Full document uploaded — Veryfi's ML returns structured fields
var response = await _client.ProcessDocumentAsync(bytes);
return (
Vendor: response.Vendor?.Name,
Total: response.Total,
Tax: response.Tax
);
}
}Imports Veryfi
Public Class ReceiptFieldExtractor
Private ReadOnly _client As VeryfiClient
Public Sub New(client As VeryfiClient)
_client = client
End Sub
Public Async Function ExtractReceiptFieldsAsync(imagePath As String) As Task(Of (Vendor As String, Total As Decimal?, Tax As Decimal?))
Dim bytes = File.ReadAllBytes(imagePath)
' Full document uploaded — Veryfi's ML returns structured fields
Dim response = Await _client.ProcessDocumentAsync(bytes)
Return (Vendor:=response.Vendor?.Name,
Total:=response.Total,
Tax:=response.Tax)
End Function
End ClassIronOCRのアプローチ:
using IronOcr;
using System.Text.RegularExpressions;
public class ReceiptFieldExtractor
{
private readonly IronTesseract _ocr = new IronTesseract();
public (string Vendor, decimal? Total, decimal? Tax)
ExtractReceiptFields(string imagePath)
{
// Region 1: Header zone — vendor name typically in top 15% of receipt
var headerRegion = new CropRectangle(0, 0, 800, 150);
using var headerInput = new OcrInput();
headerInput.LoadImage(imagePath, headerRegion);
headerInput.Deskew();
var headerResult = _ocr.Read(headerInput);
// Region 2: Footer zone — totals typically in bottom 20% of receipt
var footerRegion = new CropRectangle(0, 650, 800, 200);
using var footerInput = new OcrInput();
footerInput.LoadImage(imagePath, footerRegion);
footerInput.DeNoise();
var footerResult = _ocr.Read(footerInput);
var vendor = headerResult.Pages[0].Paragraphs
.OrderBy(p => p.Y)
.Select(p => p.Text.Trim())
.FirstOrDefault(t => t.Length > 3);
var footerText = footerResult.Text;
var totalMatch = Regex.Match(footerText,
@"(?:Total|Grand Total):?\s*\$?\s*([\d,]+\.\d{2})",
RegexOptions.IgnoreCase);
var taxMatch = Regex.Match(footerText,
@"(?:Tax|Sales Tax|VAT):?\s*\$?\s*([\d,]+\.\d{2})",
RegexOptions.IgnoreCase);
return (
Vendor: vendor,
Total: totalMatch.Success
? decimal.Parse(totalMatch.Groups[1].Value.Replace(",", ""))
: (decimal?)null,
Tax: taxMatch.Success
? decimal.Parse(taxMatch.Groups[1].Value.Replace(",", ""))
: (decimal?)null
);
}
}Imports IronOcr
Imports System.Text.RegularExpressions
Public Class ReceiptFieldExtractor
Private ReadOnly _ocr As New IronTesseract()
Public Function ExtractReceiptFields(imagePath As String) As (Vendor As String, Total As Decimal?, Tax As Decimal?)
' Region 1: Header zone — vendor name typically in top 15% of receipt
Dim headerRegion = New CropRectangle(0, 0, 800, 150)
Using headerInput = New OcrInput()
headerInput.LoadImage(imagePath, headerRegion)
headerInput.Deskew()
Dim headerResult = _ocr.Read(headerInput)
' Region 2: Footer zone — totals typically in bottom 20% of receipt
Dim footerRegion = New CropRectangle(0, 650, 800, 200)
Using footerInput = New OcrInput()
footerInput.LoadImage(imagePath, footerRegion)
footerInput.DeNoise()
Dim footerResult = _ocr.Read(footerInput)
Dim vendor = headerResult.Pages(0).Paragraphs _
.OrderBy(Function(p) p.Y) _
.Select(Function(p) p.Text.Trim()) _
.FirstOrDefault(Function(t) t.Length > 3)
Dim footerText = footerResult.Text
Dim totalMatch = Regex.Match(footerText,
"(?:Total|Grand Total):?\s*\$?\s*([\d,]+\.\d{2})",
RegexOptions.IgnoreCase)
Dim taxMatch = Regex.Match(footerText,
"(?:Tax|Sales Tax|VAT):?\s*\$?\s*([\d,]+\.\d{2})",
RegexOptions.IgnoreCase)
Return (
Vendor := vendor,
Total := If(totalMatch.Success, Decimal.Parse(totalMatch.Groups(1).Value.Replace(",", "")), CType(Nothing, Decimal?)),
Tax := If(taxMatch.Success, Decimal.Parse(taxMatch.Groups(1).Value.Replace(",", "")), CType(Nothing, Decimal?))
)
End Using
End Using
End Function
End Class(x, y, width, height)を受け取ります。 ヘッダーとフッター領域のみを処理する方が、ページ全体を読み込むよりも高速であり、領収書本文の明細金額による誤検出を防ぐことができます。 領域ベースのOCRガイドでは、可変サイズのドキュメントに対する座標測定戦略について解説しており、領域切り抜き例ではその全容を示しています。
構造化された段落データを用いた経費の分類
VeryfiはTotalを含む、事前に構造化されたオブジェクトの配列として返します。 IronOCRは、各テキストブロックをそのX/Y座標と共に公開するresult.Linesを通じて同等の機能を提供します。 経費の分類ロジック(個々の経費項目が食事代、旅費、備品費、またはソフトウェア費用のいずれに該当するかを判断する処理)は、どちらの場合でも同じテキストに基づいて動作します。 IronOCRとの違いは、分類ロジックを自社で所有し、調整し、拡張できる点にあり、有料の機械学習再トレーニングサイクルを必要としません。
Veryfiのアプローチ:
using Veryfi;
public class ExpenseCategorizer
{
private readonly VeryfiClient _client;
public ExpenseCategorizer(VeryfiClient client)
{
_client = client;
}
public async Task<Dictionary<string, decimal>> CategorizeExpensesAsync(string receiptPath)
{
var bytes = File.ReadAllBytes(receiptPath);
var response = await _client.ProcessDocumentAsync(bytes);
var categories = new Dictionary<string, decimal>();
// Line items arrive pre-parsed from Veryfi's ML pipeline
foreach (var item in response.LineItems ?? Enumerable.Empty<dynamic>())
{
var category = response.Category ?? "Uncategorized";
var amount = (decimal)(item.Total ?? 0m);
if (!categories.ContainsKey(category))
categories[category] = 0m;
categories[category] += amount;
}
return categories;
}
}Imports Veryfi
Public Class ExpenseCategorizer
Private ReadOnly _client As VeryfiClient
Public Sub New(client As VeryfiClient)
_client = client
End Sub
Public Async Function CategorizeExpensesAsync(receiptPath As String) As Task(Of Dictionary(Of String, Decimal))
Dim bytes = File.ReadAllBytes(receiptPath)
Dim response = Await _client.ProcessDocumentAsync(bytes)
Dim categories = New Dictionary(Of String, Decimal)()
' Line items arrive pre-parsed from Veryfi's ML pipeline
For Each item In If(response.LineItems, Enumerable.Empty(Of Dynamic)())
Dim category = If(response.Category, "Uncategorized")
Dim amount = CType(If(item.Total, 0D), Decimal)
If Not categories.ContainsKey(category) Then
categories(category) = 0D
End If
categories(category) += amount
Next
Return categories
End Function
End ClassIronOCRのアプローチ:
using IronOcr;
using System.Text.RegularExpressions;
public class ExpenseCategorizer
{
private readonly IronTesseract _ocr = new IronTesseract();
// Keyword-based categorization — tune these for your expense policy
private static readonly Dictionary<string, string[]> CategoryKeywords = new()
{
["Meals & Entertainment"] = new[] { "restaurant", "cafe", "coffee", "lunch", "dinner", "food", "bar" },
["Travel"] = new[] { "airline", "hotel", "uber", "lyft", "taxi", "parking", "gas", "fuel" },
["Office Supplies"] = new[] { "staples", "office depot", "paper", "ink", "toner", "supplies" },
["Software & Subscriptions"] = new[] { "adobe", "microsoft", "github", "aws", "azure", "slack" }
};
public Dictionary<string, decimal> CategorizeExpenses(string receiptPath)
{
var result = _ocr.Read(receiptPath);
// Use paragraph coordinates to isolate line items
// Line items typically appear in the middle vertical band of the receipt
var lineItemParagraphs = result.Pages[0].Paragraphs
.Where(p => p.Y > 150 && p.Y < 650) // skip header/footer regions
.OrderBy(p => p.Y)
.ToList();
var categories = new Dictionary<string, decimal>();
var pricePattern = new Regex(@"\$?([\d,]+\.\d{2})$");
var vendorText = result.Text.ToLower();
// Determine top-level category from vendor name
var topCategory = "Uncategorized";
foreach (var (cat, keywords) in CategoryKeywords)
{
if (keywords.Any(kw => vendorText.Contains(kw)))
{
topCategory = cat;
break;
}
}
// Extract individual line item amounts
foreach (var para in lineItemParagraphs)
{
var priceMatch = pricePattern.Match(para.Text.Trim());
if (!priceMatch.Success)
continue;
if (!decimal.TryParse(priceMatch.Groups[1].Value.Replace(",", ""), out var amount))
continue;
// Classify individual items where keywords appear in the description
var itemCategory = topCategory;
var descriptionText = para.Text.ToLower();
foreach (var (cat, keywords) in CategoryKeywords)
{
if (keywords.Any(kw => descriptionText.Contains(kw)))
{
itemCategory = cat;
break;
}
}
if (!categories.ContainsKey(itemCategory))
categories[itemCategory] = 0m;
categories[itemCategory] += amount;
}
return categories;
}
}Imports IronOcr
Imports System.Text.RegularExpressions
Public Class ExpenseCategorizer
Private ReadOnly _ocr As New IronTesseract()
' Keyword-based categorization — tune these for your expense policy
Private Shared ReadOnly CategoryKeywords As New Dictionary(Of String, String()) From {
{"Meals & Entertainment", New String() {"restaurant", "cafe", "coffee", "lunch", "dinner", "food", "bar"}},
{"Travel", New String() {"airline", "hotel", "uber", "lyft", "taxi", "parking", "gas", "fuel"}},
{"Office Supplies", New String() {"staples", "office depot", "paper", "ink", "toner", "supplies"}},
{"Software & Subscriptions", New String() {"adobe", "microsoft", "github", "aws", "azure", "slack"}}
}
Public Function CategorizeExpenses(receiptPath As String) As Dictionary(Of String, Decimal)
Dim result = _ocr.Read(receiptPath)
' Use paragraph coordinates to isolate line items
' Line items typically appear in the middle vertical band of the receipt
Dim lineItemParagraphs = result.Pages(0).Paragraphs _
.Where(Function(p) p.Y > 150 AndAlso p.Y < 650) ' skip header/footer regions
.OrderBy(Function(p) p.Y).ToList()
Dim categories = New Dictionary(Of String, Decimal)()
Dim pricePattern = New Regex("\$?([\d,]+\.\d{2})$")
Dim vendorText = result.Text.ToLower()
' Determine top-level category from vendor name
Dim topCategory = "Uncategorized"
For Each kvp In CategoryKeywords
Dim cat = kvp.Key
Dim keywords = kvp.Value
If keywords.Any(Function(kw) vendorText.Contains(kw)) Then
topCategory = cat
Exit For
End If
Next
' Extract individual line item amounts
For Each para In lineItemParagraphs
Dim priceMatch = pricePattern.Match(para.Text.Trim())
If Not priceMatch.Success Then
Continue For
End If
Dim amount As Decimal
If Not Decimal.TryParse(priceMatch.Groups(1).Value.Replace(",", ""), amount) Then
Continue For
End If
' Classify individual items where keywords appear in the description
Dim itemCategory = topCategory
Dim descriptionText = para.Text.ToLower()
For Each kvp In CategoryKeywords
Dim cat = kvp.Key
Dim keywords = kvp.Value
If keywords.Any(Function(kw) descriptionText.Contains(kw)) Then
itemCategory = cat
Exit For
End If
Next
If Not categories.ContainsKey(itemCategory) Then
categories(itemCategory) = 0D
End If
categories(itemCategory) += amount
Next
Return categories
End Function
End ClassY座標を提供し、標準の領収書レイアウトでラインアイテムが現れる垂直ゾーンを簡単に分離できます。 構造化データアクセスガイドは、その座標プロパティと共にCharactersの完全な階層を説明します。 紙がよれよれになっている、熱転写印刷でコントラストが低いなど、スキャン品質の悪いレシートについては、画像品質補正ガイドで、分類ロジックが実行される前に精度を向上させる前処理フィルターについて解説しています。
Webhookの排除と同期バッチ処理への置換
ドキュメントの量が多い場合、VeryfiではポーリングよりもWebhookベースの通知を推奨します。 このパターンには、一般に公開された HTTPS エンドポイント、署名検証用の Webhook シークレット、Webhook が発火するまで結果を保持するキュー、および配信に失敗した場合の再試行ロジックが必要です。 これは、クラウドOCRがローカル処理に比べて処理速度が遅いという事実に対する、いわば回避策となる重要なインフラストラクチャです。 IronOCRは同期処理を行います。 Webhookを使用する場合、非同期処理のギャップを埋める必要はありません。
Veryfiのアプローチ:
using Veryfi;
using Microsoft.AspNetCore.Mvc;
// ベリーフィ webhook receiver — required for high-volume reliable processing
[ApiController]
[Route("webhooks")]
public class VeryfiWebhookController : ControllerBase
{
private readonly IDocumentResultQueue _queue;
public VeryfiWebhookController(IDocumentResultQueue queue)
{
_queue = queue;
}
[HttpPost("veryfi")]
public IActionResult ReceiveWebhook([FromBody] VeryfiWebhookPayload payload,
[FromHeader(Name = "X-Veryfi-Token")] string token)
{
// Validate webhook signature — prevents spoofed payloads
if (!IsValidSignature(token, payload))
return Unauthorized();
// Enqueue result for async downstream consumption
_queue.Enqueue(new DocumentResult
{
DocumentId = payload.Id,
Vendor = payload.Data?.Vendor?.Name,
Total = payload.Data?.Total
});
return Ok();
}
private bool IsValidSignature(string token, VeryfiWebhookPayload payload) =>
// HMAC validation against webhook secret — infrastructure requirement
token == ComputeHmac(payload, Environment.GetEnvironmentVariable("VERYFI_WEBHOOK_SECRET"));
}
// Document batch submission — fire and forget, results arrive via webhook
public class VeryfiDocumentBatchSubmitter
{
private readonly VeryfiClient _client;
public async Task SubmitBatchAsync(string[] documentPaths)
{
foreach (var path in documentPaths)
{
var bytes = File.ReadAllBytes(path);
// Submit — result arrives asynchronously via webhook, not here
await _client.ProcessDocumentAsync(bytes);
}
}
}Imports Veryfi
Imports Microsoft.AspNetCore.Mvc
' ベリーフィ webhook receiver — required for high-volume reliable processing
<ApiController>
<Route("webhooks")>
Public Class VeryfiWebhookController
Inherits ControllerBase
Private ReadOnly _queue As IDocumentResultQueue
Public Sub New(queue As IDocumentResultQueue)
_queue = queue
End Sub
<HttpPost("veryfi")>
Public Function ReceiveWebhook(<FromBody> payload As VeryfiWebhookPayload,
<FromHeader(Name:="X-Veryfi-Token")> token As String) As IActionResult
' Validate webhook signature — prevents spoofed payloads
If Not IsValidSignature(token, payload) Then
Return Unauthorized()
End If
' Enqueue result for async downstream consumption
_queue.Enqueue(New DocumentResult With {
.DocumentId = payload.Id,
.Vendor = payload.Data?.Vendor?.Name,
.Total = payload.Data?.Total
})
Return Ok()
End Function
Private Function IsValidSignature(token As String, payload As VeryfiWebhookPayload) As Boolean
' HMAC validation against webhook secret — infrastructure requirement
Return token = ComputeHmac(payload, Environment.GetEnvironmentVariable("VERYFI_WEBHOOK_SECRET"))
End Function
End Class
' Document batch submission — fire and forget, results arrive via webhook
Public Class VeryfiDocumentBatchSubmitter
Private ReadOnly _client As VeryfiClient
Public Async Function SubmitBatchAsync(documentPaths As String()) As Task
For Each path In documentPaths
Dim bytes = File.ReadAllBytes(path)
' Submit — result arrives asynchronously via webhook, not here
Await _client.ProcessDocumentAsync(bytes)
Next
End Function
End ClassIronOCRのアプローチ:
using IronOcr;
using System.Text.RegularExpressions;
using System.Collections.Concurrent;
// なし webhook controller needed — results are synchronous and local
public class DocumentBatchProcessor
{
// IronTesseract is thread-safe when one instance is created per thread
public List<DocumentResult> ProcessBatch(string[] documentPaths)
{
var results = new ConcurrentBag<DocumentResult>();
Parallel.ForEach(documentPaths, documentPath =>
{
// One IronTesseract per thread — thread-safe pattern
var ocr = new IronTesseract();
var result = ocr.Read(documentPath);
results.Add(new DocumentResult
{
FilePath = documentPath,
Vendor = ExtractVendor(result),
Total = ExtractTotal(result.Text),
Confidence = result.Confidence,
// Result is available immediately — no queue, no webhook
ProcessedAt = DateTime.UtcNow
});
});
return results.OrderBy(r => r.FilePath).ToList();
}
private string ExtractVendor(OcrResult result)
{
// Vendor: first substantive paragraph ordered by vertical position
return result.Pages[0].Paragraphs
.OrderBy(p => p.Y)
.Select(p => p.Text.Trim())
.FirstOrDefault(t => !string.IsNullOrWhiteSpace(t) && t.Length > 3);
}
private decimal? ExtractTotal(string text)
{
var match = Regex.Match(text,
@"(?:Total|Grand Total|Amount Due):?\s*\$?\s*([\d,]+\.\d{2})",
RegexOptions.IgnoreCase);
return match.Success
? decimal.Parse(match.Groups[1].Value.Replace(",", ""))
: (decimal?)null;
}
}
public class DocumentResult
{
public string FilePath { get; set; }
public string Vendor { get; set; }
public decimal? Total { get; set; }
public double Confidence { get; set; }
public DateTime ProcessedAt { get; set; }
}Imports IronOcr
Imports System.Text.RegularExpressions
Imports System.Collections.Concurrent
' なし webhook controller needed — results are synchronous and local
Public Class DocumentBatchProcessor
' IronTesseract is thread-safe when one instance is created per thread
Public Function ProcessBatch(documentPaths As String()) As List(Of DocumentResult)
Dim results = New ConcurrentBag(Of DocumentResult)()
Parallel.ForEach(documentPaths, Sub(documentPath)
' One IronTesseract per thread — thread-safe pattern
Dim ocr = New IronTesseract()
Dim result = ocr.Read(documentPath)
results.Add(New DocumentResult With {
.FilePath = documentPath,
.Vendor = ExtractVendor(result),
.Total = ExtractTotal(result.Text),
.Confidence = result.Confidence,
' Result is available immediately — no queue, no webhook
.ProcessedAt = DateTime.UtcNow
})
End Sub)
Return results.OrderBy(Function(r) r.FilePath).ToList()
End Function
Private Function ExtractVendor(result As OcrResult) As String
' Vendor: first substantive paragraph ordered by vertical position
Return result.Pages(0).Paragraphs _
.OrderBy(Function(p) p.Y) _
.Select(Function(p) p.Text.Trim()) _
.FirstOrDefault(Function(t) Not String.IsNullOrWhiteSpace(t) AndAlso t.Length > 3)
End Function
Private Function ExtractTotal(text As String) As Decimal?
Dim match = Regex.Match(text, "(?:Total|Grand Total|Amount Due):?\s*\$?\s*([\d,]+\.\d{2})", RegexOptions.IgnoreCase)
Return If(match.Success, Decimal.Parse(match.Groups(1).Value.Replace(",", "")), CType(Nothing, Decimal?))
End Function
End Class
Public Class DocumentResult
Public Property FilePath As String
Public Property Vendor As String
Public Property Total As Decimal?
Public Property Confidence As Double
Public Property ProcessedAt As DateTime
End ClassWebhookレイヤーを削除することで、HTTPSエンドポイント、Webhookシークレットのローテーション要件、結果キュー、HMAC検証ロジック、およびリトライ設定が不要になります。 下流のインフラ全体が存在するのは、Veryfiの結果がリモートサーバーから非同期で届くためです。 IronOCRでは、Parallel.ForEachがすべてを置き換えます。 マルチスレッド例は、スレッドごとのIronTesseractパターンを詳細に示し、非同期OCRガイドはUIの応答性のためのTask.Run統合を取り扱います。 この速度最適化ガイドでは、バッチワークロードで最大のスループットを実現するためのインスタンス構成について解説しています。
ベリーフィ API から IronOCR へのマッピングリファレンス
| ベリーフィ | IronOCR相当値 |
|---|---|
new VeryfiClient(clientId, clientSecret, username, apiKey) | new IronTesseract() + IronOcr.License.LicenseKey = "key" |
_client.ProcessDocumentAsync(bytes) | ocr.Read(ocrInput) |
_client.ProcessDocumentAsync(bytes, categories: new[] { "invoices" }) | input.LoadPdf(path); ocr.Read(input) |
_client.ProcessDocumentAsync(bytes, categories: new[] { "bank_statements" }) | input.LoadPdf(path); ocr.Read(input) |
response.Vendor?.Name | 最初の段落はresult.Pages[0].Paragraphsから並べ替えられています |
response.Total | Regex.Match(result.Text, @"Total:?\s*\$?([\d,]+\.\d{2})") |
response.Tax | Regex.Match(result.Text, @"Tax:?\s*\$?([\d,]+\.\d{2})") |
response.Date | Regex.Match(result.Text, @"\d{1,2}/\d{1,2}/\d{4}") |
response.LineItems | result.Pages[0].ParagraphsはY座標範囲でフィルタリングされています |
response.InvoiceNumber | Regex.Match(result.Text, @"Invoice\s*#?\s*:?\s*(\w+[-\w]*)") |
response.BankAccount?.AccountNumber | Regex.Match(result.Text, @"Account\s*#?\s*:?\s*(\d{4,})") |
response.BankAccount?.RoutingNumber | Regex.Match(result.Text, @"Routing\s*#?\s*:?\s*(\d{9})") |
response.ConfidenceScore | result.Confidence (全体) または word.Confidence (単語ごと) |
response.Payment?.Last4 | Regex.Match(result.Text, @"\*{4}\s*(\d{4})") |
VeryfiApiException (401/402/429/500) | 標準的な .NET Standard 例外 - ローカル処理における HTTP エラーコードは使用しない |
| アップロード前にBase64エンコードする | 必要ありません - ocr.Read(filePath)はファイルパスを直接受け付けます |
response.Category | result.Textに対するカスタムキーワードマッチング |
| Webhookペイロードのデシリアライズ | 必要ありません - ocr.Read()は結果を同期して返します |
ProcessDocumentAsyncは再試行/バックオフと共に | 必須ではありません - ローカル処理におけるレート制限はありません |
一般的な移行の問題と解決策
課題 1: 事前解析済みフィールドの欠落
Veryfi: response.LineItemsは、事前に訓練されたMLモデルからの構造化フィールドとして到着します。 クライアント側でのデータ抽出ロジックは不要です。
解決策:アプリケーションが使用する各フィールドに対して正規表現(Regex)パターンを記述してください。 移行にかかる時間は、処理するドキュメントのレイアウトの種類数に応じて、通常8~24時間程度です。 一般的な領収書や請求書のパターンについては、請求書OCRチュートリアルおよび領収書スキャンチュートリアルで、完全な抽出パターンの実装例が提供されています。
// Map each ベリーフィ field to a Regex extraction
private static readonly Dictionary<string, string> FieldPatterns = new()
{
["InvoiceNumber"] = @"Invoice\s*#?\s*:?\s*(\w+[-\w]*)",
["PurchaseOrder"] = @"(?:PO|P\.O\.|Purchase Order)\s*#?\s*:?\s*(\w+)",
["DueDate"] = @"Due\s*(?:Date)?:?\s*(\d{1,2}/\d{1,2}/\d{4})",
["PaymentTerms"] = @"(?:Terms|Net)\s*:?\s*(\w+\s*\d+)"
};
public string ExtractField(string text, string fieldName)
{
if (!FieldPatterns.TryGetValue(fieldName, out var pattern))
return null;
var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase);
return match.Success ? match.Groups[1].Value.Trim() : null;
}' Map each ベリーフィ field to a Regex extraction
Private Shared ReadOnly FieldPatterns As New Dictionary(Of String, String) From {
{"InvoiceNumber", "Invoice\s*#?\s*:?\s*(\w+[-\w]*)"},
{"PurchaseOrder", "(?:PO|P\.O\.|Purchase Order)\s*#?\s*:?\s*(\w+)"},
{"DueDate", "Due\s*(?:Date)?:?\s*(\d{1,2}/\d{1,2}/\d{4})"},
{"PaymentTerms", "(?:Terms|Net)\s*:?\s*(\w+\s*\d+)"}
}
Public Function ExtractField(text As String, fieldName As String) As String
Dim pattern As String = Nothing
If Not FieldPatterns.TryGetValue(fieldName, pattern) Then
Return Nothing
End If
Dim match = Regex.Match(text, pattern, RegexOptions.IgnoreCase)
Return If(match.Success, match.Groups(1).Value.Trim(), Nothing)
End Function課題 2: コードベース全体における非同期メソッドのシグネチャ
Veryfi: ProcessDocumentAsyncはVeryfi SDKレベルで非同期です。 チームは通常、async Task<t>シグネチャを持ちます。
ソリューション: IronOCRのRead()は同期です。 既存のTask.Runでラップすることで保持できます。 これにより、クラウドへの依存を排除しつつ、コードベース全体での大規模な署名変更を回避できます。
// Preserve async signature during transition — no codebase-wide refactor needed
public async Task<string> GetVendorNameAsync(string documentPath)
{
return await Task.Run(() =>
{
var result = _ocr.Read(documentPath);
return result.Pages[0].Paragraphs
.OrderBy(p => p.Y)
.Select(p => p.Text.Trim())
.FirstOrDefault(t => t.Length > 3);
});
}Public Async Function GetVendorNameAsync(documentPath As String) As Task(Of String)
Return Await Task.Run(Function()
Dim result = _ocr.Read(documentPath)
Return result.Pages(0).Paragraphs _
.OrderBy(Function(p) p.Y) _
.Select(Function(p) p.Text.Trim()) _
.FirstOrDefault(Function(t) t.Length > 3)
End Function)
End Function課題 3: 環境ごとに分散している認証情報の設定
Veryfi: 4つの資格情報(appsettings.json、Docker Composeファイルの環境変数ブロック、GitHub Actionsの秘密情報、Azure Key Vaultの参照、CI/CDパイプライン構成に現れます。
解決策:すべての環境から、4つの認証情報エントリをすべて検索して削除してください。 単一のIRONOCR_LICENSE_KEY環境変数を追加します。 起動時に読み込む。
# Find all ベリーフィ credential references
grep -r "Veryfi:ClientId\|Veryfi:ClientSecret\|Veryfi:Username\|Veryfi:ApiKey" \
--include="*.json" --include="*.yml" --include="*.yaml" --include="*.env" .
// Load from environment at startup
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
?? throw new InvalidOperationException("IRONOCR_LICENSE_KEY not set");' Load from environment at startup
IronOcr.License.LicenseKey = If(Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY"), Throw New InvalidOperationException("IRONOCR_LICENSE_KEY not set"))課題 4: これまで確認できなかったスキャン品質の問題
Veryfi: クラウド処理には、ML推論の実行前にサーバー側で行う画像補正が含まれます。 品質の低いレシートスキャン(しわくちゃな紙、色あせた感熱印刷、傾いたスマホの写真など)は、フィールド抽出の前に自動的に補正されました。
解決策:IronOCRの前処理パイプラインを明示的に適用する。 Contrast()は現実の領収書スキャン品質問題の大部分をカバーします。
using var input = new OcrInput();
input.LoadImage("receipt-phone-photo.jpg");
input.Deskew(); // correct rotation from angled phone capture
input.DeNoise(); // remove compression artifacts
input.Contrast(); // improve faded thermal print
input.Sharpen(); // recover edge detail
var result = _ocr.Read(input);Using input As New OcrInput()
input.LoadImage("receipt-phone-photo.jpg")
input.Deskew() ' correct rotation from angled phone capture
input.DeNoise() ' remove compression artifacts
input.Contrast() ' improve faded thermal print
input.Sharpen() ' recover edge detail
Dim result = _ocr.Read(input)
End Using画像品質補正ガイドおよび画像フィルターチュートリアルでは、特定のスキャン劣化パターンに対してどのフィルターを適用すべきかについて解説しています。
課題 5: 大容量バッチ処理のスループット
Veryfi: レート制限により、ドキュメントの送信速度が調整されます。 HTTP 429レスポンスには、指数関数的バックオフロジックが必要です。 スループットは、お客様のハードウェアではなく、Veryfiの各プランごとのレート制限によって制限されます。
解決策:IronOCRのパフォーマンスはCPUコア数にのみ制限されます。 スレッドごとに1つのParallel.ForEachを使用します。 8コアのサーバーでは、スループットはコア数にほぼ比例して増加します。
// One IronTesseract per thread — do not share instances across threads
Parallel.ForEach(
documentPaths,
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
path =>
{
var ocr = new IronTesseract();
var result = ocr.Read(path);
SaveResult(path, result.Text, result.Confidence);
});Parallel.ForEach(
documentPaths,
New ParallelOptions With { .MaxDegreeOfParallelism = Environment.ProcessorCount },
Sub(path)
Dim ocr As New IronTesseract()
Dim result = ocr.Read(path)
SaveResult(path, result.Text, result.Confidence)
End Sub)課題 6: ベリーフィ にロックされた独自の JSON スキーマ
Veryfi: すべての抽出コードはVeryfiのレスポンススキーマから読み取ります: response.BankAccount?.RoutingNumber。 このコードは、VeryfiのSDKでのみ動作します。 ベリーフィ APIの更新に伴うフィールド名の変更は、アプリケーションのコードに不具合を引き起こします。
ソリューション: IronOCR抽出はプレーンテキストに対して標準 for .NET System.Text.RegularExpressions.Regexを使用します。 これらのパターンは移植性が高く、SDKをモックすることなくテスト可能であり、完全に制御可能です。 ユニットテストは、ネットワーク接続なしで実行されます。
// Extraction logic that is fully portable and unit-testable
[Fact]
public void ExtractsRoutingNumberFromInvoiceText()
{
const string sampleText = "Routing Number: 021000021\nAccount: 1234567890";
var match = Regex.Match(sampleText, @"Routing\s*(?:Number)?:?\s*(\d{9})",
RegexOptions.IgnoreCase);
Assert.True(match.Success);
Assert.Equal("021000021", match.Groups[1].Value);
}<Fact>
Public Sub ExtractsRoutingNumberFromInvoiceText()
Const sampleText As String = "Routing Number: 021000021" & vbLf & "Account: 1234567890"
Dim match = Regex.Match(sampleText, "Routing\s*(?:Number)?:?\s*(\d{9})", RegexOptions.IgnoreCase)
Assert.True(match.Success)
Assert.Equal("021000021", match.Groups(1).Value)
End Subベリーフィ 移行チェックリスト
移行前
コードに手を加える前に、コードベースを監査して ベリーフィ の使用箇所をすべて把握してください:
# Find all ベリーフィ using statements
grep -rn "using Veryfi" --include="*.cs" .
# Find all VeryfiClient instantiations
grep -rn "VeryfiClient\|ProcessDocumentAsync" --include="*.cs" .
# Find all ベリーフィ response field accesses
grep -rn "response\.Vendor\|response\.Total\|response\.LineItems\|response\.BankAccount" --include="*.cs" .
# Find all credential configuration references
grep -r "Veryfi:ClientId\|Veryfi:ClientSecret\|Veryfi:Username\|Veryfi:ApiKey" \
--include="*.json" --include="*.yml" --include="*.yaml" --include="*.env" .
# Find all webhook-related code
grep -rn "VeryfiWebhook\|X-Veryfi-Token\|webhook" --include="*.cs" .
合計ProcessDocumentAsync呼び出しサイトの数、各呼び出しサイトでアクセスされたレスポンスフィールドのリスト、およびVeryfi資格情報を含む環境のリストを記録します。
コードの移行
- ソリューション内のすべてのプロジェクトから
VeryfiNuGetパッケージを削除します。 - 以前に
IronOcrNuGetパッケージをインストールします。 - アプリケーションの起動時(OCRコールの前)に
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";を追加します。 - すべての
using IronOcr;に置き換えます。 IronTesseractのフィールド初期化に置き換えます。- すべての
appsettings.*.json、および秘密設定ファイルから4つのVeryfi資格情報エントリを削除します。 ocr.Read(ocrInput)に変換します。result.Pages[0].Paragraphsからの段落順テキスト抽出に置き換えます。result.Textに対するRegexパターンに置き換えます。result.Pages[0].Paragraphsイテレーションに置き換えます。- Webhook コントローラークラスを削除し、Webhook エンドポイントの登録を解除します。
- すべての環境から Webhook シークレット環境変数を削除してください。
- スキャンされた画像入力に対する前処理(
OcrInputを追加します。 - シングルスレッドの逐次ループを、スレッドごとに1つの
Parallel.ForEachに置き換えます。 - すべての環境変数設定とCI/CD秘密ストアに
IRONOCR_LICENSE_KEYを追加します。
移行後
- 移行後のデプロイ後、HTTPトラフィックログにVeryfiネットワークへの呼び出しが一切含まれていないことを確認してください。
- 20~50件のレシートからなるサンプルセットにおいて、抽出されたベンダー名が期待される値と一致していることを確認してください。
- 同じサンプルセットにおいて、抽出された合計値が、許容誤差±0.01ドル以内で期待値と一致することを確認してください。
- 文書コーパス内の各請求書フォーマットについて、請求書番号の抽出が正常に行われることを確認してください。
- レート制限の解除を確認するため、バッチ処理のスループットをVeryfiのベースラインスループットと比較テストする。
- ネットワーク接続なしでテストSuite全体を実行し、クラウドへの依存が一切ないことを確認します。
- クリーンなドキュメントスキャンに対して
result.Confidenceスコアが80%を超えているか確認します。 80%未満の場合は、前処理ステップを追加する必要があります。 - すべての環境(開発、ステージング、本番)から、4つのVeryfi認証情報がすべて削除されていることを確認してください。
- Webhookエンドポイントが404を返すか、ルーティングテーブルから削除されていることを確認してください。
- 前処理パイプラインを有効にした状態で、品質の低いレシートスキャン(しわくちゃ、色あせ、歪み)に対する動作をテストします。
IronOCRへの移行の主なメリット
ローカルで処理される財務文書は、第三者による不正アクセスを防ぐことができます。移行後は、請求書から抽出された銀行口座番号、小切手から解析されたルーティング番号、および銀行取引明細書から読み込まれた取引履歴が、すべてお客様のハードウェア上で処理されます。 サードパーティによるセキュリティインシデント、下請け業者によるデータアクセス、またはVeryfiのインフラストラクチャへの侵害があっても、お客様のサーバーから一度も外部に持ち出されていないドキュメントが漏洩することはありません。
移行がデプロイされた日をもって、文書ごとのコストはゼロになります。月間50,000件の文書処理において、月額5,000~15,000ドルのVeryfiの経費項目は消滅します。 2,999ドルのIronOCR Professional Licenseは、導入後最初の月の第1週で回収できます。 大量利用の場合、ボリュームディスカウントの交渉や契約更新を行うことなく、毎年節約効果が積み上がっていきます。
処理スループットは、ベンダーのレート制限ではなく、ハードウェアに応じて拡張します。HTTP 429レスポンス、プランレベルのスループット上限、季節的な超過料金は、クラウドAPIのアーキテクチャに起因するものです。IronOCRでは、CPUコアを追加することで、スループットが比例して向上します。 10,000件の領収書バッチは、Veryfiのレート制限スケジュールではなく、お客様のタイムラインに従って処理されます。
あらゆる種類の文書を同一のAPIで処理できます。人事部門が入社手続き用フォームの処理を依頼したり、法務部門が契約書のテキスト抽出を必要としたり、運用部門が配送書類のデータを必要としたりする場合でも、組織は別のOCRツールを用意する必要がなくなります。 ocr.Read()はすべてを処理します。 画像からテキストを読み取るチュートリアルや専門的なドキュメントガイドでは、IronOCRが対応するあらゆるドキュメント形式について網羅的に解説しています。
抽出ロジックは、コードベースの重要な構成要素となります。正規表現パターンはソース管理下にあり、プルリクエストでレビュー可能で、SDKをモックすることなくユニットテストで検証でき、本番環境からのフィードバックに基づいて調整可能です。 Veryfiの事前学習済みモデルが誤ったベンダー名を返した場合、調整すべき点はありません。 IronOCRの抽出パターンが誤ったベンダー名を返した場合、修正はユニットテスト付きの1行の正規表現変更で済みます。IronOCRのライセンスページでは、永続ライセンス購入よりも年次課金を希望するチーム向けのSaaSサブスクリプションを含む、各種プランのオプションについて説明しています。
導入に必要なリソースは、どこでも実行可能な単一の NuGet パッケージに縮小されます。IronOCR は、外部依存関係、ネイティブバイナリの管理、tessdata フォルダーの設定を一切必要とせず、1 つのパッケージとしてインストールされます。 同じパッケージ参照は、プラットフォーム固有のコードを記述することなく、Windows、Linux、macOS、Docker、Azure App Service、およびAWS Lambdaで解決されます。 Veryfiのネットワークエグレス要件がデプロイの障害となるコンテナ化環境については、DockerデプロイガイドおよびLinuxデプロイガイドを参照してください。
