"""빌드/온라인 머신 전용: 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). SOURCE: the historical PINTO_model_zoo raw URL is DEAD (that repo hosts its models on external storage, not as repo blobs — the raw path returns a GitHub HTML 404). The pinned source below is a tf2onnx export of Google's MoveNet SinglePose Lightning v4, checked to match what edge/adapters/movenet_pose.py feeds and reads: input [1,192,192,3] int32 NHWC, output [1,1,17,3] float (y, x, score). A conversion with a different layout would load fine and then produce silently wrong keypoints, so the digest below is what makes this safe to fetch — not the hostname. For the Hailo delivery path this model is optional (POSE_BACKEND=hailo). """ import hashlib import os import sys import tempfile import urllib.request URL = os.environ.get( "MOVENET_ONNX_URL", "https://huggingface.co/Xenova/movenet-singlepose-lightning/resolve/main/onnx/model.onnx", ) DEST = os.path.join(os.path.dirname(__file__), "..", "..", "models", "movenet_lightning.onnx") # Pinned from the bytes of the source above. An override must bring its own digest — pointing # MOVENET_ONNX_URL elsewhere while keeping this pin just makes the download fail, which is the # intended direction of failure. PINNED_SHA256 = os.environ.get( "MOVENET_ONNX_SHA256", "1ad4f8d6c2f776a9967db3993c9ca740bc350104f9d37c151dc183fc29a464ad") PINNED_SIZE = int(os.environ.get("MOVENET_ONNX_SIZE", "9413268") 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()