"""빌드/온라인 머신 전용: MoveNet SinglePose Lightning ONNX 다운로드(폐쇄망 번들). Apache-2.0. Integrity is enforced (spec: SHA256/GPG verify of every vendored model). The download is staged to a temp file, validated, and only then moved into place — a truncated / HTML-error-page / tampered download must never be saved as the model (onnxruntime would then fail to load it, and the operator would see a cryptic runtime error instead of a clear fetch failure). STATUS: the historical PINTO_model_zoo raw URL below is DEAD (that repo hosts its models on external storage, not as repo blobs — the raw path now returns a GitHub HTML 404). So there is no known-good sha256 to pin here yet. Until a working source is set: * this tool FAILS LOUD if the download is not a real ONNX (rejects the HTML it currently gets), instead of silently saving garbage; * set MOVENET_ONNX_URL to a valid source and pin PINNED_SHA256/PINNED_SIZE from its bytes. For the Hailo delivery path this model is optional (POSE_BACKEND=hailo), so a missing MoveNet is non-blocking; the CPU fallback needs this resolved. """ import hashlib import os import sys import tempfile import urllib.request URL = os.environ.get( "MOVENET_ONNX_URL", "https://github.com/PINTO0309/PINTO_model_zoo/raw/main/115_MoveNet/resources/" "movenet_singlepose_lightning_4.onnx", ) DEST = os.path.join(os.path.dirname(__file__), "..", "..", "models", "movenet_lightning.onnx") # Pin from the bytes of a known-good source once MOVENET_ONNX_URL points at one. Empty = unpinned # (still validated as a real ONNX below, but not byte-identical-verified). PINNED_SHA256 = os.environ.get("MOVENET_ONNX_SHA256", "") PINNED_SIZE = int(os.environ.get("MOVENET_ONNX_SIZE", "0") or "0") def _looks_like_onnx(head: bytes) -> bool: # ONNX is a protobuf ModelProto; it is NOT text. Reject an HTML error page / LFS pointer # outright (the current dead URL returns exactly that). stripped = head.lstrip() if stripped[:1] in (b"<", b"{") or stripped[:5].lower() == b" {dest}") tmp = None try: fd, tmp = tempfile.mkstemp(dir=os.path.dirname(dest), prefix=".movenet.", suffix=".part") os.close(fd) urllib.request.urlretrieve(URL, tmp) with open(tmp, "rb") as f: head = f.read(64) if not _looks_like_onnx(head): raise SystemExit( f"ERROR: {URL}\n did not return an ONNX model (looks like HTML / an error page /" f" an LFS pointer).\n Set MOVENET_ONNX_URL to a valid source and pin" f" MOVENET_ONNX_SHA256. (Hailo path does not need this model.)" ) size = os.path.getsize(tmp) digest = hashlib.sha256(open(tmp, "rb").read()).hexdigest() if PINNED_SHA256 and (digest != PINNED_SHA256 or (PINNED_SIZE and size != PINNED_SIZE)): raise SystemExit( f"ERROR: MoveNet ONNX does not match the pinned digest.\n" f" expected: {PINNED_SIZE} bytes, sha256 {PINNED_SHA256}\n" f" got : {size} bytes, sha256 {digest}\n Refusing to install it." ) if not PINNED_SHA256: print(f" WARNING: integrity not pinned (MOVENET_ONNX_SHA256 unset). Got {size} bytes," f" sha256 {digest}. Pin this once the source is trusted.", file=sys.stderr) os.replace(tmp, dest) tmp = None print(f"done: {dest} ({size} bytes)") finally: if tmp and os.path.exists(tmp): os.remove(tmp) if __name__ == "__main__": main()