Read an invoice image into typed fields with their EN 16931 business-term ids, line items included, and see which words and box each value came from.
Extraction needs the extraction model, which ships in its own package:
dotnet add package RecursiveAI.OneOcr --version 1.0.0 dotnet add package RecursiveAI.OneOcr.Models.Documents --version 1.0.0
A small layout-aware model tags the words of the page, and deterministic code assembles the fields from the tagged words, so a value is always text that is on the page. The image is invoice.png from the sample images:
using OneImage; using OneOcr; using OneOcr.Documents; using OneOcr.Models.Documents; OcrEngine engine = OcrEngine.LoadBundled(); DocumentModels.Configure(); // the extraction model from RecursiveAI.OneOcr.Models.Documents, once Invoice invoice = Image.Load("invoice.png").ExtractInvoice(engine); Console.WriteLine($"{invoice.Number?.Value}, issued {invoice.IssueDate?.Value}, total {invoice.Total?.Value}"); Console.WriteLine($"{invoice.SellerName?.Value} -> {invoice.BuyerName?.Value}, IBAN {invoice.Iban?.Value}"); foreach (InvoiceLine line in invoice.Lines) Console.WriteLine($" {line.Description?.Text}: {line.Quantity?.Value} {line.Unit?.Text} x {line.UnitPrice?.Value} = {line.Amount?.Value}"); ExtractedField total = invoice.Extraction.Get("BT-112")!; Console.WriteLine($"BT-112 \"{total.Text}\" -> {total.Number}, words [{string.Join(", ", total.WordIndices)}], " + $"box ({total.Box.Left:F0}, {total.Box.Top:F0}), confidence {total.Confidence:F2}"); Console.WriteLine($"Due date: {invoice.DueDate?.Value ?? "null"}, delivery date: {invoice.DeliveryDate?.Value ?? "null"}");
INV-2041, issued 2026-10-14, total 215.78 Northwind Supply Ltd -> Blue Heron Bakery, IBAN GB33 BUKB 2020 1555 5555 55 Flour, 25 kg bag: 4 pcs x 31.50 = 126 Cane sugar, 10 kg: 2 pcs x 18.25 = 36.50 Baking parchment roll: 6 pcs x 7.40 = 44.40 BT-112 "£215.78" -> 215.78, words [83], box (2072, 1474), confidence 1.00 Due date: null, delivery date: 2026-11-13
What came back
- Each
ExtractedFieldhas the exactText, a normalizedValue(an ISO date, a plain decimal amount), typedNumberorDatewhere it applies, the indices of its words in the page and their box. invoice.Extraction.Get("BT-112")looks any field up by its business term:BT-1number,BT-2issue date,BT-27seller,BT-44buyer,BT-84IBAN,BT-106subtotal,BT-110tax,BT-112total, and line items fromBT-126toBT-153.- A field the model didn’t find is
null, never a guess. Here the model tagged the second date as the delivery date (BT-72) rather than the due date (BT-9), soDueDateisnull: read the fields you depend on, and look atConfidence, which is the mean probability of the field’s word tags (not a calibrated probability like a word’s). - Amounts are normalized to plain decimals:
126.00came back as126.
Good to know
ExtractInvoiceandExtractReceipttake aDocumentExtractionOptions(MaxDegreeOfParallelism,Strict) and haveAsynctwins.- For your own document type,
Extract<T>takes a source-generatedJsonTypeInfo<T>and a model you fine-tuned; the extraction model spec describes the model and the overview the API. - Receipts work the same way: see Receipts.