Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ uv sync --all-extras --group=cu130-train
source .venv/bin/activate && export LD_LIBRARY_PATH=
```

If you are starting from the recommended NGC image (`nvcr.io/nvidia/pytorch:25.09-py3`), see the [one-shot quickstart](./docs/setup.md#quickstart-from-the-recommended-base-image).
If you are starting from the recommended NGC image (`nvcr.io/nvidia/pytorch:26.06-py3`), see the [one-shot quickstart](./docs/setup.md#quickstart-from-the-recommended-base-image).

## Training

Expand Down
17 changes: 8 additions & 9 deletions cosmos_framework/inference/inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import cattrs.preconf.json
import safetensors.torch
import torch
import torchvision.io
from PIL import Image
from qwen_vl_utils.vision_process import smart_nframes
from torch.utils._pytree import tree_map_only
Expand Down Expand Up @@ -46,6 +45,7 @@
from cosmos_framework.inference.vision import (
build_conditioned_video_batch,
build_image_edit_batch,
decode_video_thwc_uint8,
load_conditioning_image,
load_conditioning_video,
load_prompt_upsampling_image,
Expand Down Expand Up @@ -574,17 +574,16 @@ def _get_prompt_sample_data(sample_args: OmniSampleArgs, model: OmniMoTModel, *,
def _decode_reasoner_video(vision_path: str, video_fps: float | None) -> dict[str, Any]:
"""Decode a local video file into the frame-list payload the Qwen3-VL processor expects.

Returns ``{"frames": [PIL.Image, ...], "fps": float}``. Uses the same
``torchvision.io.read_video`` decode the rest of the inference path relies on
(no ``decord`` dependency), then uniformly samples frames toward ``video_fps``
(default 2.0) via Qwen's ``smart_nframes``. The repo ``Qwen3VLProcessor`` runs
with ``do_sample_frames=False``, so it consumes this pre-sampled frame list
as-is and handles its own per-frame resize."""
frames, _, info = torchvision.io.read_video(str(vision_path), pts_unit="sec") # [T,H,W,C] uint8
Returns ``{"frames": [PIL.Image, ...], "fps": float}``. Uses the same TorchCodec
decode the rest of the inference path relies on (no ``decord`` dependency), then
uniformly samples frames toward ``video_fps`` (default 2.0) via Qwen's
``smart_nframes``. The repo ``Qwen3VLProcessor`` runs with ``do_sample_frames=False``,
so it consumes this pre-sampled frame list as-is and handles its own per-frame resize."""
frames, src_fps = decode_video_thwc_uint8(Path(vision_path)) # [T,H,W,C] uint8
total_frames = int(frames.shape[0])
if total_frames == 0:
raise ValueError(f"Decoded zero frames from reasoner video: {vision_path}")
src_fps = float(info.get("video_fps") or 0.0) or 1.0
src_fps = src_fps or 1.0
target_fps = video_fps if video_fps is not None else 2.0
nframes = smart_nframes({"fps": target_fps}, total_frames=total_frames, video_fps=src_fps)
idx = torch.linspace(0, total_frames - 1, nframes).round().long().tolist()
Expand Down
26 changes: 22 additions & 4 deletions cosmos_framework/inference/vision.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import numpy as np
import torch
import torchvision.io
import torchvision.transforms.functional as TF
from PIL import Image

Expand Down Expand Up @@ -76,6 +75,26 @@ def load_conditioning_image(image_path: Path, target_h: int, target_w: int) -> t
return img_tensor.unsqueeze(1) # [3,1,target_h,target_w]


def decode_video_thwc_uint8(path: Path) -> tuple[torch.Tensor, float]:
"""Read all frames of a video as a uint8 [T, H, W, C] tensor plus fps.

TorchCodec replacement for ``torchvision.io.read_video``; mirrors its frame layout so
callers are unchanged. As of torchvision 0.28 the public wheel no longer binds that
symbol — ``torchvision/io/__init__.py`` imports it from the Meta-internal
``pytorch.vision.fb.io.video`` under ``except ImportError: pass``, so ``import
torchvision.io`` still succeeds but calling ``read_video`` raises ``AttributeError``.
"""
from cosmos_framework.utils.generator.torchcodec_video import decode_frames_nhwc_uint8, probe_video

# torchcodec's core ops are CPU-only. Force CPU tensor creation so an active default-CUDA
# device context (torch.set_default_device during generation) doesn't route torchcodec's
# internal frame-index tensor to CUDA and raise NotImplementedError.
with torch.device("cpu"):
num_frames = probe_video(path).num_frames
frames_nhwc, meta = decode_frames_nhwc_uint8(path, list(range(num_frames)))
return torch.from_numpy(frames_nhwc), float(meta.average_fps)


def load_conditioning_video(
video_path: Path,
target_h: int,
Expand All @@ -88,7 +107,7 @@ def load_conditioning_video(

``keep`` selects which ``max_frames`` to take when the input is longer.
"""
frames, _, _ = torchvision.io.read_video(str(video_path), pts_unit="sec")
frames, _ = decode_video_thwc_uint8(video_path)
frames = frames[-max_frames:] if keep == "last" else frames[:max_frames] # [T,H,W,3]
frames_tchw = frames.permute(0, 3, 1, 2).float() # [T,3,H,W]
frames_resized = _resize_and_center_crop(frames_tchw, target_h, target_w) # [T,3,target_h,target_w]
Expand Down Expand Up @@ -185,9 +204,8 @@ def read_media_frames(path: Path, max_frames: int) -> tuple[torch.Tensor, float]
return frames, 1.0
if ext not in _VIDEO_EXTENSIONS:
raise ValueError(f"Unsupported media extension: {ext}")
frames, _, info = torchvision.io.read_video(str(path), pts_unit="sec")
frames, fps = decode_video_thwc_uint8(path)
frames = frames[:max_frames].permute(0, 3, 1, 2).permute(1, 0, 2, 3)
fps = float(info.get("video_fps", 24.0))
return frames, fps


Expand Down
4 changes: 2 additions & 2 deletions docs/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ ______________________________________________________________________
For CUDA 13 builds, the [NVIDIA NGC PyTorch container](https://catalog.ngc.nvidia.com/orgs/nvidia/containers/pytorch) is the recommended starting point — it bundles PyTorch + CUDA 13 + cuDNN + NCCL tuned for NVIDIA hardware, plus Apex, TransformerEngine, and Megatron utilities that training infra users commonly need.

```dockerfile
FROM nvcr.io/nvidia/pytorch:25.09-py3
FROM nvcr.io/nvidia/pytorch:26.06-py3
```

For CUDA 12.8 builds, pin to an earlier NGC tag (e.g. `nvcr.io/nvidia/pytorch:25.06-py3`) that still ships CUDA 12.
Expand All @@ -70,7 +70,7 @@ The two supported install paths are the recommended base image and the Docker co

### Quickstart: From the Recommended Base Image

If you started from the [recommended base image](#recommended-base-image) (`nvcr.io/nvidia/pytorch:25.09-py3`), the following commands set up the full environment in one go. Run them **from the root of this repository** (i.e. inside the `Cosmos/` directory you just cloned):
If you started from the [recommended base image](#recommended-base-image) (`nvcr.io/nvidia/pytorch:26.06-py3`), the following commands set up the full environment in one go. Run them **from the root of this repository** (i.e. inside the `Cosmos/` directory you just cloned):

```shell
apt-get update
Expand Down
43 changes: 43 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,46 @@ cu130-train = [
"transformer-engine==2.12.0+cu130.torch210",
"triton==3.6.0",
]
# torch 2.13 on CUDA 13.0. Same layout as cu130 but pinned to torch 2.13.
cu130-torch213 = [
#"flash-attn-3-nv==1.0.3+cu130.torch210; platform_machine == 'x86_64'", # TODO: torch213 build (cu130.torch210 wheels are cp313-only + torch210 ABI)
#"flash-attn==2.7.4.post1+cu130.torch210; platform_machine == 'x86_64'", # TODO: torch213 build (cu130.torch210 wheels are cp313-only + torch210 ABI)
# torch2.13 build (natten 0.21.6, sm 80/86/89/90/100/103/110/120/121) — matches this group's torch/ABI.
# Both arches now ship a torch213 wheel, so no platform split is needed.
"natten==0.21.6+cu130.torch213",
"torch==2.13.0+cu130",
"torchcodec==0.14.0+cu130", # https://github.com/meta-pytorch/torchcodec/releases
"torchvision==0.28.0+cu130", # TODO: release matching torch 2.13 — https://github.com/pytorch/vision/releases
# Dependencies determined from 'uv pip compile --group cu130-torch213' — these are the exact
# ==-pins that torch==2.13.0+cu130 (download.pytorch.org) declares. NOTE: this is the CUDA 13.0/13.1
# line the public wheel ships, NOT the CUDA 13.3 libs in nvcr.io/nvidia/pytorch:26.06.
# Issue: https://github.com/astral-sh/uv/issues/14237
"nvidia-cublas==13.1.1.3",
"nvidia-cuda-cupti==13.0.85",
"nvidia-cuda-nvrtc==13.0.88",
"nvidia-cuda-runtime==13.0.96",
# NOTE: torch 2.13.0+cu130 hard-pins this to ==9.20.0.48. The framework's cuDNN attention backend
# needs >= 9.22 (else it falls back to NATTEN, which is fine). To use the cuDNN backend, override at
# runtime: `uv pip install nvidia-cudnn-cu13==9.25.0.15` (can't pin here without a global override).
"nvidia-cudnn-cu13==9.20.0.48",
"nvidia-cufft==12.0.0.61",
"nvidia-cufile==1.15.1.6",
"nvidia-curand==10.4.0.35",
"nvidia-cusolver==12.0.4.66",
"nvidia-cusparse==12.6.3.3",
"nvidia-cusparselt-cu13==0.8.1",
"nvidia-nccl-cu13==2.29.7",
"nvidia-npp==13.0.0.50",
"nvidia-nvjitlink==13.3.33",
"nvidia-nvshmem-cu13==3.4.5",
"nvidia-nvtx==13.0.85",
]
cu130-torch213-train = [
{include-group = "cu130-torch213"},
"torchao==0.17.0+cu130; platform_machine == 'x86_64'", # 0.18.0+cu130 not published; no aarch64 build; https://github.com/pytorch/ao/issues/2919
"transformer-engine==2.12.0+cu130.torch210", # TODO: torch213 build
"triton==3.7.1", # torch==2.13.0+cu130 pins triton==3.7.1
]
# LIBERO simulator dependencies for the closed-loop eval client.
# Mirrors packages/cosmos-policy
# `libero` group; `libero` from PyPI declares `robosuite` transitively so we
Expand Down Expand Up @@ -274,10 +314,13 @@ repository = "https://github.com/NVIDIA/cosmos-framework"
required-version = ">=0.11.3"
conflicts = [
[
{group = "vllm"},
{group = "cu128"},
{group = "cu128-train"},
{group = "cu130"},
{group = "cu130-train"},
{group = "cu130-torch213"},
{group = "cu130-torch213-train"},
],
]
override-dependencies = [
Expand Down
Loading