Koichi Namekata1,2,3 · Yash Kant1,2† · Zhizheng Liu1,4 · Ryan D Burgert1,2,5 · Yuancheng Xu1,2 . Kuan Heng Lin1,6 · Emmett Steven2 . Julien Philip1 . Li Ma1 . Andrea Vedaldi3 . Paul Debevec1,2 . Ning Yu1,2†
1Eyeline Labs · 2Netflix · 3University of Oxford · 4University of California, Los Angeles · 5Stony Brook University · 6Columbia University
†Equal supervision.
K.N., Z.L., R.B., and K.H.L. performed this work during an internship at Netflix and Eyeline Labs.
This is the official implementation of Go-with-the-Track: Video Compositing and Motion Control with Point Tracking.
Go-with-the-Track is a video generation framework that unifies spatio-temporal motion control and reference image insertion via point-tracks. While conventional point-tracks are defined as the 2D flow of a point strictly within the generated video sequence, we extend this definition by anchoring the point-tracks to the reference images, explicitly establishing point correspondences between the generated video frames and the reference content. Treating these correspondences as integral to point-track conditioning, Go-with-the-Track enables fine-grained compositing and motion control throughout the video.
The code has been tested on Ubuntu 22.04.5 LTS.
Make sure to run the following commands in a GPU-enabled environment.
For CUDA 11.8 (x86):
conda create -n gwtt python=3.10.*
conda activate gwtt
python -m pip install -e . --no-build-isolation
Download the checkpoints from Eyeline-Labs/Go-with-the-Track and place them under ./checkpoints/.
For example:
pip install -U "huggingface_hub"
#set up cache directory
export SCRATCHDIR="/path/to/your/scratchdir" # <--- CHANGE THIS to your scratch directory
export HF_HOME=$SCRATCHDIR/hf_home
export HF_HUB_CACHE=$HF_HOME/hub
mkdir -p "$HF_HUB_CACHE"
#download checkpoints
mkdir -p "$SCRATCHDIR/checkpoints/Go-with-the-Track"
HF_XET_HIGH_PERFORMANCE=1 hf download Eyeline-Labs/Go-with-the-Track \
--repo-type model \
--local-dir "$SCRATCHDIR/checkpoints/Go-with-the-Track"
ln -s "$SCRATCHDIR/checkpoints/Go-with-the-Track" checkpoints
Expected Folder Structure:
./checkpoints
./ckpt_wan
./Wan-AI/
./Wan2.2-T2V-A14B/
high_noise_model/
low_noise_model/
./480P
low.safetensors
high.safetensors
./720P
low.safetensors
high.safetensors
Download the evaluation dataset from Eyeline-Labs/Go-with-the-Track and save it as eval_data.
For example:
# Set up cache directory
export HF_HOME=$SCRATCHDIR/hf_home
export HF_HUB_CACHE=$HF_HOME/hub
mkdir -p "$HF_HUB_CACHE"
# Download dataset
mkdir -p "$SCRATCHDIR/eval_data/Go-with-the-Track"
HF_XET_HIGH_PERFORMANCE=1 hf download Eyeline-Labs/Go-with-the-Track \
--repo-type dataset \
--local-dir "$SCRATCHDIR/eval_data/Go-with-the-Track"
ln -s "$SCRATCHDIR/eval_data/Go-with-the-Track" eval_data
Expected Folder Structure:
./eval_data/
{Video name 1}/
stylized_prompt.txt
track.npy
video.mp4 (<- strictly 49 frames; 480P or 720P)
video720.mp4 (<- strictly 49 frames; guaranteed 720P)
ref/
0.png (up to 720P)
0.npy
1.png
1.npy
...
3.png
3.npy
{Video name 2}/
...
By default, the model is trained to generate 49 frames at 480P resolution, with the number of reference frames ranging between 1 and 4. The following demonstrates how to run inference on the evaluation dataset downloaded above.
Using 1 GPU:
python run_inference_dataset.py \
--config_path "./configs/480P.sh" \
--path_to_dataset ./eval_data \
--output_folder ./tmp_output/wan22_480P \
--fps 15 \
--skip_exist 1 \
--save_minimal 0 \
--max_trajectory_num 10000 \
--cfg_scale 3.0 \
--gpus 1
This variant uses the same base model, further fine-tuned on the 720P dataset for 4,000 iterations starting from the 480P checkpoint.
Using 4 GPUs:
python run_inference_dataset.py \
--config_path "./configs/720P.sh" \
--path_to_dataset ./eval_data \
--output_folder ./tmp_output/wan22_720P \
--fps 15 \
--skip_exist 1 \
--save_minimal 0 \
--max_trajectory_num 10000 \
--cfg_scale 3.0 \
--gpus 4
After running the inference commands above, the results will be stored in your output directory:
{output_folder}/
{video_name}.mp4 <- Generated video (MP4 format)
{video_name}.gif <- Generated video (GIF format)
Visualize/
{video_name}/
combined.gif <- Visualization of point-track conditions, reference images, and source videos (if available)
Go-with-the-Track generates video by establishing direct, point-based correspondences between Reference Frames and generated videos. To achieve this, the model requires a specific directory structure and data format for inputting reference images and the point-track conditions for both reference frames and generated sequences.
eval_data/
{Example 1}/
ref/
{reference_id 1}.png
{reference_id 1}.npy
{reference_id 2}.png
{reference_id 2}.npy
...
track.npy
stylized_prompt.txt
(Optional) video.mp4
{Example 2}/
...
Explanation
Each video example must be contained in its own folder within the dataset directory.
track.npy: The point-track conditions for generated frames.
ref/: A folder containing 1 to 4 reference images (.png) and their corresponding reference point-tracks (.npy).
reference_id: This can be any arbitrary integer, but both the reference image and its corresponding reference point-track must share the same ID (e.g., 100.png and 100.npy).video.mp4: (Optional) The source video (original video) for visualization purposes. It can be 480 x 832 or 720 x 1280.
Details of point-track files (.npy files)
The .npy files are saved as Python dictionaries. While the generated video might be 720P, point-tracks are typically defined at a resolution of 480 x 832 for efficiency, as specified by H and W.
| Key | Type | Shape | Description |
|---|---|---|---|
uvz | np.float32 | $(F, N, 3)$ | $(w_i, h_i, -1)$. The pixel coordinates for $N$ tracks over $F$ frames. |
vis | np.bool | $(F, N)$ | Visibility mask. True if visible; False if occluded or unknown. |
F | int | Scalar | Frame count (typically 49). For reference tracks, $F=1$. |
H | int | Scalar | Height of point-track resolution (typically 480). |
W | int | Scalar | Width of point-track resolution (typically 832). |
[!IMPORTANT]
- Track Consistency:
track.npyand all files inref/*.npymust contain the same number of point-tracks $N$. The $i$-th point-track in a reference must correspond to the $i$-th point-track in the generated frames.- Note on Indexing: In
ref/*.npyfiles, the shape ofuvzis $(1, N, 3)$ andvisis $(1, N)$ because the reference frame is treated as a single-frame video.- Handling Invisible Point-tracks: If a specific point-track is not visible in a particular reference/generated frame, simply set its corresponding
visvalue toFalse. In such cases, the values inuvzare ignored and can be arbitrary.
Here we explain how to create your own stylization examples.
For x86:
mkdir -p ./tmp_data
cd ./tmp_data
wget https://download.blender.org/release/Blender5.0/blender-5.0.0-linux-x64.tar.xz -O blender-5.0.0-linux-x64.tar.xz
tar -xJf blender-5.0.0-linux-x64.tar.xz
cd blender-5.0.0-linux-x64/
./5.0/python/bin/python3.11 -m pip install tqdm joblib
cd ../../
This installs Blender in the following directory structure:
./tmp_data/
blender-5.0.0-linux-x64/
blender
Verify the installation:
./tmp_data/blender-5.0.0-linux-x64/blender -b
.blend FileImport or create an animated mesh using Blender and save it as a .blend file.
Example:
./tmp_data/
./mesh_example/
mesh.blend
mesh1.abc (make sure to save all associated files)
mesh2.abc
We will now convert the .blend file into our inference format. In the following scripts, intermediate results are saved at ./tmp_data/blender_output.
cd ./tmp_data/blender-5.0.0-linux-x64
./blender -b ../mesh_example/mesh.blend -P ../../data_preprocess/preprocess_blender_part1.py
cd ../../
This saves the mesh output under ./tmp_data/blender_output.
./tmp_data/
./blender_output/
blender_results.pkl
final_video.mp4
frame_{idx}.png
python ./data_preprocess/preprocess_blender_part2.py ./tmp_data/blender_output
This saves additional information to ./tmp_data/blender_output.
./tmp_data/
./blender_output/
pred_track.pkl <- newly saved file
Here, --num_frames specifies the number of frames to extract (49 by default), and --max_tracks specifies the number of point-tracks to keep (15000 by default). These default values match the training data configurations. For longer videos, please subsample frames using the --stride flag.
Finally, --ref_idx specifies the index of the reference keyframes. For instance, --ref_idx="0,48" means we use two reference keyframes extracted from the 0th and 48th frames.
mkdir -p ./tmp_data/blender_unprocessed
mv ./tmp_data/blender_output ./tmp_data/blender_unprocessed/
python ./data_preprocess/preprocess_blender_part3.py \
--input_folder ./tmp_data/blender_unprocessed \
--output_folder ./tmp_data/blender_preprocessed \
--num_frames 49 \
--max_tracks 15000 \
--skip_exist 1 \
--stride 1 \
--ref_idx "0"
This creates the preprocessed Blender data at ./tmp_data/blender_preprocessed.
Folder Structure:
./tmp_data/
blender_preprocessed/
blender_{start_frame_idx}/
original.mp4 (<- Extracted video saved at the original resolution)
video720p.mp4 (<- Extracted video saved at 720x1280 resolution)
video.mp4 (<- Extracted video saved at 480x832 resolution)
track.npy (<- Storing point-track conditions)
ref/ (<- Storing reference images and point-track conditions)
0.png
0.npy
...
tmp_traj/
traj_ref0.gif (<- Visualization)
(Note: start_frame_idx indicates the starting frame index of the extracted video.)
Currently, ref/*.png stores the original frames. Please replace them with your customized stylized keyframes.
Our model also requires a text caption describing the video. Place this as a text file named stylized_prompt.txt inside each video example directory.
Final Inference-Ready Folder Structure:
./tmp_data/
blender_preprocessed/
blender_output_{start_frame_idx}/
original.mp4
video720p.mp4
video.mp4
track.npy
stylized_prompt.txt (<- Newly created)
ref/
0.png (<- Updated with stylized frame)
0.npy
You can now run inference as described in previous sections by adjusting the --path_to_dataset and --output_folder flags.
Inference with wan22 (480P) using 1 GPU:
python run_inference_dataset.py \
--config_path "./configs/480P.sh" \
--path_to_dataset ./tmp_data/blender_preprocessed \
--output_folder ./tmp_output/wan22_480P \
--fps 15 \
--skip_exist 1 \
--save_minimal 0 \
--max_trajectory_num 10000 \
--cfg_scale 3.0 \
--gpus 1
To perform video stylization, we need to estimate the underlying point-tracks. In this section, we demonstrate how to obtain point-tracks using DELTA and Video-Depth-Anything.
Download Checkpoints:
mkdir -p ./data_preprocess/delta/checkpoints/
pip install gdown
gdown --fuzzy https://drive.google.com/file/d/18d5M3nl3AxbG4ZkT7wssvMXZXbmXrnjz/view?usp=sharing -O ./data_preprocess/delta/checkpoints/
Build Environment (CUDA 11.8, x86):
conda create -n densetrack3d python=3.10 cmake=3.14.0 -y
conda activate densetrack3d
conda install pytorch torchvision pytorch-cuda=11.8 -c pytorch -c nvidia -y # use the correct version of cuda for your system
pip install pip==24.0 # downgrade pip to install pytorch_lightning==1.6.0
pip install -r ./data_preprocess/delta/requirements.txt # This may take some time
conda install ffmpeg -c conda-forge # to write .mp4 video
Install UniDepth (CUDA 11.8, x86):
pip install ninja
pip install "git+https://github.com/facebookresearch/pytorch3d.git@stable"
Download Checkpoints:
mkdir -p ./data_preprocess/Video-Depth-Anything/checkpoints
wget https://huggingface.co/depth-anything/Video-Depth-Anything-Large/resolve/main/video_depth_anything_vitl.pth?download=true -O ./data_preprocess/Video-Depth-Anything/checkpoints/video_depth_anything_vitl.pth
Build Environment (CUDA 11.8, x86):
conda create -n VideoDepthAnything python=3.10
conda activate VideoDepthAnything
pip install numpy==1.24.0
pip install torch==2.1.0 torchvision --index-url https://download.pytorch.org/whl/cu118
pip install -r ./data_preprocess/Video-Depth-Anything/requirements.txt
pip install xformers==0.0.22.post4 --index-url https://download.pytorch.org/whl/cu118
Please prepare your source video(s). Since our model is trained on 49 frames, the source videos must contain at least 49 frames. In this demo, we save all source videos under ./tmp_data/source_videos/.
./tmp_data/source_videos/
a.mp4
b.mp4
{video_name}.mp4
...
Run the following commands sequentially:
# 1. Extract frames from the source videos and save to a temporary directory.
# (Specify --stride > 1 if subsampling videos)
conda activate gwtt
python ./data_preprocess/preprocess_video_part1.py --input_folder "./tmp_data/source_videos/" --output_folder "./tmp_data/tmp_videos/" --stride 1 --min_frames 49 --max_frames 49
# 2. Compute relative video depth (required for DELTA tracking)
conda activate VideoDepthAnything
CUDA_VISIBLE_DEVICES=0 python ./data_preprocess/preprocess_video_part2.py --input_folder "./tmp_data/tmp_videos/" --input_video "./video.mp4" --encoder vitl
# 3. Compute metric depth via UniDepth2 (required for DELTA)
conda activate densetrack3d
CUDA_VISIBLE_DEVICES=0 python ./data_preprocess/preprocess_video_part3.py --input_folder "./tmp_data/tmp_videos/" --input_video "./video.mp4" --input_depth "./video_depth_anything_disp.npy"
# 4. Run DELTA for point tracking
# --max_limit indicates the maximum number of point-tracks to estimate (default is 3000 for efficiency).
# We use the iterative query-point strategy (--iter 5) introduced in our paper to uniformly sample point-tracks.
# Lower --iter for speed, or increase it for more accurate uniform point-track coverage.
conda activate densetrack3d
CUDA_VISIBLE_DEVICES=0 python ./data_preprocess/preprocess_video_part4.py --input_folder "./tmp_data/tmp_videos/" --input_video "./video.mp4" --input_depth "./unidepth2_metric_depth.npy" --max_limit 3000 --iter 5
Current Folder Structure:
./tmp_data/
tmp_videos/
{video_name}/
video.mp4 (<- 480 x 832)
video720p.mp4 (<- 720 x 1280)
original.mp4 (original resolution)
track.npy (<- estimated track file)
...
disparity_VideoDepthAnything.mp4 (<- visualization of relative disparity video depth)
unidepth2_aligned_depth.mp4 (<- visualization of estimated metric video depth)
traj.gif (<- visualization of estimated point-tracks)
Given the estimated point-tracks and source video, convert them into the inference format:
conda activate gwtt
python ./data_preprocess/preprocess_video_part5.py --input_folder "./tmp_data/tmp_videos" --output_folder "./tmp_data/video_processed" --ref_idx "0,48"
(Note: --ref_idx="0,48" means we use keyframes extracted from the 0th and 48th frames. --ref_idx="0" means we exclusively use the first frame).
Current Folder Structure:
./tmp_data/
video_processed/
{video_name}/
video720p.mp4
video.mp4
track.npy
ref/
0.png (<- updated)
0.npy
...
Just like the mesh stylization steps, replace the original frames in ref/*.png with your stylized frames and create a stylized_prompt.txt file for each video directory.
Final inference-ready folder structure:
./tmp_data/
video_processed/
{video_name}/
video720p.mp4
video.mp4
track.npy
stylized_prompt.txt (<- newly created)
ref/
0.png (<- updated)
0.npy
You can now run inference by pointing --path_to_dataset to ./tmp_data/video_processed.
Inference with wan22 (480P) using 1 GPU:
python run_inference_dataset.py --config_path "./configs/480P.sh" --path_to_dataset ./tmp_data/video_processed --output_folder ./output/test/wan22_480P --fps 15 --skip_exist 1 --save_minimal 0 --max_trajectory_num 10000 --cfg_scale 3.0 --gpus 1
We would like to thank Pablo Salamanca, Simon Su, and Nick Abrahan for their technical support; Nhat Phong Tran and Mingming He for their production support; Jeffrey Shapiro, Ritwik Kumar, and Hossein Taghavi for their executive support; Jennifer Lao and Lianette Alnaber for their operational support. Koichi acknowledges support from the Clarendon Scholarship.
This codebase is partially built on Wan2.2, DiffSynth-Studio, DELTA, Video-Depth-Anything, CoTracker, and Diffusion as Shader. We thank the authors for their open-source contributions.
If you find our paper and code useful, please cite us:
@inproceedings{namekata2026gowiththetrack,
author = {Namekata, Koichi and Kant, Yash and Liu, Zhizheng and Burgert, Ryan D. and Xu, Yuancheng and Lin, Kuan Heng and Steven, Emmett and Philip, Julien and Ma, Li and Vedaldi, Andrea and Debevec, Paul and Yu, Ning},
title = {Go-with-the-Track: Video Compositing and Motion Control with Point Tracking},
year = {2026},
isbn = {979-8-4007-2554-8/2026/07},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
url = {https://doi.org/10.1145/3799902.3811093},
doi = {10.1145/3799902.3811093},
booktitle = {Special Interest Group on Computer Graphics and Interactive Techniques Conference Conference Papers (SIGGRAPH Conference Papers '26), July 19--23, 2026, Los Angeles, CA, USA},
series = {SIGGRAPH Conference Papers '26}
}
2 commits
1 commits
Jupyter Notebook
77.0%
Python
21.1%
TypeScript
1.3%
Koichi Namekata1,2,3 · Yash Kant1,2† · Zhizheng Liu1,4 · Ryan D Burgert1,2,5 · Yuancheng Xu1,2 . Kuan Heng Lin1,6 · Emmett Steven2 . Julien Philip1 . Li Ma1 . Andrea Vedaldi3 . Paul Debevec1,2 . Ning Yu1,2†
1Eyeline Labs · 2Netflix · 3University of Oxford · 4University of California, Los Angeles · 5Stony Brook University · 6Columbia University
†Equal supervision.
K.N., Z.L., R.B., and K.H.L. performed this work during an internship at Netflix and Eyeline Labs.
This is the official implementation of Go-with-the-Track: Video Compositing and Motion Control with Point Tracking.
Go-with-the-Track is a video generation framework that unifies spatio-temporal motion control and reference image insertion via point-tracks. While conventional point-tracks are defined as the 2D flow of a point strictly within the generated video sequence, we extend this definition by anchoring the point-tracks to the reference images, explicitly establishing point correspondences between the generated video frames and the reference content. Treating these correspondences as integral to point-track conditioning, Go-with-the-Track enables fine-grained compositing and motion control throughout the video.
The code has been tested on Ubuntu 22.04.5 LTS.
Make sure to run the following commands in a GPU-enabled environment.
For CUDA 11.8 (x86):
conda create -n gwtt python=3.10.*
conda activate gwtt
python -m pip install -e . --no-build-isolation
Download the checkpoints from Eyeline-Labs/Go-with-the-Track and place them under ./checkpoints/.
For example:
pip install -U "huggingface_hub"
#set up cache directory
export SCRATCHDIR="/path/to/your/scratchdir" # <--- CHANGE THIS to your scratch directory
export HF_HOME=$SCRATCHDIR/hf_home
export HF_HUB_CACHE=$HF_HOME/hub
mkdir -p "$HF_HUB_CACHE"
#download checkpoints
mkdir -p "$SCRATCHDIR/checkpoints/Go-with-the-Track"
HF_XET_HIGH_PERFORMANCE=1 hf download Eyeline-Labs/Go-with-the-Track \
--repo-type model \
--local-dir "$SCRATCHDIR/checkpoints/Go-with-the-Track"
ln -s "$SCRATCHDIR/checkpoints/Go-with-the-Track" checkpoints
Expected Folder Structure:
./checkpoints
./ckpt_wan
./Wan-AI/
./Wan2.2-T2V-A14B/
high_noise_model/
low_noise_model/
./480P
low.safetensors
high.safetensors
./720P
low.safetensors
high.safetensors
Download the evaluation dataset from Eyeline-Labs/Go-with-the-Track and save it as eval_data.
For example:
# Set up cache directory
export HF_HOME=$SCRATCHDIR/hf_home
export HF_HUB_CACHE=$HF_HOME/hub
mkdir -p "$HF_HUB_CACHE"
# Download dataset
mkdir -p "$SCRATCHDIR/eval_data/Go-with-the-Track"
HF_XET_HIGH_PERFORMANCE=1 hf download Eyeline-Labs/Go-with-the-Track \
--repo-type dataset \
--local-dir "$SCRATCHDIR/eval_data/Go-with-the-Track"
ln -s "$SCRATCHDIR/eval_data/Go-with-the-Track" eval_data
Expected Folder Structure:
./eval_data/
{Video name 1}/
stylized_prompt.txt
track.npy
video.mp4 (<- strictly 49 frames; 480P or 720P)
video720.mp4 (<- strictly 49 frames; guaranteed 720P)
ref/
0.png (up to 720P)
0.npy
1.png
1.npy
...
3.png
3.npy
{Video name 2}/
...
By default, the model is trained to generate 49 frames at 480P resolution, with the number of reference frames ranging between 1 and 4. The following demonstrates how to run inference on the evaluation dataset downloaded above.
Using 1 GPU:
python run_inference_dataset.py \
--config_path "./configs/480P.sh" \
--path_to_dataset ./eval_data \
--output_folder ./tmp_output/wan22_480P \
--fps 15 \
--skip_exist 1 \
--save_minimal 0 \
--max_trajectory_num 10000 \
--cfg_scale 3.0 \
--gpus 1
This variant uses the same base model, further fine-tuned on the 720P dataset for 4,000 iterations starting from the 480P checkpoint.
Using 4 GPUs:
python run_inference_dataset.py \
--config_path "./configs/720P.sh" \
--path_to_dataset ./eval_data \
--output_folder ./tmp_output/wan22_720P \
--fps 15 \
--skip_exist 1 \
--save_minimal 0 \
--max_trajectory_num 10000 \
--cfg_scale 3.0 \
--gpus 4
After running the inference commands above, the results will be stored in your output directory:
{output_folder}/
{video_name}.mp4 <- Generated video (MP4 format)
{video_name}.gif <- Generated video (GIF format)
Visualize/
{video_name}/
combined.gif <- Visualization of point-track conditions, reference images, and source videos (if available)
Go-with-the-Track generates video by establishing direct, point-based correspondences between Reference Frames and generated videos. To achieve this, the model requires a specific directory structure and data format for inputting reference images and the point-track conditions for both reference frames and generated sequences.
eval_data/
{Example 1}/
ref/
{reference_id 1}.png
{reference_id 1}.npy
{reference_id 2}.png
{reference_id 2}.npy
...
track.npy
stylized_prompt.txt
(Optional) video.mp4
{Example 2}/
...
Explanation
Each video example must be contained in its own folder within the dataset directory.
track.npy: The point-track conditions for generated frames.
ref/: A folder containing 1 to 4 reference images (.png) and their corresponding reference point-tracks (.npy).
reference_id: This can be any arbitrary integer, but both the reference image and its corresponding reference point-track must share the same ID (e.g., 100.png and 100.npy).video.mp4: (Optional) The source video (original video) for visualization purposes. It can be 480 x 832 or 720 x 1280.
Details of point-track files (.npy files)
The .npy files are saved as Python dictionaries. While the generated video might be 720P, point-tracks are typically defined at a resolution of 480 x 832 for efficiency, as specified by H and W.
| Key | Type | Shape | Description |
|---|---|---|---|
uvz | np.float32 | $(F, N, 3)$ | $(w_i, h_i, -1)$. The pixel coordinates for $N$ tracks over $F$ frames. |
vis | np.bool | $(F, N)$ | Visibility mask. True if visible; False if occluded or unknown. |
F | int | Scalar | Frame count (typically 49). For reference tracks, $F=1$. |
H | int | Scalar | Height of point-track resolution (typically 480). |
W | int | Scalar | Width of point-track resolution (typically 832). |
[!IMPORTANT]
- Track Consistency:
track.npyand all files inref/*.npymust contain the same number of point-tracks $N$. The $i$-th point-track in a reference must correspond to the $i$-th point-track in the generated frames.- Note on Indexing: In
ref/*.npyfiles, the shape ofuvzis $(1, N, 3)$ andvisis $(1, N)$ because the reference frame is treated as a single-frame video.- Handling Invisible Point-tracks: If a specific point-track is not visible in a particular reference/generated frame, simply set its corresponding
visvalue toFalse. In such cases, the values inuvzare ignored and can be arbitrary.
Here we explain how to create your own stylization examples.
For x86:
mkdir -p ./tmp_data
cd ./tmp_data
wget https://download.blender.org/release/Blender5.0/blender-5.0.0-linux-x64.tar.xz -O blender-5.0.0-linux-x64.tar.xz
tar -xJf blender-5.0.0-linux-x64.tar.xz
cd blender-5.0.0-linux-x64/
./5.0/python/bin/python3.11 -m pip install tqdm joblib
cd ../../
This installs Blender in the following directory structure:
./tmp_data/
blender-5.0.0-linux-x64/
blender
Verify the installation:
./tmp_data/blender-5.0.0-linux-x64/blender -b
.blend FileImport or create an animated mesh using Blender and save it as a .blend file.
Example:
./tmp_data/
./mesh_example/
mesh.blend
mesh1.abc (make sure to save all associated files)
mesh2.abc
We will now convert the .blend file into our inference format. In the following scripts, intermediate results are saved at ./tmp_data/blender_output.
cd ./tmp_data/blender-5.0.0-linux-x64
./blender -b ../mesh_example/mesh.blend -P ../../data_preprocess/preprocess_blender_part1.py
cd ../../
This saves the mesh output under ./tmp_data/blender_output.
./tmp_data/
./blender_output/
blender_results.pkl
final_video.mp4
frame_{idx}.png
python ./data_preprocess/preprocess_blender_part2.py ./tmp_data/blender_output
This saves additional information to ./tmp_data/blender_output.
./tmp_data/
./blender_output/
pred_track.pkl <- newly saved file
Here, --num_frames specifies the number of frames to extract (49 by default), and --max_tracks specifies the number of point-tracks to keep (15000 by default). These default values match the training data configurations. For longer videos, please subsample frames using the --stride flag.
Finally, --ref_idx specifies the index of the reference keyframes. For instance, --ref_idx="0,48" means we use two reference keyframes extracted from the 0th and 48th frames.
mkdir -p ./tmp_data/blender_unprocessed
mv ./tmp_data/blender_output ./tmp_data/blender_unprocessed/
python ./data_preprocess/preprocess_blender_part3.py \
--input_folder ./tmp_data/blender_unprocessed \
--output_folder ./tmp_data/blender_preprocessed \
--num_frames 49 \
--max_tracks 15000 \
--skip_exist 1 \
--stride 1 \
--ref_idx "0"
This creates the preprocessed Blender data at ./tmp_data/blender_preprocessed.
Folder Structure:
./tmp_data/
blender_preprocessed/
blender_{start_frame_idx}/
original.mp4 (<- Extracted video saved at the original resolution)
video720p.mp4 (<- Extracted video saved at 720x1280 resolution)
video.mp4 (<- Extracted video saved at 480x832 resolution)
track.npy (<- Storing point-track conditions)
ref/ (<- Storing reference images and point-track conditions)
0.png
0.npy
...
tmp_traj/
traj_ref0.gif (<- Visualization)
(Note: start_frame_idx indicates the starting frame index of the extracted video.)
Currently, ref/*.png stores the original frames. Please replace them with your customized stylized keyframes.
Our model also requires a text caption describing the video. Place this as a text file named stylized_prompt.txt inside each video example directory.
Final Inference-Ready Folder Structure:
./tmp_data/
blender_preprocessed/
blender_output_{start_frame_idx}/
original.mp4
video720p.mp4
video.mp4
track.npy
stylized_prompt.txt (<- Newly created)
ref/
0.png (<- Updated with stylized frame)
0.npy
You can now run inference as described in previous sections by adjusting the --path_to_dataset and --output_folder flags.
Inference with wan22 (480P) using 1 GPU:
python run_inference_dataset.py \
--config_path "./configs/480P.sh" \
--path_to_dataset ./tmp_data/blender_preprocessed \
--output_folder ./tmp_output/wan22_480P \
--fps 15 \
--skip_exist 1 \
--save_minimal 0 \
--max_trajectory_num 10000 \
--cfg_scale 3.0 \
--gpus 1
To perform video stylization, we need to estimate the underlying point-tracks. In this section, we demonstrate how to obtain point-tracks using DELTA and Video-Depth-Anything.
Download Checkpoints:
mkdir -p ./data_preprocess/delta/checkpoints/
pip install gdown
gdown --fuzzy https://drive.google.com/file/d/18d5M3nl3AxbG4ZkT7wssvMXZXbmXrnjz/view?usp=sharing -O ./data_preprocess/delta/checkpoints/
Build Environment (CUDA 11.8, x86):
conda create -n densetrack3d python=3.10 cmake=3.14.0 -y
conda activate densetrack3d
conda install pytorch torchvision pytorch-cuda=11.8 -c pytorch -c nvidia -y # use the correct version of cuda for your system
pip install pip==24.0 # downgrade pip to install pytorch_lightning==1.6.0
pip install -r ./data_preprocess/delta/requirements.txt # This may take some time
conda install ffmpeg -c conda-forge # to write .mp4 video
Install UniDepth (CUDA 11.8, x86):
pip install ninja
pip install "git+https://github.com/facebookresearch/pytorch3d.git@stable"
Download Checkpoints:
mkdir -p ./data_preprocess/Video-Depth-Anything/checkpoints
wget https://huggingface.co/depth-anything/Video-Depth-Anything-Large/resolve/main/video_depth_anything_vitl.pth?download=true -O ./data_preprocess/Video-Depth-Anything/checkpoints/video_depth_anything_vitl.pth
Build Environment (CUDA 11.8, x86):
conda create -n VideoDepthAnything python=3.10
conda activate VideoDepthAnything
pip install numpy==1.24.0
pip install torch==2.1.0 torchvision --index-url https://download.pytorch.org/whl/cu118
pip install -r ./data_preprocess/Video-Depth-Anything/requirements.txt
pip install xformers==0.0.22.post4 --index-url https://download.pytorch.org/whl/cu118
Please prepare your source video(s). Since our model is trained on 49 frames, the source videos must contain at least 49 frames. In this demo, we save all source videos under ./tmp_data/source_videos/.
./tmp_data/source_videos/
a.mp4
b.mp4
{video_name}.mp4
...
Run the following commands sequentially:
# 1. Extract frames from the source videos and save to a temporary directory.
# (Specify --stride > 1 if subsampling videos)
conda activate gwtt
python ./data_preprocess/preprocess_video_part1.py --input_folder "./tmp_data/source_videos/" --output_folder "./tmp_data/tmp_videos/" --stride 1 --min_frames 49 --max_frames 49
# 2. Compute relative video depth (required for DELTA tracking)
conda activate VideoDepthAnything
CUDA_VISIBLE_DEVICES=0 python ./data_preprocess/preprocess_video_part2.py --input_folder "./tmp_data/tmp_videos/" --input_video "./video.mp4" --encoder vitl
# 3. Compute metric depth via UniDepth2 (required for DELTA)
conda activate densetrack3d
CUDA_VISIBLE_DEVICES=0 python ./data_preprocess/preprocess_video_part3.py --input_folder "./tmp_data/tmp_videos/" --input_video "./video.mp4" --input_depth "./video_depth_anything_disp.npy"
# 4. Run DELTA for point tracking
# --max_limit indicates the maximum number of point-tracks to estimate (default is 3000 for efficiency).
# We use the iterative query-point strategy (--iter 5) introduced in our paper to uniformly sample point-tracks.
# Lower --iter for speed, or increase it for more accurate uniform point-track coverage.
conda activate densetrack3d
CUDA_VISIBLE_DEVICES=0 python ./data_preprocess/preprocess_video_part4.py --input_folder "./tmp_data/tmp_videos/" --input_video "./video.mp4" --input_depth "./unidepth2_metric_depth.npy" --max_limit 3000 --iter 5
Current Folder Structure:
./tmp_data/
tmp_videos/
{video_name}/
video.mp4 (<- 480 x 832)
video720p.mp4 (<- 720 x 1280)
original.mp4 (original resolution)
track.npy (<- estimated track file)
...
disparity_VideoDepthAnything.mp4 (<- visualization of relative disparity video depth)
unidepth2_aligned_depth.mp4 (<- visualization of estimated metric video depth)
traj.gif (<- visualization of estimated point-tracks)
Given the estimated point-tracks and source video, convert them into the inference format:
conda activate gwtt
python ./data_preprocess/preprocess_video_part5.py --input_folder "./tmp_data/tmp_videos" --output_folder "./tmp_data/video_processed" --ref_idx "0,48"
(Note: --ref_idx="0,48" means we use keyframes extracted from the 0th and 48th frames. --ref_idx="0" means we exclusively use the first frame).
Current Folder Structure:
./tmp_data/
video_processed/
{video_name}/
video720p.mp4
video.mp4
track.npy
ref/
0.png (<- updated)
0.npy
...
Just like the mesh stylization steps, replace the original frames in ref/*.png with your stylized frames and create a stylized_prompt.txt file for each video directory.
Final inference-ready folder structure:
./tmp_data/
video_processed/
{video_name}/
video720p.mp4
video.mp4
track.npy
stylized_prompt.txt (<- newly created)
ref/
0.png (<- updated)
0.npy
You can now run inference by pointing --path_to_dataset to ./tmp_data/video_processed.
Inference with wan22 (480P) using 1 GPU:
python run_inference_dataset.py --config_path "./configs/480P.sh" --path_to_dataset ./tmp_data/video_processed --output_folder ./output/test/wan22_480P --fps 15 --skip_exist 1 --save_minimal 0 --max_trajectory_num 10000 --cfg_scale 3.0 --gpus 1
We would like to thank Pablo Salamanca, Simon Su, and Nick Abrahan for their technical support; Nhat Phong Tran and Mingming He for their production support; Jeffrey Shapiro, Ritwik Kumar, and Hossein Taghavi for their executive support; Jennifer Lao and Lianette Alnaber for their operational support. Koichi acknowledges support from the Clarendon Scholarship.
This codebase is partially built on Wan2.2, DiffSynth-Studio, DELTA, Video-Depth-Anything, CoTracker, and Diffusion as Shader. We thank the authors for their open-source contributions.
If you find our paper and code useful, please cite us:
@inproceedings{namekata2026gowiththetrack,
author = {Namekata, Koichi and Kant, Yash and Liu, Zhizheng and Burgert, Ryan D. and Xu, Yuancheng and Lin, Kuan Heng and Steven, Emmett and Philip, Julien and Ma, Li and Vedaldi, Andrea and Debevec, Paul and Yu, Ning},
title = {Go-with-the-Track: Video Compositing and Motion Control with Point Tracking},
year = {2026},
isbn = {979-8-4007-2554-8/2026/07},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
url = {https://doi.org/10.1145/3799902.3811093},
doi = {10.1145/3799902.3811093},
booktitle = {Special Interest Group on Computer Graphics and Interactive Techniques Conference Conference Papers (SIGGRAPH Conference Papers '26), July 19--23, 2026, Los Angeles, CA, USA},
series = {SIGGRAPH Conference Papers '26}
}
2 commits
1 commits
Jupyter Notebook
77.0%
Python
21.1%
TypeScript
1.3%