如何在 C# 中使用 Async 和多執行緒
從Mindee遷移到IronOCR將文件處理從外部雲端伺服器移到您的基礎設施上,消除了按頁計費、第三方資料傳輸和作為運行時依賴的網路可用性。 以下部分涵蓋封包替代、命名空間更新以及四個實際的前後程式碼案例:發票區域提取、多頁收據PDF處理、財務文件預處理和可搜尋PDF存檔。
為什麼要從Mindee遷移
Mindee優雅地解決了一個真正的問題:提交文件,接收結構化的JSON字段。 對於如果傳輸財務文件到外部API是可以接受的,而工作量保持在中等的團隊,它能提供快速結果。 當其中一個條件發生變化時,通常會觸發遷移。
財務資料跨越合規邊界。 Mindee的ParseAsync<InvoiceV4>會將完整的文件上傳到外部伺服器。 IBAN程式碼、路由號碼、供應商EINs和列出線目項目通過公共互聯網傳輸並在您無法控制的硬體上處理。 SOC 2型別II證書確認Mindee遵循安全實踐; 它不會將您的文件保存在您的安全邊界內。 當合規審查、新客戶合約或資料駐留法規對第三方雲端處理作出限制時,Mindee的架構即使在提取準確性上也是不合格的。
按頁費用不利於您。 起步計畫提供每月1,000頁,以49美元/月為單位。 專業計畫提供每月5,000頁,以499美元/月為單位。 多頁PDF每頁計數。 開發運行會計入配額。 一個團隊每月處理4,000份發票需要支付499美元/月——每年5,988美元——這個數字每年重置,並隨著任何業務增長而上升。 每月24,000頁時,您正在談判Enterprise定價。 IronOCR的永久授權為1,499美元(專業層級)涵蓋無限文件數量,並在不到四個月內回收其對Mindee專業的成本。
異步輪詢遍及您的堆棧。 每個Mindee操作都是異步的,因為每個操作都是網路請求。這種強制性異步鏈將向上級傳遞到控制器、服務層和工作類。 當網路不可用或Mindee的服務出現故障時,該瀑布效應會導致失敗,沒有本地備份。 IronOCR以同步方式處理——本地CPU工作——並在需要異步介面以保持架構一致性時封裝為Task.Run。
預先構建的API型錄有硬性上限。 Mindee涵蓋PassportV1,以及一個小型其他文件型別型錄。任何不在該列表中的文件型別需要Enterprise計畫的自定義模型訓練:樣本收集、標注、訓練時間以及API可供使用前的前導時間。 您的企業需要的每一種新文件型別都是一個單獨的供應商合作。 IronOCR處理任何型別的文件使用相同的IronTesseract().Read()調用; 新增新的文件型別意味著編寫提取模式,而不是協商訓練合同。
API密鑰管理增加了操作表面。 必須將Mindee憑證安全地儲存在伺服器端、定期旋轉並防止暴露。 網路出口規則必須允許對Mindee端點的輸出HTTPS。 重試邏輯必須處理MindeeException,這是不可避免的網路失敗。 移除Mindee將移除所有這些操作表面:無需憑證,無需出口規則,無需網路I/O的重試包裝。
隔離和受限環境被排除。 政府承包商、國防分包商、醫療網路以及沒有穩定互聯網連接的工業系統都無法按照設計使用Mindee。 IronOCR在沒有輸出存取的Docker容器中、在具有出口限制的Azure函式中以及在完全隔離的環境中運行一致。 運行時無需雲端依賴。請參閱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 toMindeecloud
// IronOCR: same invoice, same result, zero data transmission
var result = new IronTesseract().Read("invoice.pdf");
// All processing local — bank details never leave your machine
##IronOCRvs Mindee: 功能比較
下表捕捉了驅使遷移決策的架構和能力差異。
| 功能 | 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()) |
| 圖像預處理 | 無公開 | Deskew,DeNoise,Contrast,Binarize,Sharpen,Scale |
| 基於區域提取 | 響應中的字段座標 | 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:更新命名空間
用IronOCR命名空間替換Mindee命名空間塊:
// 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等效使用CropRectangle讀取文件的特定區域,匹配Mindee的提取空間意識,無需傳輸文件。
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 toMindee— 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通過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>();
//Mindeeprocesses 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用本地一次調用處理相同文件,無需每頁費用且無需每頁網路往返。 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)
{
//Mindeeapplies 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; }
}
兩遍模式——先快速閱讀,然後對低信心進行預處理——避免在乾淨的掃描上進行全面預處理的開銷。 對於應付帳款中最常見的掃描質量情況(略微傾斜的平板掃描,傳真工件,複印件發票),DeNoise()一起能恢復大部分的識別準確性。 圖像質量校正指南記錄了每個可用的過濾器,並提供了哪些組合最適合不同掃描缺陷的建議。 圖像方向校正指南涵蓋了對倒置掃描文件的自動旋轉功能。
財務文件可搜索PDF存檔
Mindee不產生任何PDF輸出。 其架構是僅輸入:提交文件,接收JSON字段。 以可搜索格式存檔掃描發票的團隊必須單獨維護該管道與Mindee的提取分開。 IronOCR直接從用於字段提取的相同識別通道生成可搜尋的PDF——無需額外的庫,也無需第二次處理步驟。
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 viaMindee(document transmitted to cloud)
var inputSource = new LocalInputSource(scanPath);
var response = await _client.ParseAsync<InvoiceV4>(inputSource);
var prediction = response.Document.Inference.Prediction;
//Mindeereturns 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查看器或企業內容管理系統中每個單詞都可選擇和搜尋。 字段提取和存檔在同一通道中運行——一個result.SaveAsSearchablePdf()以進行存檔。可搜索PDF指南涵蓋了輸出選項包括HOCR導出,PDF OCR用例頁面涵蓋了文件存檔管道的生產部署模式。
移除FinancialDocumentV1的自定義端點
Mindee的FinancialDocumentV1API通過自動檢測文件型別來處理發票和收據。 使用它的團隊在消除了分支邏輯的同時增加了更高的雲端依賴性——無論型別如何,每個文件都是上傳的。 將其替換為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的伺服器端result.Text。 對於大多數財務文件,"Invoice"或"Bill To"的存在可以明確識別發票; 不存在則識別為收據。如果您的文件集包括邊緣情況,result.Pages中的字級位置資料允許更複雜的基於佈局的檢測。 讀取結果指南解釋瞭整個物件模型,包括Paragraphs及其邊界坐標。
##MindeeAPI到IronOCR的映射參考
| Mindee | IronOCR 等效 |
|---|---|
new MindeeClient(apiKey) | new IronTesseract() — 無需API密鑰 |
new LocalInputSource(filePath) | new OcrInput() + 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) + 通過CropRectangle的MRZ區域提取 |
_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 | word.Confidence(按字) |
catch (MindeeException ex) | 無對應— 認識時無網路錯誤 |
await Task.Delay(100)(速率限制) | 無需—無速率限制 |
input.LoadPdf(path) | ocrInput.LoadPdf(path)—同樣操作,完全本地 |
| 配置中的API密鑰 | 通過IronOcr.License.LicenseKey = "..."授權密鑰—無需旋轉 |
常見的遷移問題与解決方案
問題1:CropRectangle坐標不匹配文件佈局
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的預處理管道。 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密鑰、允許到HttpRequestException的重試邏輯是所有必需的基礎設施。
解決方案: 這三者一起被移除。MindeeAPI密鑰條目從配置中刪除。 網路出口規則已撤銷。 移除了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: 上傳到Mindee的12頁供應商報表消耗了每月配額的12頁。 在專業層的超額費率下,如果接近限額,單個文件需額外支付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自動檢測文件型別並提取字段,而不對調用程式碼進行型別分支。
解決方案: 使用關鍵詞存在構建輕量級檢測器。 財務文件分為發票(包含"Invoice"、"Bill To"或"Due Date")和收據(包含"Receipt"、"Thank You"或缺少發票標記)。 兩種提取方法加上一個三行檢測器替換了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.*指令 - 將
IronTesseract實例化或DI註冊 - 將每個
_ocr.Read(filePath)加上發票提取方法 - 將每個
_ocr.Read(filePath)加上收據提取方法 - 使用
CropRectangleMRZ區域上的基於區域的OCR替換每個ParseAsync<PassportV1>調用 - 用文件型別檢測器+路由模式替換每個
ParseAsync<FinancialDocumentV1>調用 - 移除所有
await Task.Delay(...)速率限制合規行 - 從OCR路徑中移除所有
catch (HttpRequestException)塊 - 新增
Deskew,DeNoise,Contrast)以供掃描文件路徑使用 - 向任何文件存檔路徑新增
result.SaveAsSearchablePdf(archivePath) - 從所有配置文件和密鑰保管庫中移除Mindee 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程式碼、路由號碼、客戶EINs和列出項目項目永不離開您的基礎設施。 合規範圍僅涵蓋您的組織。 與第三方AI供應商的處理器協議被從您的審計軌迹中移除。 依GLBA、CMMC、FedRAMP或資料駐留要求運行的團隊可以處理金融文件而無需第三方雲端傳輸所需的合規審查。
**不管數量如何,成本都是可預測的。**IronOCRLite許可證於$999涵蓋一位開發者擁有無限文件處理。 從每月500份發票到每月50,000份發票的擴展不會產生任何額外的成本。 年末處理峰值、批量補救運行和開發測試周期不會增加任何計費計數器。 對於每月處理5,000頁的團隊來說,三年總擁有成本從大約$17,964(Mindee Pro)降至$1,499–$2,399(IronOCR永久許可證)。 參見完整的IronOCR產品頁面獲取層級詳細資訊。
永久擁有的提取邏輯。 IronOCR提取模式是您的儲存庫中的純C#程式碼。 它們是版本控制的、可審查的、可測試的,並在任何外部供應商的發布進度或API版本決定獨立可部署的。 當Mindee釋出InvoiceV4時,這是供應商施加的遷移期限。 當您維護提取模式時,改進是一個git commit。
現實世界掃描質量的預處理控制。 應付帳款操作從數十或數百個供應商那裡接收發票,每個供應商使用不同的裝置在不同的設置下掃描。 IronOCR的預處理管線,Binarize()針對每個掃描類別中的特定缺陷。 熱敏收據照片與平板掃描的多頁發票有不同的缺陷;您對每條路徑應用適當的過濾器。 Mindee的預處理是不可見的且不可配置的。 請參閱預處理功能頁以獲取完整的過濾器型錄。
一步即可實現可搜索的PDF存檔。 通過IronOCR處理的每張發票都可以通過result.SaveAsSearchablePdf()存檔為可搜索的PDF。 識別的文字層嵌入輸出文件中,使得每個單詞在文件管理系統、SharePoint庫和企業內容儲存庫中皆可全文檢索。 Mindee根本不提供PDF輸出; 可搜索的存檔先前需要單獨的庫、單獨的處理通道和額外的維護。 遷移後, 提取和存檔歸為一個_ocr.Read(input)調用。
無需架構變更即可部署到任何地方。 IronOCR可在Windows、Linux、macOS、Docker、Azure App Service、AWS Lambda和Google Cloud Run上使用相同的NuGet包和相同的初始化進行部署。 隔離環境、工廠車間系統和政府安全設施在不修改下運作一致。 Docker部署指南、Azure指南 和AWS指南涵蓋每個平台的環境特定配置。
