Download training/predict.py from ProCreations/nadir: direct link, hf CLI and curl.
- Browser
- Download file 5.36 kB
-
https://huggingface.co/ProCreations/nadir/resolve/main/training/predict.py
- Command line
-
hf download hf://ProCreations/nadir/training/predict.py
-
curl -L -o predict.py https://huggingface.co/ProCreations/nadir/resolve/main/training/predict.py
5.36 kB
| #!/usr/bin/env python3 | |
| """Run a trained NadirNet over held-out pages and write render jobs (Python port of nadir-infer.js decode).""" | |
| import argparse, gzip, json, math, pickle | |
| from pathlib import Path | |
| import numpy as np | |
| import torch | |
| from model import NadirNet | |
| from train import collate, prep | |
| from mkjobs import job_from | |
| TREAT = ["keep", "invert", "adapt", "remove"] | |
| RENDER_T = {"keep": "keep", "invert": "invert", "adapt": "invert", "remove": "clear"} # render.py has no adapt path | |
| from safety import oklab, lab_to_rgb, contrast, safe_treatment, enforce_contrast, is_media_keep, hue_guard, keep_invisible, rescue_surfaces, glass_panels, opaque_surfaces, dark_washes | |
| def decode(L, lab, alpha_logit, img_logits, rels): | |
| toks = L["tokens"][:256] | |
| out, keep = [], set() | |
| for i, t in enumerate(toks): | |
| ain = min(1 - 1e-3, max(1e-3, t["rgba"][3])) | |
| alpha = 1 / (1 + math.exp(-(alpha_logit[i] + math.log(ain / (1 - ain))))) | |
| a = 1.0 if (t["rgba"][3] >= 0.999 and alpha > 0.97) else alpha | |
| out.append({"rgb": lab_to_rgb(*lab[i]), "a": a, "L": float(lab[i][0])}) | |
| if is_media_keep(t): # shown as-is: later safety passes see its real colour | |
| keep.add(i) | |
| rgb = [int(x) for x in t["rgba"][:3]] | |
| out[i] = {"rgb": rgb, "a": float(t["rgba"][3]), "L": oklab(*rgb)[0]} | |
| else: | |
| hue_guard(t, out[i]) | |
| glass_panels(toks, out, keep) | |
| for i, t in enumerate(toks): | |
| if t["f"][24] > 0.5 and t["role"] == 0 and out[i]["L"] > 0.35: | |
| o = oklab(*out[i]["rgb"]); out[i]["rgb"] = lab_to_rgb(0.2, o[1] * 0.3, o[2] * 0.3); out[i]["L"] = 0.2 | |
| opaque_surfaces(toks, out, keep) | |
| dark_washes(toks, out, keep) | |
| keep_invisible(toks, out, rels, keep) | |
| enforce_contrast(toks, out, rels, keep) | |
| rescue_surfaces(toks, out, rels, keep) | |
| def hx(o): | |
| h = "#%02x%02x%02x" % tuple(o["rgb"]) | |
| return h + ("%02x" % round(o["a"] * 255) if o["a"] < 0.999 else "") | |
| colors = [None if i in keep else hx(o) for i, o in enumerate(out)] | |
| treat = {} | |
| for j, im in enumerate(L["images"][:32]): | |
| t = TREAT[int(np.argmax(img_logits[j]))] if j < len(img_logits) else "keep" | |
| treat[str(j)] = RENDER_T[t] | |
| return colors, sorted(keep), treat | |
| def element_treatments(model, s, lab, ctx, dev): | |
| """Element head over a page's first 32 images (render jobs only know those).""" | |
| m = min(32, len(s["IF"])) | |
| if not m: | |
| return [] | |
| f = torch.from_numpy(s["IF"][:m])[None].to(dev) | |
| th = torch.from_numpy(s["thumbs"][:m].transpose(0, 3, 1, 2) / 255.0).float()[None].to(dev) | |
| ul = torch.from_numpy(s["IF"][:m, 23:26] * np.array([1, 0.25, 0.25], np.float32))[None].to(dev) | |
| u = np.where((s["img_under"][:m] >= 0) & (s["img_under"][:m] < lab.shape[1]), s["img_under"][:m], s["root"]) | |
| ud = lab[:, torch.from_numpy(u).to(dev)] | |
| t, _ = model.elements(f, th, ul, ud, ctx) | |
| return t[0].float().cpu().numpy().tolist() | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--ckpt", required=True) | |
| ap.add_argument("--data", required=True) | |
| ap.add_argument("--crawl", required=True) | |
| ap.add_argument("--out", required=True) | |
| ap.add_argument("--n", type=int, default=150) | |
| ap.add_argument("--src", type=int, default=0) | |
| ap.add_argument("--tag", default="nadir") | |
| ap.add_argument("--also", default="haiku,dr", help="extra render tags to emit for the same pages") | |
| ap.add_argument("--haiku", default="") | |
| args = ap.parse_args() | |
| ck = torch.load(args.ckpt, map_location="cpu") | |
| model = NadirNet(**ck["cfg"]); model.load_state_dict(ck["model"]); model.eval() | |
| dev = "cuda" if torch.cuda.is_available() else "cpu" | |
| model.to(dev) | |
| data = pickle.load(open(args.data, "rb")) | |
| val = [s for s in data["val"] if s["src"] == args.src][: args.n] | |
| with open(args.out, "w") as f: | |
| for s in val: | |
| s = prep(s) | |
| L = json.load(gzip.open(Path(args.crawl) / "pages" / s["id"] / "light.json.gz")) | |
| b = collate([s], dev) | |
| with torch.no_grad(): | |
| lab, al, ctx = model(b["tok_f"], b["role"], b["lab"], b["img_f"], b["thumbs"], b["page"], torch.zeros_like(b["src"]), b["rel"], b["mask"]) | |
| il = element_treatments(model, s, lab.float(), ctx, dev) | |
| n = len(s["role"]) | |
| colors, keep, treat = decode(L, lab[0, :n].float().cpu().numpy().tolist(), al[0, :n].float().cpu().numpy().tolist(), | |
| il, s["rel"]) | |
| colors = colors + [None] * (len(L["tokens"]) - len(colors)) | |
| j = job_from(L, s["id"], args.tag, colors, treat) | |
| j["keep"] = sorted(set(j["keep"]) | set(keep)) | |
| f.write(json.dumps(j) + "\n") | |
| if "dr" in args.also: | |
| f.write(json.dumps(job_from(L, s["id"], "dr", [None] * len(L["tokens"]), {}, baseline=True)) + "\n") | |
| if "haiku" in args.also and args.haiku: | |
| hp = Path(args.haiku) / f"{s['id']}.json" | |
| if hp.exists(): | |
| h = json.loads(hp.read_text()) | |
| outs = [h["colors"].get(str(i)) for i in range(len(L["tokens"]))] | |
| f.write(json.dumps(job_from(L, s["id"], "haiku", outs, h["images"])) + "\n") | |
| print("wrote", len(val), "pages") | |
| if __name__ == "__main__": | |
| main() | |