Recursive AI

Extract a table from an image in C#

All oneocr recipes

OCR the image with Ocr, then read page.Tables: each table has its rows, columns and cells, and each cell lists the words in it. The program below turns the biggest table on a sample invoice, its items, into CSV.

Terminal
dotnet new console -o TableFromImage && cd TableFromImage
dotnet add package RecursiveAI.OneOcr --version 1.0.0
curl -O https://recursiveai.org/oneocr/samples/invoice.png

Put this in Program.cs and run dotnet run:

C#
using OneImage;
using OneOcr;
 
OcrEngine engine = OcrEngine.LoadBundled();
OcrPage page = Image.Load("invoice.png").Ocr(engine);
 
OcrTable table = page.Tables.MaxBy(t => t.Rows * t.Columns)!;
Console.WriteLine($"{page.Tables.Count} tables; the biggest is " +
    $"{table.Rows} rows x {table.Columns} columns");
 
var grid = new string[table.Rows, table.Columns];
foreach (OcrTableCell cell in table.Cells)
    grid[cell.Row, cell.Column] =
        string.Join(" ", cell.WordIndices.Select(i => page.Words[i].Text));
 
for (int r = 0; r < table.Rows; r++)
{
    var row = Enumerable.Range(0, table.Columns).Select(c => grid[r, c] ?? "");
    Console.WriteLine(string.Join(",", row.Select(Csv)));
}
 
static string Csv(string s) =>
    s.Contains(',') || s.Contains('"') ? $"\"{s.Replace("\"", "\"\"")}\"" : s;
Text
2 tables; the biggest is 4 rows x 5 columns
Description,Qty,Unit price,VAT,Amount
"Flour, 25 kg bag",4 pcs,31.50,0%,126.00
"Cane sugar, 10 kg",2 pcs,18.25,0%,36.50
Baking parchment roll,6 pcs,7.40,20%,44.40

The other table is the grid of invoice number and dates at the top right: aligned label–value pairs count as a table too. A cell’s WordIndices point into page.Words, so each cell also has its words’ boxes and confidences. Cells that span several rows or columns say so in RowSpan and ColumnSpan.

The table as Markdown

ToMarkdown writes the whole page as Markdown, with detected tables as pipe tables and headings from text size. Running headers and footers are left out, which is why the bank details at the foot of the page are missing. It is built from the page model, with no extra model call:

C#
using OneImage;
using OneOcr;
 
OcrEngine engine = OcrEngine.LoadBundled();
Console.WriteLine(Image.Load("invoice.png").ToMarkdown(engine));
Text
Northwind Supply Ltd
 
14 Harbour Street
 
BS1 4DJ Bristol
 
United Kingdom VAT ID: GB284193752
 
# INVOICE
 
Bill to
 
Blue Heron Bakery
 
88 Elm Road
 
OX1 2JD Oxford
 
United Kingdom
 
| Invoice no. | INV-2041 |
| --- | --- |
| Date | 14/10/2026 |
| Due date | 13/11/2026 |
| Order no. | PO-7731 |
 
| Description | Qty | Unit price | VAT | Amount |
| --- | --- | --- | --- | --- |
| Flour, 25 kg bag | 4 pcs | 31.50 | 0% | 126.00 |
| Cane sugar, 10 kg | 2 pcs | 18.25 | 0% | 36.50 |
| Baking parchment roll | 6 pcs | 7.40 | 20% | 44.40 |
 
Subtotal
 
VAT total
 
Total
 
£206.90
 
£8.88
 
£215.78
 
Payment terms: Payable within 30 days

Limits

  • Ruled and aligned tables. Tables are found from the layout of the words and lines; a table with no lines and ragged columns may come back as plain text blocks.
  • The first row isn’t marked as a header. In CSV it is just the first row; in Markdown the first row becomes the header.
  • Printed Latin, Greek and Cyrillic text, as for all of OneOcr.

To get the line items of an invoice as typed values instead of cells, see Extract data from an invoice. The rules for tables are in Table structure.