| |
| |
| |
|
|
| """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 |
|
|
|
|
| |
| |
| 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") |
|
|
|
|
| |
| |
| KEPT_BLOCK_TYPES = ( |
| "Text", |
| "Title", |
| "SectionHeader", |
| "Table", |
| "Equation", |
| "Caption", |
| "Footnote", |
| "PageHeader", |
| "PageFooter", |
| "PageNumber", |
| ) |
|
|
| |
| |
| DROP_TYPES = frozenset({"Figure", "Picture"}) |
|
|
| |
| MARGINALIA = frozenset({"Header", "Footer", "Page-number", "Folio"}) |
| HEAD_FOOT = ("Header", "Footer") |
|
|
| |
| |
| 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", |
| } |
|
|