indic-doc-parser / ARCHITECTURE.md
spark-ux's picture
Copy from bodhan-ai/indic-doc-parser
7b2177e verified
|
Raw
History Blame Contribute Delete
3.99 kB
# Architecture
Two models with a JSON file between them.
```text
page image ──> IndicDocLayout ──> layout JSON ──> IndicBlockOCR ──> page JSON + markdown
```
`IndicDocLayout` finds the blocks and orders them. `IndicBlockOCR` transcribes one block crop
per request. Everything between the two models (cropping, prompt selection, reassembly) is this
package.
## The call path
One page, top to bottom, with the file that owns each step.
```text
IndicDocParser.parse(image) idp_offline.py
IndicDocLayout.detect(image) idp_offline.py
_open image -> PIL RGB idp_offline.py
IndicDocLayoutBackend.detect idp_layout.py
infer -> [y0,x0,y1,x1] at 0-1000 idp_model_infer.py
convert + clamp_to_page -> pixel [x0,y0,x1,y1] idp_layout.py, blocks.py
clean_layout drop duplicate boxes idp_blocks.py
_densify order -> gap-free 0..n-1 idp_layout.py
= PageResult, every block has text None idp_types.py
IndicBlockOCR.run(image, layout) idp_offline.py
_as_page dict -> PageResult, validated
is_transcribed drop OCR_SKIP_LABELS idp_contract.py
resolve_nested_equations drop nested equations idp_blocks.py
build_requests -> [CropRequest] idp_recognizer.py
crop_for + area_clamp block -> image idp_crops.py
prompt_for(block.type) type -> prompt idp_contract.py
backend.transcribe [CropRequest] -> [str] a recognizer backend
match by order texts -> blocks idp_offline.py
reconstruct blocks -> markdown idp_reconstruct.py
= PageResult, every block has text
```
Blocks that were skipped are not deleted. They come back with `text: ""`.
## Files by role
**Contract and data.** The vocabulary everything else shares.
| file | holds |
| --- | --- |
| `idp_contract.py` | prompts, the label to type map, which labels are skipped |
| `idp_types.py` | `Block`, `PageResult`, and every tunable config |
**The layout model.** Torch lives here and nowhere else.
| file | holds |
| --- | --- |
| `idp_model_infer.py` | preprocessing and decode for one page |
| `idp_model_labels.py` | the 37 classes, pure Python |
| `idp_model_ppdoc.py` | the PP-DocLayoutV3 subclass |
| `idp_model_order_loss.py` | reading-order decode, training loss |
**Geometry and crops.** No model, no network.
| file | holds |
| --- | --- |
| `idp_blocks.py` | box math, `clean_layout`, nested-equation dedup |
| `idp_crops.py` | `crop_for` and `area_clamp` |
**Backends.** Two Protocols, five implementations, one per deployment.
| class | file | used by |
| --- | --- | --- |
| `IndicDocLayoutBackend` | `idp_layout.py` | the real detector |
| `JsonLayoutBackend` | `idp_layout.py` | replaying a layout file, no torch |
| `HfRecognizer` | `idp_recognizer.py` | plain transformers, the Hub package |
**Orchestration.**
| file | holds |
| --- | --- |
| `idp_offline.py` | `IndicDocLayout`, `IndicBlockOCR`, `IndicDocParser` |
| `idp_recognizer.py` | `CropRequest`, `build_requests`, the recognizer Protocol |
| `idp_reconstruct.py` | blocks to markdown, math and hyphen repair |
## Invariants
1. **Two box conventions.** The model emits `[y0, x0, y1, x1]` normalised to 0-1000. The
pipeline uses pixel `[x0, y0, x1, y1]`.
2. **`order` must be gap-free and 0-based.** Transcriptions are matched back to blocks by
position, so a gap or a duplicate moves text onto the wrong block.
3. **The crop clamp is on area, not on a side.** Pinning a side explodes elongated crops. See
`CropConfig` in `idp_types.py`.
4. **`type` selects the prompt.** A wrong `type` changes what the model was asked to do, not
just how a block is labelled. Unrecognised labels are rejected for this reason.