hamer-p150 / code /scripts /eval.py
changh95's picture
Add Tenstorrent Blackhole tt-nn port
25e8396 verified
Raw History Blame Contribute Delete
4.59 kB
# SPDX-FileCopyrightText: © 2026 Tenstorrent Inc.
# SPDX-License-Identifier: Apache-2.0
"""Real-checkpoint, real-image PCC + numeric-drift evaluation.
Run from the repo root:
python scripts/eval.py \
--ckpt /path/to/_DATA/hamer_ckpts/checkpoints/hamer.ckpt \
--mean /path/to/_DATA/data/mano_mean_params.npz \
--images /path/to/hamer/example_data \
--use-tt
"""
from __future__ import annotations
import argparse
import os
import sys
from pathlib import Path
from typing import List
import torch
_REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(_REPO_ROOT))
import json # noqa: E402
from hamer import Hamer, load_hamer_checkpoint # noqa: E402
from hamer.preprocess import preprocess # noqa: E402
def pcc(a: torch.Tensor, b: torch.Tensor) -> float:
a = a.detach().reshape(-1).float()
b = b.detach().reshape(-1).float()
a = a - a.mean()
b = b - b.mean()
denom = (a.norm() * b.norm()).clamp_min(1e-12)
return float((a @ b) / denom)
def parse_args() -> argparse.Namespace:
p = argparse.ArgumentParser(description=__doc__)
p.add_argument("--ckpt", type=Path, required=True, help="hamer.ckpt from upstream demo data")
p.add_argument("--mean", type=Path, required=True, help="mano_mean_params.npz")
p.add_argument("--images", type=Path, required=True, help="dir of input images")
p.add_argument("--bboxes", type=Path, required=True, help="JSON {image_name: [x1,y1,x2,y2]}")
p.add_argument("--use-tt", action="store_true", help="Also run the tt-nn port")
p.add_argument("--device-id", type=int, default=int(os.environ.get("HAMER_DEVICE", "0")))
return p.parse_args()
def main() -> int:
args = parse_args()
bboxes_map = json.loads(args.bboxes.read_text())
image_paths: List[Path] = sorted(
p for p in args.images.iterdir() if p.suffix.lower() in (".jpg", ".jpeg", ".png")
)
if not image_paths:
print(f"ERROR: no images in {args.images}")
return 2
print("=== Loading reference HaMeR checkpoint ===")
torch.manual_seed(0)
ref = Hamer()
ref.eval()
report = load_hamer_checkpoint(ref, args.ckpt, args.mean)
print(f" {report['loaded']} tensors mapped, {report['skipped_upstream']} upstream keys ignored")
if report["missing_in_ref"] or report["unexpected_in_ref"]:
print(f" WARN: missing={report['missing_in_ref']} unexpected={report['unexpected_in_ref']}")
tt_model = None
if args.use_tt:
os.environ["HAMER_USE_TT"] = "1"
os.environ["HAMER_FORCE_TT"] = "1"
from hamer import build_from_reference
print(f"=== Opening tt-nn device {args.device_id} ===")
tt_model = build_from_reference(ref, device_id=args.device_id)
print()
print(f"{'image':<24} {'ref|cam|':>10} {'pcc(tt,ref)':>12} {'maxabs_pose':>12} {'maxabs_betas':>13} {'maxabs_cam':>11}")
print("-" * 90)
pccs = []
for img_path in image_paths:
entries = bboxes_map.get(img_path.name)
if entries is None:
continue
# Take the first hand for each image (full multi-hand sweep is in mpjpe.py).
if isinstance(entries, dict):
bbox, is_right = entries["bbox"], int(entries.get("is_right", 1))
elif isinstance(entries, list) and entries and isinstance(entries[0], dict):
bbox, is_right = entries[0]["bbox"], int(entries[0].get("is_right", 1))
else:
bbox, is_right = entries, 1
x, _ = preprocess(img_path, bbox_xyxy=tuple(bbox), is_right=bool(is_right))
with torch.no_grad():
ref_out = ref(x).detach()
cam_norm = float(ref_out[:, -3:].norm())
if tt_model is not None:
with torch.no_grad():
tt_out = tt_model(x).detach()
p = pcc(tt_out, ref_out) * 100.0
diff = (tt_out - ref_out).abs()
mx_pose = float(diff[:, :144].max())
mx_betas = float(diff[:, 144:154].max())
mx_cam = float(diff[:, 154:157].max())
pccs.append(p)
print(f"{img_path.name:<24} {cam_norm:>10.4f} {p:>11.4f}% {mx_pose:>12.4e} {mx_betas:>13.4e} {mx_cam:>11.4e}")
else:
print(f"{img_path.name:<24} {cam_norm:>10.4f}")
if pccs:
print()
print(f"mean PCC across {len(pccs)} images: {sum(pccs)/len(pccs):.4f}%")
print(f"min PCC: {min(pccs):.4f}%")
if tt_model is not None and hasattr(tt_model, "close"):
tt_model.close()
return 0
if __name__ == "__main__":
raise SystemExit(main())