Skip to content

[models] Improve line grouping and reading order for skewed pages, multi-column layouts and receipts - #2167

Merged
felixdittrich92 merged 4 commits into
mindee:mainfrom
felixdittrich92:order-improvements
Oct 26, 2026
Merged

felixdittrich92 merged 4 commits into
mindee:mainfrom
felixdittrich92:order-improvements

Conversation

@felixdittrich92

@felixdittrich92 felixdittrich92 commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

What this PR does

Improves how words are grouped into lines and how lines are put in reading order, mainly for skewed scans, multi-column pages (with and without the layout model) and receipts.

Line grouping (DocumentBuilder)

  • New row grouping (_resolve_rows): a word joins the row whose horizontally closest words are best aligned with it, instead of comparing against the running mean of the whole row. This follows slanted lines (skew, perspective), keeps the rows of columns with offset baselines apart, and stops a label stacked under another (e.g. figure labels) from joining a row further right.
  • Layout-aware line splitting: with a layout, a line is also split between two distinct layout regions, so columns separated by a gutter as narrow as a word gap are no longer merged. Overlapping regions with the same label count as one region.
  • Break distance: a box nested in a wider one no longer inflates the gap to the next word, and paragraph_break is scaled up for large type.

Reading order (doctr.models.reading_order)

  • An element shifted between two rows (e.g. an amount on a crumpled receipt) only waits for the row it overlaps most.
  • Elements crossing the column gutter (e.g. a centered title) no longer merge the columns.
  • Receipts: a column split that only separates the values of key-value rows from their items (short values, level with their item and far from it, on a short page) no longer makes the page multi-column, so the amounts stay with their items.
  • resolve_reading_segments: segments respect layout regions, and the fragments of a line split at a wide word gap rejoin their paragraph (new optional row_gap argument).

Exports

  • Page-wide tables no longer vote in the multi-column detection.
  • Receipts: the text lines of a page-wide table region don't vote either, so a receipt detected as one table is read row by row.
  • Text in a table or figure region without recognized structure keeps its label and is rendered one paragraph per row.
  • A block counts as figure text only if each of its lines lies in the figure.

Benchmark

The models ran once per page and the same raw predictions were replayed through the base (434145c) and this branch, so every difference comes from this PR. All rows use detect_layout=True.

Datasets:

  • Original (365 pages): CORD and SROIE receipts, XFUND-de forms, the English OmniDocBench demo pages, multi-column PDFs from the pdf.js corpus, and copies of those PDFs rotated by ±1–2°.
  • Held-out (169 pages): CORD train receipts, XFUND-de train forms and new multi-column PDFs, captured after all thresholds were chosen.

Reading-order edit distance: 0 = perfect order.

Base This PR
Reading-order edit distance (original / held-out) 0.311 / 0.523 0.153 / 0.119
Pages better / worse (original / held-out) 132 / 5, 112 / 3
Out-of-order jumps (original / held-out) 2035 / 1320 1024 / 171
Ground-truth lines read intact (original / held-out) 84.5% / 80.2% 96.4% / 96.4%
Text error, page.render() vs ground truth (original / held-out) 0.384 / 0.537 0.247 / 0.167

Edit distance per dataset:

Dataset Base This PR
OmniDocBench, incl. a 3-column Federal Register page 0.173 0.020
pdf.js multi-column PDFs 0.241 0.168
Skewed PDFs (±1–2°) 0.421 0.256
CORD receipts 0.465 0.058
Held-out multi-column PDFs 0.751 0.302
Held-out CORD receipts 0.461 0.074

Without the layout model the edit distance drops from 0.313 to 0.167 (original) and from 0.527 to 0.222 (held-out). On SROIE and XFUND-de the results are unchanged within ±0.2 points.

Builder, reading order and exports take about 10 ms per page in both versions.

Tests

  • New cases in test_resolve_lines: offset columns, perspective, ±2° skew, nested boxes, stacked figure labels, table rows, large type.
  • New tests for layout-aware line splitting, overlapping regions, the gutter-crossing title, table regions without structure, figure text, a real receipt's amount column, and a receipt detected as one page-wide table.
  • Fuzzing: 3,000 builder runs on random, rotated, duplicated, tiny and dense pages produced no new exceptions.

Known limitations

  • On one skewed page (2-column paper, +2°, layout on), labels of a page-wide figure are merged into a line that crosses the gutter, so part of the right column is read before the end of the left one (edit distance 0.38 → 0.62).
  • Without the layout model, the held-out multi-column PDFs are unchanged overall (4 pages better, 6 worse). The gains there come with detect_layout=True.

@codecov

codecov Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.15668% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.69%. Comparing base (4abce0e) to head (e25d487).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
doctr/models/builder.py 98.24% 2 Missing ⚠️
doctr/models/reading_order/base.py 97.50% 2 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main    #2167    +/-   ##
========================================
  Coverage   97.69%   97.69%            
========================================
  Files         170      170            
  Lines       10744    10922   +178     
========================================
+ Hits        10496    10670   +174     
- Misses        248      252     +4     
Flag Coverage Δ
unittests 97.69% <98.15%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@felixdittrich92 felixdittrich92 self-assigned this Oct 26, 2026
@felixdittrich92 felixdittrich92 added this to the 1.2.0 milestone Oct 26, 2026
@felixdittrich92 felixdittrich92 added type: enhancement Improvement module: models Related to doctr.models module: io Related to doctr.io ext: tests Related to tests folder topic: reading order Related to the task of reading order labels Oct 26, 2026
@felixdittrich92 felixdittrich92 changed the title test reading order updates [models] Improve line grouping and reading order for skewed pages, multi-column layouts and receipts Oct 26, 2026
@felixdittrich92
felixdittrich92 marked this pull request as ready for review October 26, 2026 10:23
@felixdittrich92
felixdittrich92 merged commit 6c46df2 into mindee:main Oct 26, 2026
56 of 57 checks passed
@felixdittrich92
felixdittrich92 deleted the order-improvements branch October 26, 2026 10:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ext: tests Related to tests folder module: io Related to doctr.io module: models Related to doctr.models topic: reading order Related to the task of reading order type: enhancement Improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant