|
Download README.md from Kedhareswer/docparse: direct link, hf CLI and curl.
- Browser
- Download file 16 kB
-
https://huggingface.co/Kedhareswer/docparse/resolve/main/README.md
- Command line
-
hf download hf://Kedhareswer/docparse/README.md
-
curl -L -o README.md https://huggingface.co/Kedhareswer/docparse/resolve/main/README.md
16 kB
| license: mit | |
| library_name: pytorch | |
| pipeline_tag: image-to-text | |
| tags: | |
| - ocr | |
| - document-parsing | |
| - crnn | |
| - ctc | |
| - trained-from-scratch | |
| - cpu | |
| # docparse: a very small document parser | |
| - **Try it:** [huggingface.co/spaces/Kedhareswer/docparse](https://huggingface.co/spaces/Kedhareswer/docparse) (drop a PDF, image, Word, Excel, CSV or PowerPoint file) | |
| - **Weights and code:** [huggingface.co/Kedhareswer/docparse](https://huggingface.co/Kedhareswer/docparse): `artifacts/docparse/ocr.pt` (6.0 MB), the `docparse` package, benchmark reports and tests | |
| - **Source:** [github.com/Kedhareswer/ML-AI](https://github.com/Kedhareswer/ML-AI) (`reasoning_harness/`) | |
| `docparse` extracts **everything** from a document (text, headings, paragraphs, | |
| lists, tables, sheets, formulas, headers/footers, metadata and figure positions) | |
| and returns it as JSON, Markdown or plain text, with reading order preserved. | |
| | Input | How it is read | Exact? | | |
| |---|---|---| | |
| | PDF with a text layer | Words and fonts from the PDF; layout analysis groups them into headings, paragraphs, lists, columns and tables | Text is exact | | |
| | Scanned PDF pages (no text layer) | Rendered at 200 dpi, then the OCR pipeline below | OCR | | |
| | Images: PNG, JPG, TIFF (multi-page), BMP, WEBP, GIF | OCR pipeline | OCR | | |
| | Word `.docx` | Document structure: styles → headings/lists, tables (merged cells), text boxes, headers/footers, properties | Exact | | |
| | Excel `.xlsx` | Every sheet: values, formulas, merged ranges, dates, separate tables split on empty rows/columns | Exact | | |
| | CSV / TSV | Delimiter and encoding sniffed | Exact | | |
| | `.doc`, `.rtf`, `.odt`, `.xls`, `.ods`, `.ppt`, `.pptx`, `.odp` | Converted with LibreOffice (if installed), then parsed as above | Exact | | |
| The design choice: **a model is only used where one is needed.** Born-digital | |
| files already contain their text; reading their structure is exact and much | |
| faster than any network. The neural network reads pixels: images, scans and | |
| (optionally, `--ocr-images`) pictures embedded inside documents. | |
| ## The model | |
| A CRNN line recognizer trained **from scratch** (no pretrained weights) on a | |
| 4-core CPU: | |
| - **1,484,357 parameters, 6.0 MB** (`artifacts/docparse/ocr.pt`): 6 convolution | |
| layers, a 2-layer bidirectional LSTM and CTC decoding. | |
| - Input: a grayscale text line, 32 px high, any width. Output: the text plus a | |
| confidence (mean probability of the emitted characters). | |
| - Characters: printable ASCII plus `• € £ ° ©`. Typographic quotes, dashes and | |
| accented letters are folded to ASCII (`é` → `e`). | |
| - Training: 480,000 synthetic lines (4 rounds of 120,000 freshly rendered | |
| lines), 27 fonts (DejaVu, Liberation, FreeFont, Inter, JetBrains Mono), | |
| text from app reviews and medical abstracts in this repository, plus | |
| generated numbers, prices, dates, emails, URLs, IDs and list items. Lines are | |
| degraded like scans and screenshots: blur, low resolution, noise, JPEG, | |
| rotation, uneven light, inverted colours and table-rule remnants. Training | |
| took 80 minutes. | |
| Everything around the network is classical image processing (numpy only): | |
| adaptive binarization (dark-on-light and light-on-dark), speck removal, | |
| deskew, ruled-table detection from long horizontal/vertical strokes, frame | |
| and outline removal, recursive XY-cut for columns and lines, then grouping | |
| lines into headings (by x-height), paragraphs (by line spacing), list items, | |
| and borderless tables (rows whose gaps line up). | |
| ## Results | |
| All numbers are from `docparse bench` (`benchmarks/docparse/report.json`). | |
| CER = character error rate (edit distance / reference length). | |
| **1. Single lines.** Test fonts **Carlito and Caladea were never used in | |
| training** (they are metric clones of Calibri and Cambria, the Office | |
| defaults), and the test sentences come from a separate corpus. | |
| | Test set | Lines | CER | Word error | Lines exactly right | | |
| |---|---:|---:|---:|---:| | |
| | Held-out fonts, clean | 2,000 | **0.27%** | 2.3% | 93.3% | | |
| | Held-out fonts, degraded | 2,000 | **0.93%** | 6.0% | 84.8% | | |
| | Training fonts, unseen text, degraded | 2,000 | 0.24% | 1.2% | 95.6% | | |
| | Untrained network (same architecture), degraded | 500 | 99.7% | 99.8% | 0% | | |
| About 3 ms per line on CPU. | |
| **2. Synthetic A4 pages** (held-out fonts and text; titles, section headings, | |
| paragraphs, bullet lists, ruled tables, key/value tables, two-column sections). | |
| "Scanned" pages are rotated up to ±2°, blurred, noised and JPEG-compressed. | |
| | | Clean (40 pages) | Scanned (40 pages) | | |
| |---|---:|---:| | |
| | Page CER, in reading order (mean / median) | 0.32% / 0.00% | 1.65% / 0.19% | | |
| | Word F1 (order-free) | 98.9% | 97.7% | | |
| | Headings found | 86.7% | 85.7% | | |
| | Tables found with the right rows × columns | 100% | 100% | | |
| | Table cells exactly right | 98.5% | 90.2% | | |
| | Seconds per page (CPU) | 0.9 | 1.3 | | |
| A few hard pages pull the scanned mean up; the median page is almost perfect. | |
| Missed headings are mostly short numbered section titles ("1. Results") read | |
| as list items or paragraphs: headings are detected by letter size, not by | |
| boldness. | |
| **3. Real pages from this repository.** 40 pages of the three PDFs in this | |
| repo (two Jupyter notebook exports and a slide deck) were rendered to | |
| 150 dpi images and read **from pixels only**. Each PDF's own text layer is the | |
| reference. | |
| | PDF | Pages | Median CER (reading order) | Median word F1 | | |
| |---|---:|---:|---:| | |
| | Customer Behavior Analysis (notebook) | 15 | 0.7% | 92.5% | | |
| | Credit Card Fraud Detection (notebook) | 11 | 2.3% | 94.8% | | |
| | PPT.pdf (slides with graphics) | 14 | 32.3% | 82.5% | | |
| | **All** | **40** | — | **91.9%** (mean 86.3%) | | |
| Most slide-deck error is reading order: slides scatter text boxes, and the | |
| PDF's own order differs from top-to-bottom reading. Text inside charts (axis | |
| labels, tick numbers) is also read as text. The weakest notebook pages are | |
| mostly plots with a few short labels. | |
| ## ParseBench (LlamaIndex) | |
| [ParseBench](https://huggingface.co/datasets/llamaindex/ParseBench) | |
| ([paper](https://arxiv.org/abs/2604.08538), [code](https://github.com/run-llama/ParseBench)) | |
| is LlamaIndex's public benchmark for document parsers: **2,078 real enterprise | |
| pages** (insurance filings, financial reports, government documents, papers), | |
| 1,211 documents and 169,011 human-verified test rules in five dimensions. | |
| Scoring is deterministic rule matching, with no LLM judge. | |
| docparse was run on the **full** dataset with the official harness | |
| (parse-bench 1.0.4) on a 4-core CPU: all 2,078 pages, 0 failures, median | |
| 0.5 s per page. The comparison rows are the "Open Source - Local" entries of | |
| ParseBench's published leaderboard. | |
| | Parser | Overall | Tables | Charts | Content faithfulness | Semantic formatting | Visual grounding | | |
| |---|---:|---:|---:|---:|---:|---:| | |
| | PyMuPDF4LLM | 53.5 | 72.0 | 2.2 | 79.3 | 52.0 | 61.9 | | |
| | Warp Ingest | 40.2 | 57.5 | 7.0 | 70.8 | 45.8 | 19.8 | | |
| | LiteParse (no OCR) | 36.9 | 42.4 | 1.8 | 70.0 | 38.5 | 31.8 | | |
| | **docparse** | **36.2** | **42.1** | **1.0** | **72.5** | **35.5** | **30.0** | | |
| | OpenDataLoader | 29.4 | 35.2 | 0.9 | 66.1 | 34.1 | 10.8 | | |
| | pdf-inspector | 26.6 | 26.6 | 5.3 | 56.1 | 35.1 | 9.9 | | |
| | MarkItDown | 18.6 | 15.8 | 2.0 | 64.5 | 0.9 | 9.9 | | |
| | PyMuPDF (text) | 16.0 | 0.0 | 0.0 | 68.3 | 1.0 | 10.9 | | |
| | pypdf | 14.9 | 0.0 | 0.0 | 62.5 | 0.9 | 10.9 | | |
| Overall is the mean of the five dimensions, as on the official leaderboard. | |
| For scale: the top of the full leaderboard (cloud services and multi-billion | |
| parameter vision-language models) reaches 75–90. | |
| **What this says honestly** | |
| - docparse is mid-table among local open-source parsers: ahead of pypdf, | |
| PyMuPDF, MarkItDown, pdf-inspector and OpenDataLoader; just behind LiteParse | |
| (36.9); behind Warp Ingest and clearly behind PyMuPDF4LLM. | |
| - Its **content faithfulness (72.5)** is second among local parsers. It is | |
| strongest on simple (89.0) and multi-column (76.5) pages, and weakest on | |
| handwriting (27.4), scans (56.1) and non-English text (58.8). That matches the | |
| OCR model's limits: printed Latin text only. | |
| - **Charts (1.0):** ParseBench wants each chart's data points as a table. | |
| docparse finds charts and keeps their labels but does not convert them to | |
| data. No local parser scores above 7. | |
| - **Tables (42.1)** are limited by pdfplumber's ruled-grid detection on | |
| borderless financial tables; 32.3 on "hard" tables. | |
| - **Formatting (35.5):** bold 29%, italic 23%, superscript 59%, subscript 57% | |
| of rules pass. Underline, strikeout, highlight, LaTeX and code blocks are not | |
| produced (0%). | |
| - **Visual grounding (30.0):** paragraph boxes and reading order are decent; | |
| figure regions and box granularity often differ from the annotation. | |
| **What the benchmark changed in docparse.** The first full run scored 33.66; the final run scored 36.2. | |
| Its failures led to fixes that are now part of docparse itself: | |
| | Change | Effect | | |
| |---|---| | |
| | Bold/italic from PDF font names, `<sup>`/`<sub>` from glyph size and baseline | Semantic formatting 24.0 → 35.5 | | |
| | Running header/footer and printed page-number detection | Header rules 0 → 9.9%, footer rules 0 → 12.2%; content faithfulness 71.8 → 72.5 | | |
| | Rotated margin text (e.g. arXiv stamps) kept out of the reading flow | It no longer turns body text into a fake table | | |
| | Vector charts and diagrams detected as figures, their labels attached | Visual grounding 28.9 → 30.0 | | |
| | Rows stacked in one cell of rule-less tables split into records | Correct rows in insurance rate tables | | |
| | A malformed PDF link annotation no longer fails the whole page | 9 failing pages → 0 | | |
| Reproduce (parse-bench needs Python 3.12+): | |
| ```bash | |
| pip install "parse-bench[fast]" | |
| python scripts/parsebench_docparse.py download # ~600 MB | |
| python scripts/parsebench_docparse.py run docparse --max_concurrent 3 | |
| python scripts/parsebench_summary.py output/docparse leaderboard.csv | |
| ``` | |
| `docparse/parsebench.py` registers the pipeline through ParseBench's public | |
| extension API. Each page is sent as Markdown with HTML tables, page | |
| header/footer fields, and layout boxes in ParseBench's canonical classes. | |
| Aggregate results are in `benchmarks/parsebench/report.json`. | |
| ## ExtractBench (LlamaIndex): small local check | |
| [ExtractBench](https://github.com/run-llama/ExtractBench) scores **schema-guided | |
| extraction**: a document plus a JSON Schema in, schema-valid JSON plus evidence | |
| boxes out. A parser cannot do this alone; an LLM fills the schema. So docparse | |
| was tested as the **parse stage** of a two-stage pipeline (as ExtractBench's own | |
| parse → LLM baselines are), against plain pypdf text, with everything else fixed: | |
| the same model (Qwen2.5-3B-Instruct, 4-bit, llama.cpp on 4 CPU cores), the same | |
| prompt and 12,288-token context budget, and the official scorer. | |
| | Parse stage (6-document test set) | Value F1 | Precision | Recall | | |
| |---|---:|---:|---:| | |
| | **docparse** | **35.8** | 63.8 | 32.5 | | |
| | pypdf | 32.9 | 66.3 | 29.8 | | |
| docparse helped most on a scanned document (Value F1 19.6 vs 8.0, from OCR) and | |
| an earnings deck with tables (35.0 vs 25.0). It lost on the 66-page document | |
| (1.4 vs 5.2), where both arms could send only the first few pages. | |
| **Read this carefully.** This is 6 documents, so a 2.9-point gap is not | |
| statistically meaningful. Absolute scores are far below the leaderboard (top | |
| systems score ~96), because a 3B model with a 12k context must cut long documents | |
| (up to 55 of 66 pages dropped) and sometimes even shorten the schema. Grounding is | |
| 0 for both arms because this pipeline does not return evidence boxes yet. A real | |
| ExtractBench number needs a strong model over all 370 documents (ExtractBench | |
| estimates $10 to $1,677 per full run, depending on the model). Per-document | |
| details are in `benchmarks/extractbench/report.json`. | |
| Reproduce: start an OpenAI-compatible server (`python -m llama_cpp.server --model | |
| qwen2.5-3b-instruct-q4_k_m.gguf --n_ctx 12288 --port 8088`), then | |
| `DOCPARSE_LLM_URL=http://127.0.0.1:8088 python scripts/extractbench_docparse.py run | |
| docparse_extract_local --test` (and `pypdf_extract_local`). | |
| **Structure parsers** (PDF text layer, DOCX, XLSX, CSV) are tested for exact | |
| output in `tests/test_docparse.py`. | |
| ## Use it | |
| ```bash | |
| pip install -e '.[docs]' # torch, numpy, pillow, pdfplumber, pypdfium2, python-docx, openpyxl | |
| docparse extract report.pdf # Markdown to stdout | |
| docparse extract scan.png --format json -o scan.json | |
| docparse extract invoice.pdf --ocr always # OCR every page, even with a text layer | |
| docparse extract deck.docx --ocr-images # also read text inside embedded pictures | |
| docparse extract *.xlsx --format md -o out/ # many files -> a directory | |
| docparse ui # browser UI on http://127.0.0.1:7862 | |
| ``` | |
| ```python | |
| from docparse import extract | |
| from docparse.extract import to_markdown | |
| doc = extract("statement.pdf") # dict: source, format, metadata, pages, blocks, sheets, warnings | |
| for b in doc["blocks"]: | |
| print(b["type"], b.get("page"), b.get("text") or b.get("rows")) | |
| print(to_markdown(doc)) | |
| ``` | |
| Each block has a `type` (`heading`, `paragraph`, `list_item`, `table`, | |
| `figure`, `header`, `footer`), a `source` (`text-layer`, `structure` or | |
| `ocr`), and where meaningful a `page`, `bbox`, `level`, `sheet`, `range` and | |
| an OCR `confidence`. Excel sheets also list formulas, merged ranges, charts and | |
| images under `sheets`. | |
| ## Online demo | |
| The [Space](https://huggingface.co/spaces/Kedhareswer/docparse) runs the same | |
| `docparse ui` server and weights. Files up to 40 MB, one at a time. LibreOffice | |
| is installed there, so `.doc`, `.rtf`, `.odt`, `.xls`, `.ods`, `.ppt`, `.pptx` | |
| and `.odp` work too. It runs on Hugging Face's free ZeroGPU hardware with all | |
| inference on CPU (the app registers an unused `@spaces.GPU` function because | |
| ZeroGPU will not start without one). Measured there: Word, Excel and CSV files | |
| about 1 s, an 11-page text-layer PDF 3.5 s, a 14-page slide PDF 13 s, a page | |
| image read by OCR 2.5 s. Uploaded files are written to a temporary folder for | |
| the extraction and deleted right after. The Space build is | |
| `scripts/make_hf_docparse.sh`. | |
| ## Train or fine-tune your own | |
| ```bash | |
| python scripts/build_doc_corpus.py # rebuild the text corpora | |
| docparse train-ocr --run runs/ocr --rounds 4 # ~80 min on 4 CPU cores | |
| docparse train-ocr --run runs/ocr --rounds 6 --resume # continue / extend training | |
| docparse bench --model runs/ocr/ocr.pt --output runs/ocr-bench | |
| ``` | |
| To specialise it (your fonts, your vocabulary, e.g. invoices in one | |
| typeface), add font files to `TRAIN_FONTS` and text to `docparse/data/corpus_train.txt` | |
| in `docparse/ocr/render.py`, then train or resume. A checkpoint is written | |
| after every round. | |
| ## Limits | |
| - **Printed text only.** No handwriting. Latin script only (ASCII plus a few | |
| symbols); accents are dropped and other scripts are not read. | |
| - Tables: ruled grids and borderless tables with aligned columns are found. | |
| Cells that span several rows or columns are kept in the first cell. Very | |
| irregular tables may come out as text. | |
| - Reading order is top-to-bottom, columns left to right. Magazine layouts, | |
| slides with scattered boxes, sidebars and text running around pictures can | |
| be read out of order. | |
| - Pictures are reported as `figure` blocks with a position. Charts are not | |
| converted to data. | |
| - Photos of documents taken at an angle (perspective), curved pages and | |
| very low resolution (text under about 10 px high) are not handled well. | |
| - Born-digital PDFs are only as good as their text layer: some PDFs embed | |
| fonts without a Unicode mapping. Use `--ocr always` for those. | |
| - Besides ParseBench, the benchmarks are synthetic pages and three real PDFs from | |
| this repository. No direct comparison with Tesseract was run here. |