- P3-1 [H] _head_boxes sizes the anonymization region from the person bbox (scales with distance) + bbox-top fallback when the nose keypoint is low-confidence; a fixed ~40px patch had left close faces identifiable. test_faceblur.py. - P3-3 [H] fetch_movenet.py rewritten to stage-validate (reject HTML/LFS-pointer, optional pinned sha256+size, fail loud) per the SHA256 spec requirement. Discovery: the old PINTO raw URL is DEAD (GitHub 404 → it was silently saving an HTML page as the .onnx). Non-blocking for the Hailo delivery path; the CPU MoveNet fallback needs a valid MOVENET_ONNX_URL pinned. python + phpunit suites green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Xmn8KZWqiQUguEMRtn6rW
87 lines
3.8 KiB
Python
87 lines
3.8 KiB
Python
"""빌드/온라인 머신 전용: 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"<!doc":
|
|
return False
|
|
if b"version https://git-lfs" in head:
|
|
return False
|
|
return True
|
|
|
|
|
|
def main():
|
|
dest = os.path.abspath(DEST)
|
|
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
|
print(f"downloading MoveNet ONNX\n {URL}\n -> {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()
|