Files
hand-motion-pipeline/scripts/download_foundationpose_weights.py
liyang ae28d55f81 Update to 2026-09-17 pipeline snapshot; add weights, L20 assets and recording via Git LFS
Source: RGB-D -> Dyn-HaMR -> L20 retargeting -> FoundationPose -> reference repair -> SPIDER,
documented in docs/PIPELINE_LATEST.md and docs/SETUP_AND_WEIGHTS.md. Adds FoundationPose and
nvdiffrast upstream snapshots, requirements/pipeline_venv.txt and the FoundationPose weight
manifest/downloader.

Assets (Git LFS): weights/ (WiLoR detector, HandFlow denoiser, UniDepth-L), FoundationPose
checkpoints, HaMeR checkpoint, Dyn-HaMR HMP model and BMC constraints, L20 URDF/meshes, the
20260915_171525 D405 recording and the two box CADs. MANO models are not redistributed
(third_party/hamer/_DATA/data/mano/README.txt). Environments, caches and run outputs excluded.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 11:43:37 +08:00

34 lines
2.2 KiB
Python

"""Download and verify the FoundationPose checkpoints listed in configs/foundationpose_weights_manifest.json.
Files go to third_party/FoundationPose/weights/<run>/{config.yml, model_best.pth}; resumable (Range requests), SHA-256 checked.
The manifest URLs point at a community Hugging Face mirror (official Google Drive quota was exceeded when the snapshot was made);
replace `url` entries with the official links if you have them. Usage: .venv/bin/python scripts/download_foundationpose_weights.py
"""
import hashlib, json, time
from pathlib import Path
import requests
ROOT = Path(__file__).resolve().parents[1]
manifest = json.loads((ROOT / 'configs/foundationpose_weights_manifest.json').read_text())
dest = ROOT / 'third_party/FoundationPose/weights'
for item in manifest['files']:
path = dest / item['path']; path.parent.mkdir(parents=True, exist_ok=True); tmp = path.with_suffix(path.suffix + '.part')
if path.exists() and path.stat().st_size == item['size'] and hashlib.sha256(path.read_bytes()).hexdigest() == item['sha256']:
print('ok ', item['path']); continue
for attempt in range(5):
try:
offset = tmp.stat().st_size if tmp.exists() else 0
print(f'download {item["path"]} from byte {offset}', flush=True)
with requests.get(item['url'], headers={'Range': f'bytes={offset}-{item["size"] - 1}'}, stream=True, timeout=(20, 60)) as r:
r.raise_for_status()
if offset and r.status_code != 206: offset = 0
with tmp.open('ab' if offset else 'wb') as f:
for chunk in r.iter_content(1024 * 1024): f.write(chunk)
if tmp.stat().st_size == item['size']: tmp.replace(path); break
except requests.RequestException as e:
print('retry:', type(e).__name__, flush=True); time.sleep(3)
assert path.exists() and path.stat().st_size == item['size'], f'incomplete: {path}'
digest = hashlib.sha256(path.read_bytes()).hexdigest(); assert digest == item['sha256'], f'sha256 mismatch: {path}'
print('verified', item['path'], flush=True)
(dest / 'download_manifest.json').write_text(json.dumps(manifest, indent=2)); print('all FoundationPose weights present in', dest)