Vision-only (ResNet-18)

August 27, 2026 · View on GitHub

EgoEMG dataset statistics

EgoEMG

A multimodal egocentric dataset with bilateral surface EMG and vision for hand pose estimation
EMG-to-pose · vision-to-pose · EMG+vision fusion

Release Code license CI Python Project Page

[ Web · Setup · Training · Visualization · Results · License · Citation ]


✨ Highlights

  • 📦 Multi-modal dataset & benchmark — 53 participants, 18+ h of synchronized recordings: bilateral wristband EMG, IMU, egocentric RGB, RGB-D, and mocap joint angles.
  • 🧠 EMG-to-pose — EMGFormer: 13.8° Avg MAE on EgoEMG (M); 12.3° User+Stage MAE on the EMG2Pose benchmark (S) — ahead of all prior methods.
  • 👁️ Vision-to-pose5.85° with ResNet-18 on the egocentric webcam stream.
  • 🔀 EMG+vision fusion5.41° with ResNet-18 fusion: 7.5% better than vision-only at only +3M parameters.

🎯 Headline results

MAE in degrees. * = released-checkpoint values (reproduce with the commands in Results and evaluation); unstarred values are paper-reported. Avg = average over the Gesture/User/Both test splits (per-sample-weighted for EMG-to-pose, unweighted split mean for vision and fusion); the EMG2Pose row follows the benchmark's User+Stage protocol.

TaskSplitOursBest baseline
EMG-to-pose on EgoEMGAvg (Gesture/User/Both)13.8° (EMGFormer-M) *15.8° (emg2pose)
EMG-to-pose on EMG2PoseUser+Stage12.3° (EMGFormer-S)14.6° (Position/Velocity MT)
Vision-to-pose on EgoEMGAvg (Gesture/User/Both)5.85° (ResNet-18) *
EMG+vision fusion on EgoEMGAvg (Gesture/User/Both)5.41° (ResNet-18) *5.85° (vision-only)

Full per-method and per-split breakdowns are in the Results and evaluation section below.

⚙️ Setup

conda env create -f environment.yml && conda activate egoemg
pip install -e '.[viz]'

Download data and checkpoints with Asset Setup, then set export EGOEMG_ROOT=/path/to/egoemg_assets.

🚀 Training

# EMG-to-pose (EMGFormer-M)
python -m egoemg.train \
  experiment=emgformer/regression_egoemg \
  'trainer.devices=[0,1,2,3,4,5]' '+trainer.strategy=ddp' \
  batch_size=500

# Vision-only (ResNet-18)
python -m egoemg.train \
  experiment=fusion/vision_resnet18 \
  train=true eval=true 'trainer.devices=[0]'

# EMG+vision fusion
python -m egoemg.train \
  experiment=fusion/fusion_rn18_s_center_16ch_wl7790 \
  train=true eval=true 'trainer.devices=[0,1,2,3,4]'

# EMGFormer multi-task pretraining
python -m egoemg.train_pretrain \
  experiment=emgformer/pretrain_multitask \
  train=true eval=false 'trainer.devices=[0]'

Hardware: the EMG example uses six GPUs, fusion five. For a single-GPU smoke run, set trainer.devices=[0] and reduce batch_size.

Experiment configs live in config/experiment/{emgformer,fusion,emg2pose}/. For evaluation use egoemg.test_analysis (fusion configs default train=true). The EMG+vision fusion model (MidFusionPoseFormer) is detailed in Fusion Architecture.

🎥 Visualization

ModeOutput
visionoverlay MP4 + per-hand crop MP4s
timelineEMG / joint-angle timeline PNG
meshworld-space MANO/FK meshes (GLB) + occlusion renders
fk_vs_manoFK vs MANO comparison
python scripts/viz/visualize_dataset.py vision \
  --memmap-dir ${EGOEMG_ROOT}/data/EgoEMG_full_memmap \
  --allintra-root ${EGOEMG_ROOT}/data/EgoEMG_videos \
  --crops-dir ${EGOEMG_ROOT}/data/EgoEMG_crops \
  --data-root ${EGOEMG_ROOT}/data \
  --output-dir /tmp/egoemg_vision_viz \
  --episode-id episode_000000 --stride 10 --max-frames 300

Sample predictions on the preview shard's three episodes — a vision + MANO-mesh overlay, embedded for each as a native video player (uploaded via Attach files).

episode_000020 · wmh · train / user / gesture / both

https://github.com/user-attachments/assets/86ab0035-fccc-4117-a66d-f82a5a12e9e6

episode_000008 · zbk · user / both

https://github.com/user-attachments/assets/80e4646c-51d2-43c4-9d1f-5d63b06e82f7

episode_000028 · wsj · train / gesture

https://github.com/user-attachments/assets/66da0936-609e-4e83-9077-a626f9b1fe9b

Preview / small dataset

Every workflow above also runs end-to-end on a small preview shard instead of the full EgoEMG_full_memmap. It is a 3-episode, v3-schema memmap in the same flat layout as the published dataset_egoemg_unified, so it drops in as a mini dataset root: point the same commands at its data/memmap_data.

Important

Download the shard once — Baidu Netdisk is the official release path. Open the share https://pan.baidu.com/s/1aG2e-mHJkmP4KiYtYRcReA (extraction code 8059), download the whole dataset_egoemg_preview folder under /EgoEMG_release/, and place it at $EGOEMG_ROOT/data/dataset_egoemg_preview.

# EMG-to-pose eval (EMGFormer-M, 8-ch) on the shard
python -m egoemg.test_analysis \
  experiment=emgformer/egoemg_emgformer_middle \
  'checkpoint=checkpoints/egoemg_emgformer_middle.ckpt' \
  egoemg_unified_memmap_dir=$EGOEMG_ROOT/data/dataset_egoemg_preview/data/memmap_data \
  'trainer.devices=[0]' \
  datamodule.per_dataset_norm_stats_path=assets/per_dataset_norm_stats_unified.json

# Vision overlay (episode_000028 is in the shard; needs MANO model files per
# docs/ASSET_SETUP.md §3 — pass --mano-model-path or place them under
# $EGOEMG_ROOT/data/mano_data/models)
python scripts/viz/visualize_dataset.py vision \
  --memmap-dir $EGOEMG_ROOT/data/dataset_egoemg_preview/data/memmap_data \
  --allintra-root $EGOEMG_ROOT/data/dataset_egoemg_preview/data/webcam_videos \
  --crops-dir $EGOEMG_ROOT/data/dataset_egoemg_preview/data/pre-crop_webcam_videoframes \
  --data-root $EGOEMG_ROOT/data/dataset_egoemg_preview/data \
  --episode-id episode_000028 --stride 10 --max-frames 300 \
  --mano-model-path $EGOEMG_ROOT/data/mano_data/models

# Smoke training (1 epoch, 2 batches; small batch_size for a single GPU)
python -m egoemg.train experiment=emgformer/egoemg_emgformer_small \
  egoemg_unified_memmap_dir=$EGOEMG_ROOT/data/dataset_egoemg_preview/data/memmap_data \
  'trainer.devices=[0]' 'trainer.max_epochs=1' batch_size=8 \
  '+trainer.limit_train_batches=2' '+trainer.limit_val_batches=0' \
  datamodule.per_dataset_norm_stats_path=assets/per_dataset_norm_stats_unified.json

📊 Results and evaluation

Full benchmark tables

* marks values measured with the released checkpoints (commands below); other cells are paper-reported. MAE in degrees.

EMG-to-pose on EgoEMG (per-user mean ± std; Avg = per-sample-weighted):

MethodParamsGestureUserBothAvg.
EMGFormer-S3.5M12.3 ± 1.516.0 ± 0.616.3 ± 1.514.1 *
EMGFormer-M6.6M11.7 ± 1.615.9 ± 0.616.4 ± 1.513.8 *
EMGFormer-L16.3M11.9 ± 1.616.0 ± 0.816.4 ± 1.213.9 *
emg2pose3.0M15.5 ± 1.314.8 ± 2.916.3 ± 0.615.8
vEMG2Pose6.0M15.0 ± 1.416.3 ± 1.717.3 ± 1.315.9
NeuroPose6.4M15.8 ± 1.215.7 ± 1.316.3 ± 0.716.1
SensingDynamics1.0M16.2 ± 1.116.4 ± 0.316.7 ± 0.816.4

EMG-to-pose on the EMG2Pose benchmark (per-user mean ± std across the User / Stage / User+Stage test splits):

MethodParamsUserStageUser+Stage
EMGFormer-S3.5M12.5 ± 1.111.1 ± 1.212.3 ± 1.1
EMGFormer-M6.6M12.4 ± 1.110.2 ± 1.112.4 ± 1.1
EMGFormer-L16.3M12.3 ± 1.19.3 ± 1.112.3 ± 1.1
emg2pose3.0M12.6 ± 1.315.2 ± 1.615.6 ± 1.3
vEMG2Pose6.0M12.2 ± 1.315.2 ± 1.615.8 ± 1.4
NeuroPose6.4M13.2 ± 1.117.2 ± 1.717.5 ± 1.5
SensingDynamics1.0M15.5 ± 1.418.8 ± 1.618.7 ± 1.6
Position MT6.0M11.5 ± 1.214.0 ± 1.614.6 ± 1.3
Velocity MT6.0M11.6 ± 1.313.9 ± 1.614.6 ± 1.3
CLDM7.0M11.3 ± 1.014.3 ± 1.514.7 ± 1.4

Vision and fusion on EgoEMG (identical center frames; Frz./FT = frozen/fine-tuned; Δavg = fusion gain):

BackboneUpdateVision AvgFusion AvgΔavg
ResNet-18FT5.85 *5.41 *+0.44
ViT-S/14FT6.04 *5.56 *+0.48
ResNet-50Frz.5.275.19+0.09
ResNet-152Frz.5.115.06+0.05
ViT-B/14Frz.5.785.75+0.03
ViT-L/14Frz.5.395.36+0.03
WiLoRFrz.4.734.68+0.04

Evaluation

S/M/L in this README map to the small/middle/large config and checkpoint variants (e.g. EMGFormer-Megoemg_emgformer_middle.ckpt).

export EGOEMG_ROOT=/absolute/path/to/dataset_root

# EgoEMG EMGFormer-M (per-group stats + overall)
python -m egoemg.test_analysis \
  experiment=emgformer/egoemg_emgformer_middle \
  'checkpoint=checkpoints/egoemg_emgformer_middle.ckpt'

# EMG2Pose benchmark (EMGFormer-S)
python -m egoemg.test_analysis \
  experiment=emgformer/emg2pose_emgformer_small \
  'checkpoint=checkpoints/emg2pose_emgformer_small.ckpt' \
  data_location=${EGOEMG_ROOT}/data/emg2pose_memmap

# Vision-only ResNet-18
python -m egoemg.test_analysis \
  experiment=fusion/vision_resnet18 \
  'checkpoint=checkpoints/vision_resnet18.ckpt'

# Vision-only ViT-S/14
python -m egoemg.test_analysis \
  experiment=fusion/vision_vit_small \
  'checkpoint=checkpoints/vision_vit_small.ckpt'

# Fusion (ResNet-18 + 16ch EMG, WL 7790)
python -m egoemg.test_analysis \
  experiment=fusion/fusion_rn18_s_center_16ch_wl7790 \
  'checkpoint=checkpoints/fusion_resnet18_emgfusion_center.ckpt'

# Fusion (ViT-S/14 + 16ch EMG, WL 7790)
python -m egoemg.test_analysis \
  experiment=fusion/fusion_vits_s_center_eval_released \
  'checkpoint=checkpoints/fusion_vit_emgfusion_center.ckpt'

🗂️ Repository Layout

egoemg/        models, datasets, training/eval, vendored UmeTrack FK
config/        Hydra experiments ({emgformer,fusion,emg2pose}/ + lineage/)
scripts/       data conversion, downloads, visualization, paper figures
experiments/   shell launchers for the paper's experiments
docs/          asset setup, support scope, config & fusion architecture
assets/        EMG layout figures and normalization statistics

📜 License

Code: MIT. Third-party material (incl. UmeTrack, CC-BY-NC-4.0) under their own terms — see THIRD_PARTY_NOTICES.md.

📖 Citing EgoEMG

The paper is under review. Until a public version is available, cite this repository and the exact commit — GitHub renders CITATION.cff.

Copyable BibTeX
@misc{egoemg2026,
  title        = {EgoEMG: A multimodal egocentric dataset with bilateral surface
                  EMG and vision for hand pose estimation},
  author       = {Zhenqi Shi and others},
  howpublished = {GitHub repository},
  note         = {https://github.com/zhenqis123/EgoEMG},
  year         = {2026},
}

The preprint entry and full author list will be published here once the paper is public.