OpenThai2.0-OCR-Markdown / docs /postprocessing.md
kobkrit's picture
OpenThai2.0 OCR release v1.0.0 (12 Nov 2026)
874752d
|
Raw History Blame Contribute Delete
5.19 kB

Parse documents and export tables

These helpers use predictions and page images; they do not load benchmark answers.

Downloaded helpers

With the native vLLM server running:

python -m pip install beautifulsoup4 Pillow numpy httpx

No serving plugin is required. Add the downloaded repository to Python's import path:

import sys
sys.path.insert(0, "./parse35")
from nemotron_parse.inference import DocumentParser

def main():
    with DocumentParser(
        base_url="http://localhost:8000/v1", model="nemotron-parse",
    ) as parser:
        documents = parser.parse_many(
            ["one.png", "two.png"], mode="triple",
            concurrency=128, triple_regions="union",
        )
    for document in documents:
        print(document["markdown"])
        print(document["blocks"])

if __name__ == "__main__":
    main()

single parses each full page. double also refines predicted Table/Chart crops. triple adds independent detection and refines the detector/parser region union. parse_many overlaps parsing and detection, submits crops as they become ready, and shares one concurrency window across stages. Put multiprocessing entry points under the main guard. The one-page parse, parse_double and parse_triple convenience methods execute their dependent stages serially.

The default alternatives merge retains differing overlapping table/chart hypotheses. Use DocumentParser(merge_policy="guarded") for conservative replacement and a single selected hypothesis; this can discard correct alternatives. Stage responses remain available in document["stages"].

Convert an existing prediction

from nemotron_parse import postprocess_document, PostprocessOptions
from postprocessing import postprocess_text

options = PostprocessOptions(
    infer_headers=True, render_charts=True,
    split_chart_value_percent=True, relocate_chart_captions=True,
)
document = postprocess_document(raw_output, image_size=image.size, options=options)
table = next(b for b in document["blocks"] if b["class"] == "Table")
print(postprocess_text(table["text"], cls="Table", table_format="csv"))
print(postprocess_text(table["text"], cls="Table", table_format="json_hierarchical"))

Blocks retain class, normalized bbox, literal text and rendered content. image_size=(width,height) adds clipped pixel boxes. Document Markdown uses HTML tables to preserve merged cells. HTML and json_hierarchical preserve cell spans; markdown, csv and record json are flat exports that expand spans. latex retains the predicted table representation. Headerless tables retain their first data row; duplicate column names are disambiguated.

Set any presentation option above to False to disable it. Caption association moves existing text; value/percentage splitting separates existing predictions and does not calculate values. Raw block text remains available. Malformed tables are retained and reported in warnings rather than forced into a lossy row splitter. For strict output schemas, select fields after a full-structure parse; prompt switches can leave residual structural tokens.

Draw predicted boxes

Continue from the prediction conversion above to save an image overlay:

from PIL import ImageDraw

overlay = image.copy()
draw = ImageDraw.Draw(overlay)
for block in document["blocks"]:
    if block.get("bbox_pixels"):
        draw.rectangle(block["bbox_pixels"], outline="red", width=2)
overlay.save("prediction.png")

Use the same image dimensions supplied to postprocess_document. This recipe draws the predicted boxes without changing their geometry or the source image.

Completion and prepared batches

Strict generation handling is the default. Token-limited, empty, failed or filtered output raises GenerationError. If partial results are useful, pass allow_partial=True: affected documents have complete=False and generation_warnings; truncated crops are excluded from refinement. Transport errors and empty responses still raise. Do not count partial pages as completed.

For repeated processing of the same page images:

prepared = parser.prepare(page_images, workers=8)
result = parser.parse_prepared(
    prepared, mode="triple", concurrency=128, postprocess_workers=8,
    triple_regions="union", schedule="pipelined", allow_partial=True,
)
print(result.metrics["inference_pages_per_second"])
print(result.metrics["complete_inference_pages_per_second"])

Run this inside the guarded main function while parser is open. Preparation is reported separately. The inference clock includes model/HTTP calls, queueing and dependent crop preparation; final rendering and serialization are reported separately. Use pipeline rates when end-to-end merge/render cost matters. Stage spans overlap and must not be added together.

For crop margins, generation controls, a loaded Transformers backend, CLI use, and historical validation, see the extended reference. The CLI can run from downloaded source as python -m nemotron_parse.inference; installing the serving plugin is unnecessary for that invocation.