Download idp_contract.py from spark-ux/indic-doc-parser: direct link, hf CLI and curl.
- Browser
- Download file 4.47 kB
-
https://huggingface.co/spark-ux/indic-doc-parser/resolve/main/idp_contract.py
- Command line
-
hf download hf://spark-ux/indic-doc-parser/idp_contract.py
-
curl -L -o idp_contract.py https://huggingface.co/spark-ux/indic-doc-parser/resolve/main/idp_contract.py
4.47 kB
| # GENERATED by hub/build_hub_package.py from src/bodhan_genai/ocr/templates/contract.py -- do not edit. | |
| # Vendored so this repo is self-contained: `pip install transformers torch pillow` is the | |
| # whole install. See indic_doc_parser.py for usage. | |
| """IndicDocParser's contract: prompts, block taxonomy, output schema. | |
| Two vocabularies meet here and mixing them fails silently. **Labels** are what IndicDocLayout | |
| emits (``ocr.layout.labels.CLASSES``); MARGINALIA and HEAD_FOOT match them case-SENSITIVELY. | |
| **Types** are the coarse categories a label maps to, and select the prompt. OCR_SKIP_LABELS is | |
| the exception: matched case-insensitively. | |
| stdlib-only -- importable with no GPU stack and no PIL. | |
| """ | |
| from __future__ import annotations | |
| from enum import StrEnum | |
| class TableFormat(StrEnum): | |
| """HTML is the default: colspan/rowspan and in-cell breaks have no GFM spelling, so a | |
| merged-cell table rendered as markdown silently loses its structure.""" | |
| HTML = "html" | |
| MARKDOWN = "markdown" | |
| TEXT_PROMPT = ( | |
| "Transcribe the text in this image. Write any mathematical expressions in LaTeX, " | |
| "using $...$ for inline math and $$...$$ for display equations." | |
| ) | |
| EQUATION_PROMPT = "Output only the LaTeX for this equation image." | |
| TABLE_PROMPTS = { | |
| TableFormat.HTML: ( | |
| "Convert this table image to HTML. Preserve the structure exactly, using colspan and " | |
| "rowspan for merged cells and <br/> for line breaks within a cell. " | |
| "Output only the HTML table." | |
| ), | |
| TableFormat.MARKDOWN: ( | |
| "Convert this table image to a GitHub-flavored markdown table. Output only the table." | |
| ), | |
| } | |
| def prompt_for(block_type: str, table_format: TableFormat = TableFormat.HTML) -> str: | |
| if block_type == "Table": | |
| return TABLE_PROMPTS[TableFormat(table_format)] | |
| if block_type == "Equation": | |
| return EQUATION_PROMPT | |
| return TEXT_PROMPT | |
| # Labels absent from this map fall through to "Text" by design -- Question, Paragraph, Answer, | |
| # List, MCQ, Code, Reference and the rest all carry prose. | |
| LABEL_TO_TYPE = { | |
| "table": "Table", | |
| "table-caption": "Caption", | |
| "equation": "Equation", | |
| "expression": "Equation", | |
| "diagram": "Figure", | |
| "chart": "Figure", | |
| "image": "Picture", | |
| "image-caption": "Caption", | |
| "title": "Title", | |
| "chapter-title": "Title", | |
| "section-title": "SectionHeader", | |
| "sub-section-title": "SectionHeader", | |
| "sub-sub-section-title": "SectionHeader", | |
| "header": "PageHeader", | |
| "footer": "PageFooter", | |
| "page-number": "PageNumber", | |
| "folio": "PageNumber", | |
| "footnote": "Footnote", | |
| } | |
| def map_label(label) -> str: | |
| """Label -> pipeline type. Unknown labels are ``Text``.""" | |
| return LABEL_TO_TYPE.get(str(label).strip().lower(), "Text") | |
| # Exactly the types map_label can produce, less DROP_TYPES. test_contract asserts this, so a | |
| # dead or undocumented type cannot creep in. | |
| KEPT_BLOCK_TYPES = ( | |
| "Text", | |
| "Title", | |
| "SectionHeader", | |
| "Table", | |
| "Equation", | |
| "Caption", | |
| "Footnote", | |
| "PageHeader", | |
| "PageFooter", | |
| "PageNumber", | |
| ) | |
| # Never cropped, never reconstructed: pictorial regions have no text and invite hallucination. | |
| # Deliberately narrow -- page numbers and margin text DO reach the recognizer. | |
| DROP_TYPES = frozenset({"Figure", "Picture"}) | |
| # Cleaned as their own group so a page-spanning paragraph cannot swallow a page number. | |
| MARGINALIA = frozenset({"Header", "Footer", "Page-number", "Folio"}) | |
| HEAD_FOOT = ("Header", "Footer") | |
| # Never sent to the recognizer, but NOT deleted: these keep their place in the output with | |
| # text "", so a consumer can still see what was detected and where. | |
| OCR_SKIP_LABELS = frozenset({"header", "footer", "diagram", "image", "chart", "advertisement"}) | |
| def is_transcribed(label) -> bool: | |
| return str(label).strip().lower() not in OCR_SKIP_LABELS | |
| OUTPUT_BLOCK_SCHEMA = { | |
| "order": "int -- reading-order rank (0 = first)", | |
| "label": "str -- raw IndicDocLayout label", | |
| "type": "str -- pipeline type (see KEPT_BLOCK_TYPES)", | |
| "bbox_xyxy": "[float x4] -- pixel [x0, y0, x1, y1], clamped to the page", | |
| "conf": "float -- detection confidence", | |
| "text": "str -- transcription; '' when not sent to the recognizer", | |
| } | |
| OUTPUT_PAGE_SCHEMA = { | |
| "image": "str -- source image filename", | |
| "width": "int -- page width in pixels", | |
| "height": "int -- page height in pixels", | |
| "blocks": "[BLOCK] -- in reading order", | |
| } | |