Instructions to use hchen657Naza/PWM with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use hchen657Naza/PWM with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("hchen657Naza/PWM", dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
Download PWM_code/reward_model/scripts/build_stackcan_tracks.py from hchen657Naza/PWM: direct link, hf CLI and curl.
- Browser
- Download file 9.89 kB
-
https://huggingface.co/hchen657Naza/PWM/resolve/main/PWM_code/reward_model/scripts/build_stackcan_tracks.py
- Command line
-
hf download hf://hchen657Naza/PWM/PWM_code/reward_model/scripts/build_stackcan_tracks.py
-
curl -L -o build_stackcan_tracks.py https://huggingface.co/hchen657Naza/PWM/resolve/main/PWM_code/reward_model/scripts/build_stackcan_tracks.py
9.89 kB
| #!/usr/bin/env python3 | |
| """Track TWO objects on the Stack-Can teleop demos, saving combined point tracks | |
| WITH per-point object labels (0=can, 1=plate). Mirrors build_boxmark_tracks.py | |
| (the box+mark push-box pipeline). | |
| NOTE on prompts: the user asked for "Green cup" + "White plate on the table". | |
| A SAM3 probe showed the manipulated object is a red/orange CAN, not a cup | |
| ("Green cup"/"cup"/"mug" -> nothing), and that grounding is FLAKY across demos | |
| and across anchor frames. The robust recipe (from _probe_stackcan_robust.py over | |
| ep000..ep004): | |
| - CAN : "red can" (a0 on most demos; identical to "can"/"orange can") | |
| - PLATE: "white round plate" grounds on ALL anchors/demos, far better than | |
| "White plate on the table" (which is per-anchor flaky). | |
| We try a prompt-variant list x several anchor frames and take the first that | |
| grounds, then CoTracker tracks the FULL video from that frame (CoTracker3 is | |
| bidirectional, so tracks cover all T frames regardless of anchor). | |
| Source: robot_new/Stack-Can/tableHF_ep{NNN}.mp4 — the 100 success teleop demos, | |
| native 1280x720. Each frame is anisotropically SQUASHed to 384x384 before | |
| tracking, matching the WM / 3-PoinTr domain (the "tableHF 1280x720 squash" gotcha). | |
| Per episode -> <OUT>/<src>/tracks_labeled.npz with: | |
| tracks (T,N,2) float32, vis (T,N) bool, labels (N,) int [0=can,1=plate], | |
| n_can, n_plate, episode_index, src, H, W | |
| Also writes a combined overlay mp4 for the first --n_overlay episodes (can=green, | |
| plate=blue). Loads SAM3 + CoTracker ONCE and loops. | |
| """ | |
| import sys, os, json, glob, argparse | |
| from pathlib import Path | |
| import numpy as np | |
| import cv2 | |
| import imageio | |
| sys.path.insert(0, "/home/hongyi/PWM/external") | |
| import sam3.model_builder as _mb | |
| from sam3.model_builder import build_sam3_video_predictor | |
| from cotracker.predictor import CoTrackerPredictor | |
| from track_objects_in_video import load_video, sam3_propagate, run_cotracker, COTRACKER_CKPT | |
| DEMO_GLOB = "/home/hongyi/PWM/external/DiffSynth-Studio/data/robot_new/Stack-Can/tableHF_ep[0-9][0-9][0-9].mp4" | |
| OUT_ROOT_DEFAULT = "/data0/hongyi/reward_model_data/3pointr_data_stack_can_2d_canplate" | |
| # (tag/label-name, ordered SAM3 prompt variants, overlay BGR color). label_id = list index. | |
| PROMPTS = [ | |
| ("can", ["red can", "can", "orange can"], (0, 255, 0)), | |
| # Plate is hard: "white round plate" misses ~1/3 of demos. Probe (v2) on the | |
| # failed set found shape-specific "white circle"/"white disc" recover most, and | |
| # "white object on the table" recovers all 15/15 (generic but table-constrained). | |
| # Order: specific-shape first (least false-positive risk), generic catch-all last. | |
| ("plate", ["white round plate", "white circle", "white disc", "round plate", | |
| "White plate on the table", "white plate", "white object on the table"], (255, 0, 0)), | |
| ] | |
| ANCHORS = [0, 16, 8, 32] # grounding-clip start frames, tried in order (0 first = full coverage) | |
| GROUND_N = 16 # grounding-clip length (the probe used 16) | |
| SQUASH = 384 # square target the WM / 3-PoinTr operate in | |
| def ground_and_track(predictor, cot, full_frames, prompt_variants, stride, retries=2): | |
| """Ground SAM3 on a short clip (prompt-variant x anchor sweep), then CoTracker | |
| over the FULL video. Returns (tracks (T,N,2), vis (T,N), used_prompt, used_frame) | |
| or None if nothing grounds.""" | |
| n = len(full_frames) | |
| tmp = f"/tmp/stackcan_ground_{os.getpid()}.mp4" | |
| for anchor in ANCHORS: | |
| if anchor >= n: | |
| continue | |
| clip = full_frames[anchor:anchor + GROUND_N] | |
| if len(clip) < 4: | |
| continue | |
| imageio.mimwrite(tmp, clip, fps=15, macro_block_size=1) | |
| for prompt in prompt_variants: | |
| masks = None | |
| for _ in range(retries): | |
| m, _n = sam3_propagate(predictor, tmp, prompt) | |
| if m is not None: | |
| masks = m | |
| break | |
| if masks is None: | |
| continue | |
| masks_arr = np.stack([masks[i] for i in range(len(clip))], axis=0) | |
| nz = [i for i in range(len(clip)) if masks_arr[i].any()] | |
| if not nz: | |
| continue | |
| ci = nz[0] | |
| tr, vis = run_cotracker(cot, full_frames, masks_arr[ci], stride, | |
| init_frame_idx=anchor + ci) | |
| if tr is not None: | |
| return tr, vis, prompt, anchor + ci | |
| return None | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--out_root", default=OUT_ROOT_DEFAULT) | |
| ap.add_argument("--stride", type=int, default=3) # box+mark used 3 | |
| ap.add_argument("--limit", type=int, default=0) # >0 = first N demos | |
| ap.add_argument("--n_overlay", type=int, default=5) | |
| ap.add_argument("--demos", default="") # comma ep ids (e.g. ep002,ep007) = re-track only these | |
| args = ap.parse_args() | |
| os.makedirs(args.out_root, exist_ok=True) | |
| demos = sorted(glob.glob(DEMO_GLOB)) | |
| if args.demos: | |
| want = {e.strip() for e in args.demos.split(",") if e.strip()} | |
| demos = [d for d in demos if Path(d).stem.replace("tableHF_", "") in want] | |
| if args.limit > 0: | |
| demos = demos[:args.limit] | |
| print(f"[build] {len(demos)} Stack-Can demos -> {args.out_root} (stride={args.stride})", flush=True) | |
| bpe = str(Path(_mb.__file__).parent / "assets" / "bpe_simple_vocab_16e6.txt.gz") | |
| print("[build] loading SAM3 ...", flush=True) | |
| predictor = build_sam3_video_predictor(bpe_path=bpe, gpus_to_use=[0], | |
| apply_temporal_disambiguation=False) | |
| print("[build] loading CoTracker3 ...", flush=True) | |
| cot = CoTrackerPredictor(checkpoint=COTRACKER_CKPT).cuda() | |
| manifest = [] | |
| for k, vid in enumerate(demos): | |
| src = Path(vid).stem # tableHF_epNNN | |
| ep_idx = int(src.split("ep")[-1]) | |
| outdir = os.path.join(args.out_root, src) | |
| os.makedirs(outdir, exist_ok=True) | |
| # native frames -> squash to SQUASH x SQUASH (anisotropic, WM-domain match) | |
| frames = [cv2.resize(f, (SQUASH, SQUASH), interpolation=cv2.INTER_AREA) | |
| for f in load_video(vid, None)] | |
| H, W = frames[0].shape[:2] | |
| res = {tag: ground_and_track(predictor, cot, frames, variants, args.stride) | |
| for tag, variants, _ in PROMPTS} | |
| if all(v is None for v in res.values()): | |
| print(f"[warn] {src}: NO objects detected — skipping", flush=True) | |
| manifest.append({"src": src, "episode_index": ep_idx, "status": "no_objects"}) | |
| continue | |
| # Build labeled combined tracks. Order = PROMPTS order (can=0, plate=1). | |
| track_list, vis_list, label_list, counts, used = [], [], [], {}, {} | |
| for label_id, (tag, _, _) in enumerate(PROMPTS): | |
| r = res[tag] | |
| if r is None: | |
| counts[tag] = 0 | |
| print(f"[warn] {src}: {tag.upper()} not detected", flush=True) | |
| continue | |
| tr, vs, used_prompt, used_frame = r | |
| n = tr.shape[1] | |
| track_list.append(tr.astype(np.float32)) | |
| vis_list.append(vs) | |
| label_list.append(np.full(n, label_id, np.int64)) | |
| counts[tag] = n | |
| used[tag] = f"{used_prompt}@f{used_frame}" | |
| T = track_list[0].shape[0] | |
| tracks = np.concatenate(track_list, axis=1).astype(np.float32) # (T, N, 2) | |
| vis = np.concatenate(vis_list, axis=1) | |
| labels = np.concatenate(label_list) # (N,) 0=can 1=plate | |
| np.savez(os.path.join(outdir, "tracks_labeled.npz"), | |
| tracks=tracks, vis=vis, labels=labels, | |
| n_can=counts.get("can", 0), n_plate=counts.get("plate", 0), | |
| episode_index=ep_idx, src=src, H=H, W=W) | |
| # can<->plate centroid gap (sanity: should shrink as the can lands on the plate) | |
| d0 = dl = -1.0 | |
| if res["can"] is not None and res["plate"] is not None: | |
| can, plate = res["can"][0], res["plate"][0] | |
| d0 = float(np.linalg.norm(can[0].mean(0) - plate[0].mean(0))) | |
| dl = float(np.linalg.norm(can[-1].mean(0) - plate[-1].mean(0))) | |
| manifest.append({"src": src, "episode_index": ep_idx, "status": "ok", | |
| "n_can": counts.get("can", 0), "n_plate": counts.get("plate", 0), | |
| "T": int(T), "can_plate_d0": round(d0, 1), "can_plate_dlast": round(dl, 1), | |
| "used": used}) | |
| print(f"[{k+1}/{len(demos)}] {src}: can={counts.get('can',0)} " | |
| f"plate={counts.get('plate',0)} T={T} gap {d0:.0f}->{dl:.0f}px " | |
| f"({used.get('can','-')} | {used.get('plate','-')})", flush=True) | |
| if k < args.n_overlay: | |
| ov = os.path.join(outdir, "combined_overlay.mp4") | |
| vw = imageio.get_writer(ov, fps=15, macro_block_size=1) | |
| for t in range(T): | |
| im = frames[t].copy() | |
| for label_id, (tag, _, color) in enumerate(PROMPTS): | |
| r = res[tag] | |
| if r is None: | |
| continue | |
| tr = r[0] | |
| for pi in range(tr.shape[1]): | |
| x, y = tr[t, pi] | |
| cv2.circle(im, (int(x), int(y)), 1, color, -1) | |
| vw.append_data(im) | |
| vw.close() | |
| with open(os.path.join(args.out_root, "manifest.json"), "w") as f: | |
| json.dump(manifest, f, indent=2) | |
| ok = sum(1 for m in manifest if m.get("status") == "ok") | |
| both = sum(1 for m in manifest if m.get("n_can", 0) > 0 and m.get("n_plate", 0) > 0) | |
| print(f"[build] DONE: {ok} ok ({both} with BOTH objects), " | |
| f"{len(manifest)-ok} failed. -> {args.out_root}", flush=True) | |
| if __name__ == "__main__": | |
| main() | |