Skip to content

fix: handle empty batches in detection and end-to-end predictors - #2138

Open
linhongyu510 wants to merge 1 commit into
mindee:mainfrom
linhongyu510:fix/empty-batch-predictors
Open

fix: handle empty batches in detection and end-to-end predictors#2138
linhongyu510 wants to merge 1 commit into
mindee:mainfrom
linhongyu510:fix/empty-batch-predictors

Conversation

@linhongyu510

Copy link
Copy Markdown

DetectionPredictor, OCRPredictor and KIEPredictor raise on an empty page list instead of returning an empty result. RecognitionPredictor has always short-circuited on empty input (recognition/predictor/pytorch.py:50), and OrientationPredictor gained the same behaviour in #2069 — these three were the remaining gap, so this follows that precedent rather than introducing a new convention.

Filtering a batch down to nothing is ordinary caller code (skip already-processed pages, drop files that failed a check upstream), and today it fails from internals that never mention the empty input.

Reproduction

from doctr.models import ocr_predictor, kie_predictor, detection

ocr_predictor(pretrained=False)([])       # IndexError: list index out of range
kie_predictor(pretrained=False)([])       # IndexError: list index out of range
detection.detection_predictor(pretrained=False)([])  # IndexError

There are three separate failure points on the way down, which is why the fix is not a one-liner in a single helper:

Location Failure
preprocessor/pytorch.py:73 num_batches is correctly 0, then samples[0] is read to pick the tuple/tensor branch → IndexError
utils/geometry.py:124 zip(*(_detach(box) for box in boxes)) over no boxes → ValueError: not enough values to unpack (expected 2, got 0)
models/_utils.py:276 KIE path only: {k: ... for k in x[0]}IndexError

I confirmed the ordering by fixing them one at a time: guarding batch_inputs moved the crash to detach_scores, and guarding that one let the whole call return Document(pages=[]).

Fix

Guard at the three public entry points instead of patching each internal, so batch_inputs, detach_scores and invert_data_structure keep their non-empty precondition and stay simple.

Two details worth flagging:

  • DetectionPredictor returns the shape its own return_maps contract promises: [] normally, ([], []) when return_maps=True.
  • KIEPredictor returns KIEDocument(pages=[]), not Document(pages=[]). KIEDocument subclasses Document, so returning the base class would type-check and pass an isinstance assertion while silently dropping the per-class page shape. The test asserts the exact type for this reason — I verified it catches the degradation by deliberately returning Document there and watching the test go red.

Tests

Added test_predictors_on_empty_batch, which covers detection (both return_maps values), recognition (already-correct, asserted so the three stay consistent), and both end-to-end predictors.

The test is load-bearing: reverting the guards fails it at preprocessor/pytorch.py:73.

tests/pytorch/test_models_zoo_pt.py::test_predictors_on_empty_batch  1 passed
tests/pytorch/test_models_zoo_pt.py                                 14 passed, 6 errors
tests/common/                                                        523 passed, 1 failed, 7 errors
ruff check . / ruff format --check                                   clean
mypy doctr/                                                          clean (171 source files)

The errors and the one failure are pre-existing on this machine and unrelated: the errors are requests.exceptions.ConnectionError from tests that download weights or fixtures, and tests/common/test_io.py::test_read_html fails with OSError: cannot load library (missing WeasyPrint system libs). I verified the test_read_html failure reproduces on a clean checkout via git stash.

AI assistance was used for this change; I reviewed every changed line and ran the commands above locally.

Passing an empty page list crashed three levels deep instead of returning an
empty result. `RecognitionPredictor` has always short-circuited on an empty
input, and `OrientationPredictor` gained the same behaviour in mindee#2069, but the
detection and end-to-end predictors did not, so filtering a batch down to
nothing raised from internals that never mention the empty input:

- `PreProcessor.batch_inputs` computes `num_batches == 0` correctly, then
  reads `samples[0]` to pick the tuple/tensor branch -> `IndexError`
- `detach_scores` calls `zip(*(...))` over no boxes -> `ValueError: not enough
  values to unpack (expected 2, got 0)`
- on the KIE path `invert_data_structure` reads `x[0]` -> `IndexError`

Guard at the three public entry points rather than patching each internal, so
the existing helpers keep their non-empty precondition. `DetectionPredictor`
returns the shape its `return_maps` contract promises, and the end-to-end
predictors return an empty document of their own type -- `Document` for
`OCRPredictor`, `KIEDocument` for `KIEPredictor`, whose per-class page shape
would otherwise be lost to the base class.

Verified `ruff check`, `ruff format --check` and `mypy doctr/` clean; reverting
the guards turns the new test red at `preprocessor/pytorch.py:73`.

Co-authored-by: Claude <noreply@anthropic.com>
@linhongyu510
linhongyu510 force-pushed the fix/empty-batch-predictors branch from 7d8556e to a11910c Compare September 5, 2026 08:42
@codecov

codecov Bot commented Sep 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.24%. Comparing base (b50ddd1) to head (a11910c).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2138   +/-   ##
=======================================
  Coverage   97.24%   97.24%           
=======================================
  Files         169      169           
  Lines       10040    10046    +6     
=======================================
+ Hits         9763     9769    +6     
  Misses        277      277           
Flag Coverage Δ
unittests 97.24% <100.00%> (+<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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant