Official Code for "SurgMotion: A Video-Native Foundation Model for Universal Understanding of Surgical Videos"
Python
130
380 commits
updated Apr 1, 2026

SurgMotion is a video-native foundation model that shifts the learning paradigm from pixel-level reconstruction to latent motion prediction, with technical innovations tailored to surgical videos, built on top of V-JEPA 2.
Key innovations:
| Variant | Backbone | Parameters | Pre-training Data |
|---|---|---|---|
| SurgMotion-L | ViT-Large | 300M | SurgMotion-15M |
| SurgMotion-G | ViT-Giant-xformer | 1B | SurgMotion-15M |
SurgMotion achieves SOTA on all representative surgical tasks (workflow, action, segmentation, triplet, skill, depth estimation). For detailed results, see our paper and project page.
SurgMotion/
├── src/ # V-JEPA2 core: ViT, VideoMAE, datasets, masks
├── evals/ # Evaluation entry points & foundation phase probing
│ ├── main.py # Single-task entry: python -m evals.main --fname <yaml>
│ └── foundation_phase_probing/
│ ├── eval.py # Probing evaluation logic
│ ├── models.py # Probing head definitions
│ └── modelcustom/ # Per-model adapters (SurgMotion, DINOv3, SurgVLP, …)
├── configs/
│ └── foundation_model_probing/
│ ├── surgmotion/ # YAML configs per dataset
│ ├── dinov3/
│ ├── endofm/
│ ├── … # 15 model families supported
│ └── videomaev2/
├── data_process/ # End-to-end dataset preprocessing scripts
│ ├── autolaparo_prepare.py
│ ├── cholect80_prepare.py
│ ├── egosurgery_prepare.py
│ ├── m2cai2016_prepare.py
│ ├── ophnet_prepare.py
│ ├── pitvis_prepare.py
│ ├── pmlr50_prepare.py
│ ├── polypdiag_prepare.py
│ └── surgicalactions160_prepare.py
├── ckpts/ # Store all the foundation models
├── scripts/ # Batch probing & environment setup shells
├── foundation_models/ # Third-party model implementations (git submodules)
├── data/ # Data directory
├── setup.py # pip install -e .
└── requirements.txt # All dependencies (excluding EndoMamba)
conda create -n SurgMotion python=3.12 -y
conda activate SurgMotion
# Install PyTorch matching your CUDA version first:
# https://pytorch.org/get-started/locally/
pip install -e .
EndoMamba requires its own Conda env with custom CUDA extensions. Do not mix with the main environment.
bash scripts/srun_endomamba_complie.sh # Creates env + compiles extensions
conda activate endomamba # Use only for EndoMamba configs
| File | Scope |
|---|---|
requirements.txt | All dependencies (V-JEPA2 core + foundation probing) |
setup.py | pip install -e . reads requirements.txt automatically |
EndoMamba has its own isolated environment managed by
scripts/srun_endomamba_complie.sh.
All preprocessing scripts under data_process/ now follow a unified end-to-end pipeline and support one-command execution:
python data_process/<dataset>_prepare.py --step all
Common step options:
--step all|frames|metadata|clips
--window_size 64
--stride 1
--fps 1
--no_padding
Typical outputs:
clip_infos/*.txt — per-case frame path lists{train,val,test}_metadata.csv — frame-level metadata for dense clip generationclips_<window_size>f/{train,val,test}_dense_<window_size>f_detailed.csvclips_<window_size>f/clip_dense_<window_size>f_info/{train,val,test}/*.txtFrame-level metadata schema:
| Column | Description |
|---|---|
Case_ID | Numeric case / video identifier |
Frame_Path | Absolute/relative frame image path |
Phase_GT | Integer phase/class id for this frame |
Phase_Name | Human-readable phase/class name |
We use an online workflow recognition setting:
For window_size=64, stride=1:
[0, ..., 63][1, ..., 64][2, ..., 65]Padding at video start:
--no_padding to disable.For phase recognition:
1 fps.Example:
0-40: Phase 041-63: Phase 1[0, ..., 63] label is Phase 1.If reproduced results are lower than expected, dense sampling mismatch is one possible source, but not the only one. We also recommend checking:
Class weighting can strongly affect surgical long-tail performance. See implementation in evals/foundation_phase_probing/eval.py.
Most datasets already provide extracted frames in data/Surge_Frames/.... The pipelines read annotations and frames; frame extraction from videos (--step frames) is optional and only needed if you have raw mp4 files.
| Dataset | Script | Annotation Path | Frames Path | Extract? |
|---|---|---|---|---|
| Cholec80 | cholect80_prepare.py | cholec80/phase_annotations | Surge_Frames/Cholec80/frames/{videoXX}/ | Optional |
| AutoLaparo | autolaparo_prepare.py | autolaparo/task1/labels | Surge_Frames/AutoLaparo/frames/{NN}/ | Optional |
| M2CAI2016 | m2cai2016_prepare.py | m2cai16/{train,test}_dataset | Surge_Frames/M2CAI16/frames/{video}/ | No |
| EgoSurgery | egosurgery_prepare.py | EgoSurgery/annotations/phase | Surge_Frames/EgoSurgery/frames/{video_id}/ | No |
| PitVis | pitvis_prepare.py | pitvits/26531686 | Surge_Frames/PitVis/frames/video_{XX}/ | No |
| OphNet2024 | ophnet_prepare.py | OphNet2024_trimmed_phase/*.csv | Surge_Frames/OphNet2024_phase/frames/ | No |
| PmLR50 | pmlr50_prepare.py | PmLR50/PmLR50/labels/*.pickle | Surge_Frames/PmLR50/frames/{XX}/ | No |
| SurgicalActions160 | surgicalactions160_prepare.py | (from video filenames) | Surge_Frames/SurgicalActions160_v1/frames/ | Yes |
| PolypDiag | polypdiag_prepare.py | (from video filenames) | Surge_Frames/PolypDiag/frames/ | Yes |
All annotation and frame paths above are relative to the data/ directory (e.g., data/Landscopy/cholec80/phase_annotations).
The scripts are built around frame-based clip CSVs. Depending on whether you already have extracted frames or only raw videos, use the path that matches your data.
You already have image sequences under --frames_root (e.g. data/Surge_Frames/...).
| Step | What it does |
|---|---|
--step all | Runs metadata → clips. Does not decode videos in most scripts (see table below). |
--step metadata | Builds {train,val,test}_metadata.csv from annotations + Frame_Path. |
--step clips | Writes dense sliding-window clip lists and detailed CSVs via gen_clips.py. |
Typical command: python data_process/<dataset>_prepare.py --step all with correct --frames_root / annotation paths. No --videos_dir needed.
You only have .mp4 files and need JPEG/PNG frames under --frames_root first.
| Step | What it does |
|---|---|
--step frames | Decodes videos → frames (needs a directory of mp4s; flag name varies by script, usually --videos_dir or dataset-specific video roots). |
| Then | Run --step all or --step metadata then --step clips on the extracted frames. |
Typical two-stage flow:
python data_process/<dataset>_prepare.py --step frames --videos_dir /path/to/mp4s # if supported
python data_process/<dataset>_prepare.py --step all
--step all treats frame extraction| Script | --step all runs video→frames? | Notes |
|---|---|---|
cholect80_prepare.py | Yes, if --videos_dir exists | If the directory is missing, extraction is skipped and existing --frames_root is assumed. |
autolaparo_prepare.py | No | Use --step frames explicitly, then --step all or metadata + clips. |
m2cai2016_prepare.py, pitvis_prepare.py, ophnet_prepare.py, pmlr50_prepare.py, egosurgery_prepare.py | No | Same as AutoLaparo: extraction is only --step frames. |
surgicalactions160_prepare.py, polypdiag_prepare.py | Yes | all runs the full video pipeline (including optional rename where applicable), then metadata and clips. |
| You have | Use |
|---|---|
| Frames on disk | --step all (or metadata + clips only). Point --frames_root at the image folders. |
| Only videos | --step frames first (where supported), with the script’s video path argument, then --step all. |
Bundled Surge_Frames + annotations | Frames-first row above; no extraction step. |
python data_process/cholect80_prepare.py \
--frames_root data/Surge_Frames/Cholec80/frames \
--annot_dir data/Landscopy/cholec80/phase_annotations \
--output_dir data/Surge_Frames/Cholec80 \
--step all \
--debug
python data_process/surgicalactions160_prepare.py \
--src_root data/Landscopy/SurgicalActions160 \
--fps 1 \
--step all
| Model | Identifier | Architecture | Source |
|---|---|---|---|
| DINOv3 | dinov3 | ViT-L, ViT-H | GitHub |
| Endo-FM | endofm | ViT-B | GitHub |
| EndoMamba | endomamba | Mamba-S | GitHub |
| EndoSSL | endossl | ViT-L | GitHub |
| EndoViT | endovit | ViT-L | GitHub |
| GastroNet | gastronet | ViT-S | IEEE Xplore |
| GSViT | gsvit | ViT | GitHub |
| SelfSupSurg | selfsupsurg | ResNet-50 | GitHub |
| SurgeNet | surgenet | CAFormer-XL, ConvNeXtV2 | GitHub |
| SurgVLP | surgvlp | ResNet-50 | GitHub |
| VideoMAEv2 | videomaev2 | ViT-L, ViT-H, ViT-g | GitHub |
# For SurgMotion
SurgMotion/ckpts
# For Other Foundation Models
SurgMotion/ckpts/ckpts_foundation
# For Other Foundation Models
SurgMotion/evals/foundation_phase_probing/modelcustom/adapters
# SurgMotion
python -m evals.main \
--fname configs/foundation_model_probing/surgmotion/AutoLaparo/surgmotion_vitl_64f_autolaparo.yaml \
--devices cuda:0
# Dinov3
python -m evals.main \
--fname configs/foundation_model_probing/dinov3/AutoLaparo/dinov3_vitl_64f_autolaparo.yaml \
--devices cuda:0
Edit the task list in scripts/run_foundation_probing.sh, then run:
bash scripts/run_foundation_probing.sh
The script auto-assigns one GPU per task from the available pool (default: all 8 GPUs). Logs are saved under logs/foundation/<Dataset>/.
data_process/<dataset>_prepare.py following the existing template (see polypdiag_prepare.py for reference).Case_ID, Frame_Path, Phase_GT, Phase_Name.gen_clips.py (or --step clips) to produce clips_64f/*_dense_64f_detailed.csv.configs/foundation_model_probing/<model>/<Dataset>/.evals/foundation_phase_probing/modelcustom/adapters/:# Input: any shape, e.g. [B, C, F, H, W]
# Output: [B, F*N, D] (spatial-temporal tokens)
evals/foundation_phase_probing/modelcustom/foundation_model_wrapper.py:elif model_type == 'your_model':
from .adapters.your_model_adapter import YourModelAdapter
adapter = YourModelAdapter.from_config(
resolution=resolution,
checkpoint=checkpoint,
model_name=model_name
)
configs/foundation_model_probing/your_model/<Dataset>/.scripts/run_foundation_probing.sh and run.This project is built on top of V-JEPA 2 by Meta. We sincerely thank the authors of the following works whose open-source models were used in our benchmark:
DINOv2 | Endo-FM | EndoMamba | EndoSSL | EndoViT | GastroNet | GSViT | SelfSupSurg | SurgeNet | SurgVISTA | SurgVLP | VideoMAEv2
If you find our work helpful, please cite our paper.
@misc{wu2026surgmotionvideonativefoundationmodel,
title={SurgMotion: A Video-Native Foundation Model for Universal Understanding of Surgical Videos},
author={Jinlin Wu and Felix Holm and Chuxi Chen and An Wang and Yaxin Hu and Xiaofan Ye and Zelin Zang and Miao Xu and Lihua Zhou and Huai Liao and Danny T. M. Chan and Ming Feng and Wai S. Poon and Hongliang Ren and Dong Yi and Nassir Navab and Gaofeng Meng and Jiebo Luo and Hongbin Liu and Zhen Lei},
year={2026},
eprint={2602.05638},
archivePrefix={arXiv},
primaryClass={cs.CV},
url={https://arxiv.org/abs/2602.05638},
}
Centre for Artificial Intelligence and Robotics, Hong Kong Institute of Science and Innovation, CAS
Python
93.0%
Cuda
4.7%
C++
1.7%
Official Code for "SurgMotion: A Video-Native Foundation Model for Universal Understanding of Surgical Videos"
Python
130
380 commits
updated Apr 1, 2026

SurgMotion is a video-native foundation model that shifts the learning paradigm from pixel-level reconstruction to latent motion prediction, with technical innovations tailored to surgical videos, built on top of V-JEPA 2.
Key innovations:
| Variant | Backbone | Parameters | Pre-training Data |
|---|---|---|---|
| SurgMotion-L | ViT-Large | 300M | SurgMotion-15M |
| SurgMotion-G | ViT-Giant-xformer | 1B | SurgMotion-15M |
SurgMotion achieves SOTA on all representative surgical tasks (workflow, action, segmentation, triplet, skill, depth estimation). For detailed results, see our paper and project page.
SurgMotion/
├── src/ # V-JEPA2 core: ViT, VideoMAE, datasets, masks
├── evals/ # Evaluation entry points & foundation phase probing
│ ├── main.py # Single-task entry: python -m evals.main --fname <yaml>
│ └── foundation_phase_probing/
│ ├── eval.py # Probing evaluation logic
│ ├── models.py # Probing head definitions
│ └── modelcustom/ # Per-model adapters (SurgMotion, DINOv3, SurgVLP, …)
├── configs/
│ └── foundation_model_probing/
│ ├── surgmotion/ # YAML configs per dataset
│ ├── dinov3/
│ ├── endofm/
│ ├── … # 15 model families supported
│ └── videomaev2/
├── data_process/ # End-to-end dataset preprocessing scripts
│ ├── autolaparo_prepare.py
│ ├── cholect80_prepare.py
│ ├── egosurgery_prepare.py
│ ├── m2cai2016_prepare.py
│ ├── ophnet_prepare.py
│ ├── pitvis_prepare.py
│ ├── pmlr50_prepare.py
│ ├── polypdiag_prepare.py
│ └── surgicalactions160_prepare.py
├── ckpts/ # Store all the foundation models
├── scripts/ # Batch probing & environment setup shells
├── foundation_models/ # Third-party model implementations (git submodules)
├── data/ # Data directory
├── setup.py # pip install -e .
└── requirements.txt # All dependencies (excluding EndoMamba)
conda create -n SurgMotion python=3.12 -y
conda activate SurgMotion
# Install PyTorch matching your CUDA version first:
# https://pytorch.org/get-started/locally/
pip install -e .
EndoMamba requires its own Conda env with custom CUDA extensions. Do not mix with the main environment.
bash scripts/srun_endomamba_complie.sh # Creates env + compiles extensions
conda activate endomamba # Use only for EndoMamba configs
| File | Scope |
|---|---|
requirements.txt | All dependencies (V-JEPA2 core + foundation probing) |
setup.py | pip install -e . reads requirements.txt automatically |
EndoMamba has its own isolated environment managed by
scripts/srun_endomamba_complie.sh.
All preprocessing scripts under data_process/ now follow a unified end-to-end pipeline and support one-command execution:
python data_process/<dataset>_prepare.py --step all
Common step options:
--step all|frames|metadata|clips
--window_size 64
--stride 1
--fps 1
--no_padding
Typical outputs:
clip_infos/*.txt — per-case frame path lists{train,val,test}_metadata.csv — frame-level metadata for dense clip generationclips_<window_size>f/{train,val,test}_dense_<window_size>f_detailed.csvclips_<window_size>f/clip_dense_<window_size>f_info/{train,val,test}/*.txtFrame-level metadata schema:
| Column | Description |
|---|---|
Case_ID | Numeric case / video identifier |
Frame_Path | Absolute/relative frame image path |
Phase_GT | Integer phase/class id for this frame |
Phase_Name | Human-readable phase/class name |
We use an online workflow recognition setting:
For window_size=64, stride=1:
[0, ..., 63][1, ..., 64][2, ..., 65]Padding at video start:
--no_padding to disable.For phase recognition:
1 fps.Example:
0-40: Phase 041-63: Phase 1[0, ..., 63] label is Phase 1.If reproduced results are lower than expected, dense sampling mismatch is one possible source, but not the only one. We also recommend checking:
Class weighting can strongly affect surgical long-tail performance. See implementation in evals/foundation_phase_probing/eval.py.
Most datasets already provide extracted frames in data/Surge_Frames/.... The pipelines read annotations and frames; frame extraction from videos (--step frames) is optional and only needed if you have raw mp4 files.
| Dataset | Script | Annotation Path | Frames Path | Extract? |
|---|---|---|---|---|
| Cholec80 | cholect80_prepare.py | cholec80/phase_annotations | Surge_Frames/Cholec80/frames/{videoXX}/ | Optional |
| AutoLaparo | autolaparo_prepare.py | autolaparo/task1/labels | Surge_Frames/AutoLaparo/frames/{NN}/ | Optional |
| M2CAI2016 | m2cai2016_prepare.py | m2cai16/{train,test}_dataset | Surge_Frames/M2CAI16/frames/{video}/ | No |
| EgoSurgery | egosurgery_prepare.py | EgoSurgery/annotations/phase | Surge_Frames/EgoSurgery/frames/{video_id}/ | No |
| PitVis | pitvis_prepare.py | pitvits/26531686 | Surge_Frames/PitVis/frames/video_{XX}/ | No |
| OphNet2024 | ophnet_prepare.py | OphNet2024_trimmed_phase/*.csv | Surge_Frames/OphNet2024_phase/frames/ | No |
| PmLR50 | pmlr50_prepare.py | PmLR50/PmLR50/labels/*.pickle | Surge_Frames/PmLR50/frames/{XX}/ | No |
| SurgicalActions160 | surgicalactions160_prepare.py | (from video filenames) | Surge_Frames/SurgicalActions160_v1/frames/ | Yes |
| PolypDiag | polypdiag_prepare.py | (from video filenames) | Surge_Frames/PolypDiag/frames/ | Yes |
All annotation and frame paths above are relative to the data/ directory (e.g., data/Landscopy/cholec80/phase_annotations).
The scripts are built around frame-based clip CSVs. Depending on whether you already have extracted frames or only raw videos, use the path that matches your data.
You already have image sequences under --frames_root (e.g. data/Surge_Frames/...).
| Step | What it does |
|---|---|
--step all | Runs metadata → clips. Does not decode videos in most scripts (see table below). |
--step metadata | Builds {train,val,test}_metadata.csv from annotations + Frame_Path. |
--step clips | Writes dense sliding-window clip lists and detailed CSVs via gen_clips.py. |
Typical command: python data_process/<dataset>_prepare.py --step all with correct --frames_root / annotation paths. No --videos_dir needed.
You only have .mp4 files and need JPEG/PNG frames under --frames_root first.
| Step | What it does |
|---|---|
--step frames | Decodes videos → frames (needs a directory of mp4s; flag name varies by script, usually --videos_dir or dataset-specific video roots). |
| Then | Run --step all or --step metadata then --step clips on the extracted frames. |
Typical two-stage flow:
python data_process/<dataset>_prepare.py --step frames --videos_dir /path/to/mp4s # if supported
python data_process/<dataset>_prepare.py --step all
--step all treats frame extraction| Script | --step all runs video→frames? | Notes |
|---|---|---|
cholect80_prepare.py | Yes, if --videos_dir exists | If the directory is missing, extraction is skipped and existing --frames_root is assumed. |
autolaparo_prepare.py | No | Use --step frames explicitly, then --step all or metadata + clips. |
m2cai2016_prepare.py, pitvis_prepare.py, ophnet_prepare.py, pmlr50_prepare.py, egosurgery_prepare.py | No | Same as AutoLaparo: extraction is only --step frames. |
surgicalactions160_prepare.py, polypdiag_prepare.py | Yes | all runs the full video pipeline (including optional rename where applicable), then metadata and clips. |
| You have | Use |
|---|---|
| Frames on disk | --step all (or metadata + clips only). Point --frames_root at the image folders. |
| Only videos | --step frames first (where supported), with the script’s video path argument, then --step all. |
Bundled Surge_Frames + annotations | Frames-first row above; no extraction step. |
python data_process/cholect80_prepare.py \
--frames_root data/Surge_Frames/Cholec80/frames \
--annot_dir data/Landscopy/cholec80/phase_annotations \
--output_dir data/Surge_Frames/Cholec80 \
--step all \
--debug
python data_process/surgicalactions160_prepare.py \
--src_root data/Landscopy/SurgicalActions160 \
--fps 1 \
--step all
| Model | Identifier | Architecture | Source |
|---|---|---|---|
| DINOv3 | dinov3 | ViT-L, ViT-H | GitHub |
| Endo-FM | endofm | ViT-B | GitHub |
| EndoMamba | endomamba | Mamba-S | GitHub |
| EndoSSL | endossl | ViT-L | GitHub |
| EndoViT | endovit | ViT-L | GitHub |
| GastroNet | gastronet | ViT-S | IEEE Xplore |
| GSViT | gsvit | ViT | GitHub |
| SelfSupSurg | selfsupsurg | ResNet-50 | GitHub |
| SurgeNet | surgenet | CAFormer-XL, ConvNeXtV2 | GitHub |
| SurgVLP | surgvlp | ResNet-50 | GitHub |
| VideoMAEv2 | videomaev2 | ViT-L, ViT-H, ViT-g | GitHub |
# For SurgMotion
SurgMotion/ckpts
# For Other Foundation Models
SurgMotion/ckpts/ckpts_foundation
# For Other Foundation Models
SurgMotion/evals/foundation_phase_probing/modelcustom/adapters
# SurgMotion
python -m evals.main \
--fname configs/foundation_model_probing/surgmotion/AutoLaparo/surgmotion_vitl_64f_autolaparo.yaml \
--devices cuda:0
# Dinov3
python -m evals.main \
--fname configs/foundation_model_probing/dinov3/AutoLaparo/dinov3_vitl_64f_autolaparo.yaml \
--devices cuda:0
Edit the task list in scripts/run_foundation_probing.sh, then run:
bash scripts/run_foundation_probing.sh
The script auto-assigns one GPU per task from the available pool (default: all 8 GPUs). Logs are saved under logs/foundation/<Dataset>/.
data_process/<dataset>_prepare.py following the existing template (see polypdiag_prepare.py for reference).Case_ID, Frame_Path, Phase_GT, Phase_Name.gen_clips.py (or --step clips) to produce clips_64f/*_dense_64f_detailed.csv.configs/foundation_model_probing/<model>/<Dataset>/.evals/foundation_phase_probing/modelcustom/adapters/:# Input: any shape, e.g. [B, C, F, H, W]
# Output: [B, F*N, D] (spatial-temporal tokens)
evals/foundation_phase_probing/modelcustom/foundation_model_wrapper.py:elif model_type == 'your_model':
from .adapters.your_model_adapter import YourModelAdapter
adapter = YourModelAdapter.from_config(
resolution=resolution,
checkpoint=checkpoint,
model_name=model_name
)
configs/foundation_model_probing/your_model/<Dataset>/.scripts/run_foundation_probing.sh and run.This project is built on top of V-JEPA 2 by Meta. We sincerely thank the authors of the following works whose open-source models were used in our benchmark:
DINOv2 | Endo-FM | EndoMamba | EndoSSL | EndoViT | GastroNet | GSViT | SelfSupSurg | SurgeNet | SurgVISTA | SurgVLP | VideoMAEv2
If you find our work helpful, please cite our paper.
@misc{wu2026surgmotionvideonativefoundationmodel,
title={SurgMotion: A Video-Native Foundation Model for Universal Understanding of Surgical Videos},
author={Jinlin Wu and Felix Holm and Chuxi Chen and An Wang and Yaxin Hu and Xiaofan Ye and Zelin Zang and Miao Xu and Lihua Zhou and Huai Liao and Danny T. M. Chan and Ming Feng and Wai S. Poon and Hongliang Ren and Dong Yi and Nassir Navab and Gaofeng Meng and Jiebo Luo and Hongbin Liu and Zhen Lei},
year={2026},
eprint={2602.05638},
archivePrefix={arXiv},
primaryClass={cs.CV},
url={https://arxiv.org/abs/2602.05638},
}
Centre for Artificial Intelligence and Robotics, Hong Kong Institute of Science and Innovation, CAS
Python
93.0%
Cuda
4.7%
C++
1.7%