# Panda Mask Overlay Generation

## What the masks show
Each `NNNNNN_mask.png` is the base RGB frame with:
- **MuJoCo robot overlay** — rendered from calibrated camera pose, blended 60/40 on the robot region
- **EEF keypoint** — green crosshair at the projected hand body position
- **Ground projection** — green circle at the EEF's XY projected to Z=0
- **Height annotation** — dotted line from EEF to ground with `h=X.XXXm` label
- **Gripper value** — "Gripper: X.XX" text box in the top right

## How to regenerate

From the lab server:
```bash
cd /data/cameron/para/panda_streaming
MUJOCO_GL=egl python3 scripts/render_panda_masks.py \
  --data_dir /data/cameron/panda_data/data_20260420_115853_632_frames
```

Or inline — the rendering code lives in the conversation history. Key parameters:

### Calibrated camera params (from 22-sample hand-eye calibration)
```python
T_cam_world = np.array([
    [ 0.94774869,  0.29251588,  0.12730624, -0.41554978],
    [ 0.24809099, -0.42493276, -0.87056476,  0.33529506],
    [-0.20055743,  0.85666015, -0.47530002,  1.1555837 ],
    [ 0.,          0.,          0.,          1.        ]])

cam_K = np.array([
    [1372.7, 0.0,    956.9],
    [0.0,    1357.2, 555.0],
    [0.0,    0.0,    1.0  ]])
```

### Dependencies
- MuJoCo with EGL backend (`MUJOCO_GL=egl`)
- ExoConfigs/panda_exo_handeye_4x2.py (4x2 ArUco board config)
- exo_utils.py (render_from_camera_pose, position_exoskeleton_meshes)
- Run from `/data/cameron/para/panda_streaming/` (model paths are relative)

### Data format
- Input: `NNNNNN.png` (BGR, 1920x1080) + `NNNNNN.npy` (float32[8]: 7 joints + gripper)
- Output: `NNNNNN_mask.png` (same resolution, same directory)

## Data viewer
- URL: https://omidlab.net/data_viewer/#panda_real/panda_first_collect
- Config: `/data/cameron/para/.agents/reports/data_viewer/datasets.json`
- Dataset ID: `panda_first_collect`
- Experiment ID: `panda_real`
- The viewer auto-detects `_mask.png` suffix and shows a toggle chip
