Download usage.py from decosaai/decosa-readconf: direct link, hf CLI and curl.
- Browser
- Download file 2.52 kB
-
https://huggingface.co/decosaai/decosa-readconf/resolve/main/usage.py
- Command line
-
hf download hf://decosaai/decosa-readconf/usage.py
-
curl -L -o usage.py https://huggingface.co/decosaai/decosa-readconf/resolve/main/usage.py
2.52 kB
| # SPDX-License-Identifier: Apache-2.0 | |
| # Copyright 2026 Decosa | |
| """Score a page read by a document reader: P(each table cell or text region is misread) and the likely error. | |
| pip install torch safetensors numpy pillow (transformers only for a vision variant; this one has none) | |
| python usage.py page.png page.json # prints the 20 units most likely misread | |
| The page dict is one page of a decosa.docreader.v1 document: {"elements": [{id, kind, bbox: [x, y, w, h], text?, | |
| table?: {rows, cols, cells: [{r, c, rs, cs, text, tok?}]}, source, conf_bp?, min_bp?, ...}]}, with bboxes in the pixels of | |
| the page image you pass. Cell token confidences (cell["tok"]) are optional but help a lot (see the eval). | |
| """ | |
| import json | |
| import sys | |
| import numpy as np | |
| import torch | |
| from PIL import Image | |
| from safetensors.torch import load_file | |
| import features as F | |
| from model import TYPES, ReadConf, encode_text | |
| D = "." | |
| cfg = json.load(open(f"{D}/readconf_config.json")) | |
| vcfg = None | |
| if cfg.get("vision"): | |
| from transformers import Siglip2VisionConfig | |
| vcfg = Siglip2VisionConfig.from_dict(cfg["vision"]["config"]) | |
| m = ReadConf(cfg["n_feat"], vision_config=vcfg, vision_layers=(cfg.get("vision") or {}).get("layers", 0)) | |
| m.load_state_dict(load_file(f"{D}/model.safetensors")) | |
| m.eval() | |
| def score(image: Image.Image, page: dict) -> list[dict]: | |
| us = F.page_units(image.convert("RGB"), page) | |
| if not us: | |
| return [] | |
| x = torch.tensor(np.array([u["x"] for u in us], np.float32)) | |
| ch = encode_text([u["text"] for u in us]) | |
| img = None | |
| if m.vision_layers: | |
| w = cfg["crop"][1] | |
| a = np.full((len(us), F.CROP_H, w), 255, np.uint8) | |
| for i, u in enumerate(us): | |
| c = F.crop(image.convert("RGB"), u, max_w=w) | |
| a[i, :, : c.width] = np.asarray(c, np.uint8) | |
| img = torch.tensor(a) | |
| lw, lt = m(x, ch, img) | |
| p, tp = m.p_wrong(lw), lt.softmax(-1) | |
| out = [] | |
| for i, u in enumerate(us): | |
| # No thresholds ship with this model: rank by p_wrong and review from the top (see the model card). | |
| out.append({"uid": u["uid"], "text": u["text"], "p_wrong": round(float(p[i]), 4), "type": TYPES[int(tp[i].argmax())]}) | |
| return out | |
| if __name__ == "__main__": | |
| im = Image.open(sys.argv[1]) | |
| page = json.load(open(sys.argv[2])) | |
| if "pages" in page: # a whole decosa.docreader.v1 document: score its first page | |
| page = page["pages"][0] | |
| for r in sorted(score(im, page), key=lambda r: -r["p_wrong"])[:20]: | |
| print(r) | |