Official codebase for the paper "PhysTrace: Interpretable Temporal Evaluation of Physical Consistency in Video Generative Models".
This repository provides the evaluation code for PhysTrace, which localizes when a video breaks physical consistency instead of returning a single video-level score.
- Sliding-window scoring: each video is split into overlapping windows, scored by a video-text alignment model (PE-AV-Large by default) and a reasoning MLLM (Qwen3-VL-8B-Thinking by default).
- IntPhys2-Timing: temporal violation annotations (onset / end) on top of IntPhys2, covering the four physical attributes
solidity,permanence,immutability, andcontinuity.
project_root/
├── AlignmentModels/ # video-text alignment / MLLM code (submodules) and weights
├── DiffusionModels/ # video generative model code (submodules) and weights
├── IntPhys2Timing/ # violation GT CSVs and per-attribute prompts
├── PhysTrace/ # core library (windowing, CLIP / MLLM scorers)
├── demo/ # interactive web demo
├── score/ # batch inference and scoring
├── requirements.txt
└── README.md
demo/app.py: interactive demo (browser UI) for inspecting per-window resultsscore/run_test.py: batch inference over every IntPhys2 pairscore/run_eval.py: scoring of the inference results against the IntPhys2-Timing GT
Tested on NVIDIA A100 with nvidia/cuda:12.8.1-cudnn-devel-ubuntu22.04.
pip3 install -r requirements.txtgit clone --recursive <REPO_URL>
# or, after a plain clone
git submodule update --init --remote --recursiveOr clone the external repositories manually:
# Alignment Scoring Models
git clone https://github.com/facebookresearch/perception_models.git AlignmentModels/PE_AV_large
# Diffusion models (only for Section 4: Evaluation for Video Generative Models)
git clone https://github.com/Wan-Video/Wan2.1.git DiffusionModels/Wan2.1
git clone https://github.com/nvidia-cosmos/cosmos-predict2.git DiffusionModels/cosmos-predict2hf auth loginAlignment Scoring Models
hf download facebook/pe-av-large --revision perception_models --local-dir AlignmentModels/weights/PE-AV-LargeMLLMs
hf download Qwen/Qwen3-VL-8B-Thinking --local-dir AlignmentModels/weights/Qwen3-VL-8B-ThinkingDiffusion models (only for 4. Evaluation for Video Generative Models)
hf download Wan-AI/Wan2.1-T2V-1.3B --revision 37ec512624d61f7aa208f7ea8140a131f93afc9a \
--local-dir DiffusionModels/weights/wan/Wan2.1-T2V-1.3B
hf download nvidia/Cosmos-Predict2-2B-Video2World --revision f50c09f5d8ab133a90cac3f4886a6471e9ba3f18 \
--local-dir DiffusionModels/weights/cosmos/nvidia/Cosmos-Predict2-2B-Video2World \
--include config.json model_index.json model-480p-16fps.pt "scheduler/*" "text_encoder/*" "tokenizer/*" "vae/*"
# Cosmos-Predict2 loads T5 with from_pretrained(<dir>), so the tokenizer must sit beside the text encoder weights
M=DiffusionModels/weights/cosmos/nvidia/Cosmos-Predict2-2B-Video2World
cp $M/tokenizer/{special_tokens_map.json,spiece.model,tokenizer.json,tokenizer_config.json} $M/text_encoder/Optional Models
# ---- Scorer : internvideo2-stage2-6b ----
# internvideo2-stage2
hf download OpenGVLab/InternVideo2-Stage2_6B-224p-f4 internvideo2-s2_6b-224p-f4_with_audio_encoder.pt \
--local-dir AlignmentModels/weights/InternVideo2-Stage2
# bert tokenizer
hf download google-bert/bert-large-uncased vocab.txt tokenizer.json tokenizer_config.json config.json \
--local-dir AlignmentModels/weights/InternVideo2-Stage2/bert-large-uncased
# ---- Scorer : internvideo2-clip-6b ----
# internvideo2-stage2 (same as above; skip if already downloaded)
hf download OpenGVLab/InternVideo2-Stage2_6B-224p-f4 internvideo2-s2_6b-224p-f4_with_audio_encoder.pt \
--local-dir AlignmentModels/weights/InternVideo2-Stage2
# InternVL-C 13B (25GB)
hf download OpenGVLab/InternVL internvl_c_13b_224px.pth \
--local-dir AlignmentModels/weights/InternVideo2-CLIP
# CLIP delta
hf download OpenGVLab/InternVideo2-CLIP-6B-224p-f8 6B_clip.pth \
--local-dir AlignmentModels/weights/InternVideo2-CLIP
# LLaMA tokenizer
hf download hfl/chinese-alpaca-lora-7b tokenizer.model tokenizer_config.json special_tokens_map.json \
--local-dir AlignmentModels/weights/InternVideo2-CLIP/tokenizer
# ---- MLLM : Qwen3-VL-32B-Thinking ----
hf download Qwen/Qwen3-VL-32B-Thinking --local-dir AlignmentModels/weights/Qwen3-VL-32B-Thinking
# ---- MLLM : Cosmos-Reason2-8B ----
hf download nvidia/Cosmos-Reason2-8B --local-dir AlignmentModels/weights/Cosmos-Reason2-8Bpython3 -c "from huggingface_hub import snapshot_download; \
snapshot_download(repo_id='facebook/IntPhys2', repo_type='dataset', local_dir='/Path/To/IntPhys2')"CUDA_VISIBLE_DEVICES=0 python3 demo/app.py \
--intphys2-dir /Path/To/IntPhys2 \
--result-dir results/demo \
--port 1535--intphys2-dir: IntPhys2 root (usesMain/VideosandMain/metadata.csv; the CLIP cacheMain/videos-npzis created beside them)--result-dir: whereresults.csv/trials.csvare written--gt-csv: violation GT drawn on the UI chart (defaults toIntPhys2Timing/intphys2_gt.csv)--mllm-backend:qwen3-vl-thinking(default),qwen3-vl-32b-thinking,cosmos-reason2--clip-model:pe-av-large(default),internvideo2-stage2-6b,internvideo2-clip-6b
CUDA_VISIBLE_DEVICES=0 python3 score/run_test.py \
--intphys2-dir /Path/To/IntPhys2 \
--result-dir results/test \
--window-seconds 2 --hop-seconds 1 \
--clip-frames 60 --mllm-frames 60 \
--categories solidity=BoxSoldity--intphys2-dir,--result-dir: same as the demo (same result format, so the result directory can be opened in the demo)--window-seconds,--hop-seconds: window length and hop between windows, in seconds--clip-frames,--mllm-frames: frames fed to CLIP / the MLLM per window--categories: limit the categories run per attribute, e.g.solidity=BoxSoldity(default: every category)--mllm-backend:qwen3-vl-thinking(default),qwen3-vl-32b-thinking,cosmos-reason2--clip-model:pe-av-large(default),internvideo2-stage2-6b,internvideo2-clip-6b
python3 score/run_eval.py --result-dir results/test--result-dir: result directory of the inference above--strict-edges: a band that only touches a GT edge is not counted as a hit (optional)--out: output file stem (default:<result-dir>/eval_gt)
Note
score/run_test.pyis resumable. Re-running the same command skips pairs that are already stored; use--statusto print progress and--forceto re-analyze.
Each IntPhys2 video is inverted (clean → noise) by a video generative model, and the x0 estimate decoded at each sigma is evaluated with the same pipeline as Section 3. Wan2.1 and Cosmos-Predict2 are run separately, so give each model its own output directory.
Wan2.1-T2V-1.3B
CUDA_VISIBLE_DEVICES=0 python3 PhysTrace/prob_wan.py \
-i /Path/To/IntPhys2 \
-o results/wan \
--sigmas 0.5,0.8 \
--stride 1 \
--chunked \
--overlap 0Cosmos-Predict2-2B
CUDA_VISIBLE_DEVICES=0 python3 PhysTrace/prob_cosmos.py \
-i /Path/To/IntPhys2 \
-o results/cosmos \
--sigmas 0.5,0.8 \
--stride 1 \
--chunked \
--overlap 0-i: IntPhys2 root (same as--intphys2-dirabove), a folder of.mp4files, or a single video-o: output directory; onesigma_<value>/Main/Videosfolder (withmetadata.csv) is written per sigma, plussigma_0.00_INPUTfor the input frames--sigmas: comma-separated sigmas (= flow time) at which the x0 estimate is decoded--stride: use every N-th source frame (1= all frames at the original 60 fps,8= 7.5 fps)--chunked: split the video into fixed-length chunks (Wan: 81 frames, Cosmos: 93 frames) and invert each chunk independently, then cross-fade the chunks back together. Without this flag, the whole video is inverted jointly (MultiDiffusion over latent windows, overlap set by--latent_overlap)--overlap(with--chunked): number of pixel frames shared by consecutive chunks, linearly cross-faded in the shared region (default10;0= chunks are placed back-to-back with no blending)--gt-csv: generate only the pairs listed in this GT CSV (defaults toIntPhys2Timing/intphys2_gt.csv)
Pass one sigma folder of the generation output as --intphys2-dir.
Wan2.1-T2V-1.3B
CUDA_VISIBLE_DEVICES=0 python3 score/run_test.py \
--intphys2-dir results/wan/sigma_0.50 \
--result-dir results/wan_test/sigma_0.50 \
--window-seconds 2 --hop-seconds 1 \
--clip-frames 16 --mllm-frames 16 \
--categories solidity=BoxSoldityCosmos-Predict2-2B
CUDA_VISIBLE_DEVICES=0 python3 score/run_test.py \
--intphys2-dir results/cosmos/sigma_0.50 \
--result-dir results/cosmos_test/sigma_0.50 \
--window-seconds 2 --hop-seconds 1 \
--clip-frames 18 --mllm-frames 18 \
--categories solidity=BoxSoldity--intphys2-dir: asigma_<value>folder written by the generation step--clip-frames,--mllm-frames: lower than in Section 3, since the generated videos have a lower frame rate (Wan: 7.5 fps, Cosmos: 93 frames per clip)- The other options are the same as 3. b) Inference
python3 score/run_eval.py --result-dir results/wan_test/sigma_0.50
python3 score/run_eval.py --result-dir results/cosmos_test/sigma_0.50--result-dir: result directory of the inference above (one per model and sigma)
Note
To compare sigmas, repeat b) and c) for eachsigma_<value>folder, includingsigma_0.00_INPUTas the reference.
If you find this repository useful, please cite the paper.
@article{yoo2026phystrace,
title={{PhysTrace}: Interpretable Temporal Evaluation of Physical Consistency in Video Generative Models},
author={Yoo, Taekoan and Jung, Wonkyung and Kim, Kyunghun and Kong, Kyeongbo},
journal={arXiv preprint arXiv:XXXX.XXXXX},
year={2026},
url={TODO}
}