Skip to content
This repository was archived by the owner on May 20, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2fd5afe
first dit commit.
sajadn Oct 30, 2025
0bf48db
inference working with old checkpoint.
sajadn Oct 30, 2025
577650d
add on_validation_start step to generate video.
sajadn Oct 31, 2025
cdb8f0a
EDM loss bug fix.
sajadn Nov 3, 2025
62cc0bb
Refactor DIT components: add comments for potential removals and clar…
abhinavg4 Nov 3, 2025
e000093
Add utility functions for tensor operations and dynamic imports
sajadn Nov 4, 2025
5783bb9
remove unused layers from spec.
sajadn Nov 4, 2025
80c2887
Remove unused AdaLN and STDiTLayerWithAdaLN classes from dit_layer_sp…
sajadn Nov 4, 2025
1e0f737
Refactor DIT model files: removed unused functions from `dit_provider…
sajadn Nov 4, 2025
8a009a1
Add README for DiT model setup: include instructions for cloning repo…
sajadn Nov 4, 2025
fda47d0
Remove unused files and refactor DIT model components: deleted obsole…
sajadn Nov 5, 2025
719d9a9
Add new components for DiT model:
sajadn Nov 5, 2025
9eb992d
revert THD to SBHD and use remove prints.
sajadn Nov 5, 2025
203b4cb
add support sequence for sequence packing.
sajadn Nov 6, 2025
7b989c5
move files in proper location, uncomment cp related code, cleaning.
sajadn Nov 6, 2025
de3f968
fix lint errors.
sajadn Nov 6, 2025
2adc46d
fix README lint error.
sajadn Nov 6, 2025
afeb1d4
add the missing copyright.
sajadn Nov 6, 2025
ffa263c
removed dependency on nemo CausalVideoTokenizer by implementing a min…
sajadn Nov 6, 2025
e7bb8cb
remove nemo vae modules.
sajadn Nov 6, 2025
48e4e2e
update readme.md
sajadn Nov 7, 2025
da9895f
remove unused temporal_self_attention attribute from DiTWithAdaLNSubm…
sajadn Nov 7, 2025
0fd3a66
create an abstract DiffusionTaskEncoder to encompass the sequence pac…
sajadn Nov 7, 2025
06afc5e
fix use seq_length instead of packing_buffer_size to set max number o…
sajadn Nov 9, 2025
72970ac
Add the missing copy write. Minor update to the config.
sajadn Nov 10, 2025
0180c3e
lint still complains about the nemo_vfm file.
sajadn Nov 10, 2025
8c82f21
move data modules to common, move cosmos to common/tokenizer instad o…
sajadn Nov 10, 2025
5dd2f8c
uncomment omega config overwrite.
sajadn Nov 10, 2025
476e71a
resolve conflict for dit_debug merge with mainlie
sajadn Nov 10, 2025
80da225
change import (remote lint complains).
sajadn Nov 10, 2025
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
Empty file.
Empty file.
14 changes: 14 additions & 0 deletions dfm/src/common/tokenizers/cosmos/cosmos1/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
193 changes: 193 additions & 0 deletions dfm/src/common/tokenizers/cosmos/cosmos1/causal_video_tokenizer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure dfm/src/common is the right place to put these tokenizer files.
dfm/src/common should be for common files used by both AutoModel and Mcore paths.
Since only DiT model use these tokenizer files, maybe dfm/src/megatron/models/dit/tokenizers would be a better place?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is ok since Automodel might use it someday?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we replace cosmos to something else and I don't think we need cosmos/cosmos1

Let's do
dfm/src/common/tokenizers/causal_video_tokenizer.py

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i still like to keep it in cosmos, what if we want to incorporate additional video tokenizers?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not have any cosmos reference just coz cosmos is a seperate project now and we are not using it anymore. Anyways it's a P1

# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# pylint: disable=C0115,C0116,C0301

"""A library for Causal Video Tokenizer inference."""

from pathlib import Path

import numpy as np
import torch
from huggingface_hub import get_token as get_hf_token
from huggingface_hub import hf_hub_download
from tqdm import tqdm

from dfm.src.common.tokenizers.cosmos.cosmos1.video_tokenizer_utils import (
load_jit_model,
numpy2tensor,
pad_video_batch,
tensor2numpy,
unpad_video_batch,
)


class CausalVideoTokenizer(torch.nn.Module):
def __init__(
self,
checkpoint_dir: str = None,
load_full_model: bool = True,
load_dec_model: bool = True,
load_enc_model: bool = True,
device: str = "cuda",
dtype: str = "bfloat16",
) -> None:
super().__init__()

checkpoint = Path(checkpoint_dir)
self._full_model_path = str(checkpoint / "autoencoder.jit")
self._enc_model_path = str(checkpoint / "encoder.jit")
self._dec_model_path = str(checkpoint / "decoder.jit")
self._dtype = dtype
self._device = device

self._full_model = load_jit_model(self._full_model_path, self._device) if load_full_model else None
self._enc_model = load_jit_model(self._enc_model_path, self._device) if load_enc_model else None
self._dec_model = load_jit_model(self._dec_model_path, self._device) if load_dec_model else None

@classmethod
def from_pretrained(
cls,
tokenizer_type="Cosmos-Tokenizer-DV4x8x8",
load_encoder=True,
load_decoder=True,
load_full_model=False,
use_pytorch=False,
dtype="bfloat16",
):
cls._hf_model_name = f"nvidia/{tokenizer_type}"

# Requires setting HF_TOKEN env variable
hf_token = get_hf_token()

full_model_path = hf_hub_download(
repo_id=cls._hf_model_name,
filename="autoencoder.jit",
token=hf_token,
)

_ = hf_hub_download(
repo_id=cls._hf_model_name,
filename="encoder.jit",
token=hf_token,
)

_ = hf_hub_download(
repo_id=cls._hf_model_name,
filename="decoder.jit",
token=hf_token,
)

# No need to load in encoder and decoder with full model loaded
if load_full_model:
load_encoder = False
load_decoder = False

# Assumes HF downloads all files to same local dir
ckpt_dir = str(Path(full_model_path).parent)
args = {
"checkpoint_dir": ckpt_dir,
"dtype": dtype,
"load_enc_model": load_encoder,
"load_dec_model": load_decoder,
"load_full_model": load_full_model,
}
return cls(**args)

@torch.no_grad()
def autoencode(self, input_tensor: torch.Tensor) -> torch.Tensor:
"""Reconstrcuts a batch of video tensors after embedding into a latent.

Args:
video: The input video Bx3xTxHxW layout, range [-1..1].
Returns:
The reconstructed video, layout Bx3xTxHxW, range [-1..1].
"""
if self._full_model is not None:
output_tensor = self._full_model(input_tensor)
output_tensor = output_tensor[0] if isinstance(output_tensor, tuple) else output_tensor
else:
output_latent = self.encode(input_tensor)[0]
output_tensor = self.decode(output_latent)
return output_tensor

@torch.no_grad()
def encode(self, input_tensor: torch.Tensor) -> tuple[torch.Tensor]:
"""Encodes a numpy video into a CausalVideo latent or code.

Args:
input_tensor: The input tensor Bx3xTxHxW layout, range [-1..1].
Returns:
For causal continuous video (CV) tokenizer, the tuple contains:
- The latent embedding, Bx16x(t)x(h)x(w), where the compression
rate is (T/t x H/h x W/w), and channel dimension of 16.
For causal discrete video (DV) tokenizer, the tuple contains:
1) The indices, Bx(t)x(h)x(w), from a codebook of size 64K, which
is formed by FSQ levels of (8,8,8,5,5,5).
2) The discrete code, Bx6x(t)x(h)x(w), where the compression rate
is again (T/t x H/h x W/w), and channel dimension of 6.
"""
assert input_tensor.ndim == 5, "input video should be of 5D."

output_latent = self._enc_model(input_tensor)
if isinstance(output_latent, torch.Tensor):
return output_latent
return output_latent[:-1]

@torch.no_grad()
def decode(self, input_latent: torch.Tensor) -> torch.Tensor:
"""Encodes a numpy video into a CausalVideo latent.

Args:
input_latent: The continuous latent Bx16xtxhxw for CV,
or the discrete indices Bxtxhxw for DV.
Returns:
The reconstructed tensor, layout [B,3,1+(T-1)*8,H*16,W*16] in range [-1..1].
"""
assert input_latent.ndim >= 4, "input latent should be of 5D for continuous and 4D for discrete."
return self._dec_model(input_latent)

def forward(
self,
video: np.ndarray,
temporal_window: int = 17,
) -> np.ndarray:
"""Reconstructs video using a pre-trained CausalTokenizer autoencoder.
Given a video of arbitrary length, the forward invokes the CausalVideoTokenizer
in a sliding manner with a `temporal_window` size.

Args:
video: The input video BxTxHxWx3 layout, range [0..255].
temporal_window: The length of the temporal window to process, default=25.
Returns:
The reconstructed video in range [0..255], layout BxTxHxWx3.
"""
assert video.ndim == 5, "input video should be of 5D."
num_frames = video.shape[1] # can be of any length.
output_video_list = []
for idx in tqdm(range(0, (num_frames - 1) // temporal_window + 1)):
# Input video for the current window.
start, end = idx * temporal_window, (idx + 1) * temporal_window
input_video = video[:, start:end, ...]

# Spatio-temporally pad input_video so it's evenly divisible.
padded_input_video, crop_region = pad_video_batch(input_video)
input_tensor = numpy2tensor(padded_input_video, dtype=self._dtype, device=self._device)
output_tensor = self.autoencode(input_tensor)
padded_output_video = tensor2numpy(output_tensor)
output_video = unpad_video_batch(padded_output_video, crop_region)

output_video_list.append(output_video)
return np.concatenate(output_video_list, axis=1)
145 changes: 145 additions & 0 deletions dfm/src/common/tokenizers/cosmos/cosmos1/video_tokenizer_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just utils but under tokenizers maybe?

# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Utility functions for the inference libraries."""

import numpy as np
import torch


_DTYPE, _DEVICE = torch.bfloat16, "cuda"
_UINT8_MAX_F = float(torch.iinfo(torch.uint8).max)
_SPATIAL_ALIGN = 16
_TEMPORAL_ALIGN = 8


def load_jit_model(jit_filepath: str = None, device: str = "cuda") -> torch.jit.ScriptModule:
"""Loads a torch.jit.ScriptModule from a filepath.

Args:
jit_filepath: The filepath to the JIT-compiled model.
device: The device to load the model onto, default=cuda.
Returns:
The JIT compiled model loaded to device and on eval mode.
"""
model = torch.jit.load(jit_filepath, map_location=device)
return model.eval().to(device)


def numpy2tensor(
input_image: np.ndarray,
dtype: torch.dtype = _DTYPE,
device: str = _DEVICE,
range_min: int = -1,
) -> torch.Tensor:
"""Converts image(dtype=np.uint8) to `dtype` in range [0..255].

Args:
input_image: A batch of images in range [0..255], BxHxWx3 layout.
Returns:
A torch.Tensor of layout Bx3xHxW in range [-1..1], dtype.
"""
ndim = input_image.ndim
indices = list(range(1, ndim))[-1:] + list(range(1, ndim))[:-1]
image = input_image.transpose((0,) + tuple(indices)) / _UINT8_MAX_F
if range_min == -1:
image = 2.0 * image - 1.0
return torch.from_numpy(image).to(dtype).to(device)


def tensor2numpy(input_tensor: torch.Tensor, range_min: int = -1) -> np.ndarray:
"""Converts tensor in [-1,1] to image(dtype=np.uint8) in range [0..255].

Args:
input_tensor: Input image tensor of Bx3xHxW layout, range [-1..1].
Returns:
A numpy image of layout BxHxWx3, range [0..255], uint8 dtype.
"""
if range_min == -1:
input_tensor = (input_tensor.float() + 1.0) / 2.0
ndim = input_tensor.ndim
output_image = input_tensor.clamp(0, 1).cpu().numpy()
output_image = output_image.transpose((0,) + tuple(range(2, ndim)) + (1,))
return (output_image * _UINT8_MAX_F + 0.5).astype(np.uint8)


def pad_video_batch(
batch: np.ndarray,
temporal_align: int = _TEMPORAL_ALIGN,
spatial_align: int = _SPATIAL_ALIGN,
) -> tuple[np.ndarray, list[int]]:
"""Pads a batch of videos to be divisible by `temporal_align` or `spatial_align`.

Zero pad spatially. Reflection pad temporally to handle causality better.
Args:
batch: The batch of videos to pad., layout BxFxHxWx3, in any range.
align: The alignment to pad to.
Returns:
The padded batch and the crop region.
"""
num_frames, height, width = batch.shape[-4:-1]
align = spatial_align
height_to_pad = (align - height % align) if height % align != 0 else 0
width_to_pad = (align - width % align) if width % align != 0 else 0

align = temporal_align
frames_to_pad = (align - (num_frames - 1) % align) if (num_frames - 1) % align != 0 else 0

crop_region = [
frames_to_pad >> 1,
height_to_pad >> 1,
width_to_pad >> 1,
num_frames + (frames_to_pad >> 1),
height + (height_to_pad >> 1),
width + (width_to_pad >> 1),
]
batch = np.pad(
batch,
(
(0, 0),
(0, 0),
(height_to_pad >> 1, height_to_pad - (height_to_pad >> 1)),
(width_to_pad >> 1, width_to_pad - (width_to_pad >> 1)),
(0, 0),
),
mode="constant",
)
batch = np.pad(
batch,
(
(0, 0),
(frames_to_pad >> 1, frames_to_pad - (frames_to_pad >> 1)),
(0, 0),
(0, 0),
(0, 0),
),
mode="edge",
)
return batch, crop_region


def unpad_video_batch(batch: np.ndarray, crop_region: list[int]) -> np.ndarray:
"""Unpads video with `crop_region`.

Args:
batch: A batch of numpy videos, layout BxFxHxWxC.
crop_region: [f1,y1,x1,f2,y2,x2] first, top, left, last, bot, right crop indices.

Returns:
np.ndarray: Cropped numpy video, layout BxFxHxWxC.
"""
assert len(crop_region) == 6, "crop_region should be len of 6."
f1, y1, x1, f2, y2, x2 = crop_region
return batch[..., f1:f2, y1:y2, x1:x2, :]
Empty file.
Loading