---
title: "Extract a table from an image in C#"
url: https://recursiveai.org/oneocr/cookbook/extract-table-from-image/
description: "Extract a table from an image in C#: OCR finds the rows and columns, and you get each cell’s text, a CSV, or a Markdown table. Local, no cloud service."
version: "1.0.0"
---

# Extract a table from an image in C#

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.

```shell
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`:

```csharp
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:

```csharp
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](https://recursiveai.org/oneocr/cookbook/invoices/). The rules for tables are in [Table structure](https://recursiveai.org/oneocr/docs/tables/).
