Skip to content

Commit e2fde45

Browse files
committed
fixes
1 parent 5a523cd commit e2fde45

5 files changed

Lines changed: 52 additions & 42 deletions

File tree

src/diffusers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@
377377
"HunyuanDiTPAGPipeline",
378378
"HunyuanDiTPipeline",
379379
"HunyuanSkyreelsImageToVideoPipeline",
380+
"HunyuanVideoFramepackPipeline",
380381
"HunyuanVideoImageToVideoPipeline",
381382
"HunyuanVideoPipeline",
382383
"I2VGenXLPipeline",
@@ -952,6 +953,7 @@
952953
HunyuanDiTPAGPipeline,
953954
HunyuanDiTPipeline,
954955
HunyuanSkyreelsImageToVideoPipeline,
956+
HunyuanVideoFramepackPipeline,
955957
HunyuanVideoImageToVideoPipeline,
956958
HunyuanVideoPipeline,
957959
I2VGenXLPipeline,

src/diffusers/models/transformers/transformer_hunyuan_video_framepack.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ def forward(
207207
encoder_attention_mask: torch.Tensor,
208208
pooled_projections: torch.Tensor,
209209
image_embeds: torch.Tensor,
210-
latent_indices: torch.Tensor,
210+
indices_latents: torch.Tensor,
211211
guidance: Optional[torch.Tensor] = None,
212212
latents_clean: Optional[torch.Tensor] = None,
213213
indices_latents_clean: Optional[torch.Tensor] = None,
@@ -241,11 +241,11 @@ def forward(
241241

242242
hidden_states, image_rotary_emb = self._pack_history_states(
243243
hidden_states,
244-
latent_indices,
244+
indices_latents,
245245
latents_clean,
246-
indices_latents_clean,
247246
latents_history_2x,
248247
indices_latents_history_2x,
248+
indices_latents_clean,
249249
latents_history_4x,
250250
indices_latents_history_4x,
251251
)
@@ -318,7 +318,7 @@ def forward(
318318
def _pack_history_states(
319319
self,
320320
hidden_states: torch.Tensor,
321-
latent_indices: torch.Tensor,
321+
indices_latents: torch.Tensor,
322322
latents_clean: Optional[torch.Tensor] = None,
323323
latents_history_2x: Optional[torch.Tensor] = None,
324324
latents_history_4x: Optional[torch.Tensor] = None,
@@ -327,13 +327,13 @@ def _pack_history_states(
327327
indices_latents_history_4x: Optional[torch.Tensor] = None,
328328
):
329329
batch_size, num_channels, num_frames, height, width = hidden_states.shape
330-
if latent_indices is None:
331-
latent_indices = torch.arange(0, num_frames).unsqueeze(0).expand(batch_size, -1)
330+
if indices_latents is None:
331+
indices_latents = torch.arange(0, num_frames).unsqueeze(0).expand(batch_size, -1)
332332

333333
hidden_states = self.x_embedder(hidden_states)
334334
hidden_states = hidden_states.flatten(2).transpose(1, 2)
335335
image_rotary_emb = self.rope(
336-
frame_indices=latent_indices, height=height, width=width, device=hidden_states.device
336+
frame_indices=indices_latents, height=height, width=width, device=hidden_states.device
337337
)
338338

339339
latents_clean, latents_history_2x, latents_history_4x = self.clean_x_embedder(

src/diffusers/pipelines/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@
227227
"HunyuanVideoPipeline",
228228
"HunyuanSkyreelsImageToVideoPipeline",
229229
"HunyuanVideoImageToVideoPipeline",
230+
"HunyuanVideoFramepackPipeline",
230231
]
231232
_import_structure["kandinsky"] = [
232233
"KandinskyCombinedPipeline",
@@ -589,6 +590,7 @@
589590
from .hidream_image import HiDreamImagePipeline
590591
from .hunyuan_video import (
591592
HunyuanSkyreelsImageToVideoPipeline,
593+
HunyuanVideoFramepackPipeline,
592594
HunyuanVideoImageToVideoPipeline,
593595
HunyuanVideoPipeline,
594596
)

src/diffusers/pipelines/hunyuan_video/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
else:
2525
_import_structure["pipeline_hunyuan_skyreels_image2video"] = ["HunyuanSkyreelsImageToVideoPipeline"]
2626
_import_structure["pipeline_hunyuan_video"] = ["HunyuanVideoPipeline"]
27+
_import_structure["pipeline_hunyuan_video_framepack"] = ["HunyuanVideoFramepackPipeline"]
2728
_import_structure["pipeline_hunyuan_video_image2video"] = ["HunyuanVideoImageToVideoPipeline"]
2829

2930
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
@@ -36,6 +37,7 @@
3637
else:
3738
from .pipeline_hunyuan_skyreels_image2video import HunyuanSkyreelsImageToVideoPipeline
3839
from .pipeline_hunyuan_video import HunyuanVideoPipeline
40+
from .pipeline_hunyuan_video_framepack import HunyuanVideoFramepackPipeline
3941
from .pipeline_hunyuan_video_image2video import HunyuanVideoImageToVideoPipeline
4042

4143
else:

src/diffusers/pipelines/hunyuan_video/pipeline_hunyuan_video_framepack.py

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2024 The HunyuanVideo Team and The HuggingFace Team. All rights reserved.
1+
# Copyright 2025 The Framepack Team, The HunyuanVideo Team and The HuggingFace Team. All rights reserved.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -29,7 +29,7 @@
2929
from ...callbacks import MultiPipelineCallbacks, PipelineCallback
3030
from ...image_processor import PipelineImageInput
3131
from ...loaders import HunyuanVideoLoraLoaderMixin
32-
from ...models import AutoencoderKLHunyuanVideo, HunyuanVideoTransformer3DModel
32+
from ...models import AutoencoderKLHunyuanVideo, HunyuanVideoFramepackTransformer3DModel
3333
from ...schedulers import FlowMatchEulerDiscreteScheduler
3434
from ...utils import is_torch_xla_available, logging, replace_example_docstring
3535
from ...utils.torch_utils import randn_tensor
@@ -182,7 +182,7 @@ def __init__(
182182
self,
183183
text_encoder: LlamaModel,
184184
tokenizer: LlamaTokenizerFast,
185-
transformer: HunyuanVideoTransformer3DModel,
185+
transformer: HunyuanVideoFramepackTransformer3DModel,
186186
vae: AutoencoderKLHunyuanVideo,
187187
scheduler: FlowMatchEulerDiscreteScheduler,
188188
text_encoder_2: CLIPTextModel,
@@ -353,13 +353,14 @@ def encode_prompt(
353353
return prompt_embeds, pooled_prompt_embeds, prompt_attention_mask
354354

355355
def encode_image(
356-
self, image: PipelineImageInput, device: Optional[torch.device] = None, dtype: Optional[torch.dtype] = None
356+
self, image: torch.Tensor, device: Optional[torch.device] = None, dtype: Optional[torch.dtype] = None
357357
):
358-
device = device or self.image_encoder.device
359-
dtype = dtype or self.image_encoder.dtype
360-
image = self.image_processor(images=image, return_tensors="pt").to(device=device, dtype=dtype)
358+
image = (image + 1) / 2.0 # [-1, 1] -> [0, 1]
359+
image = self.feature_extractor(images=image, return_tensors="pt").to(
360+
device=self.image_encoder.device, dtype=self.image_encoder.dtype
361+
)
361362
image_embeds = self.image_encoder(**image).last_hidden_state
362-
return image_embeds
363+
return image_embeds.to(dtype=dtype)
363364

364365
def check_inputs(
365366
self,
@@ -445,12 +446,11 @@ def prepare_image_latents(
445446
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
446447
latents: Optional[torch.Tensor] = None,
447448
) -> torch.Tensor:
448-
if latents is not None:
449-
return latents.to(device=device, dtype=dtype)
450-
image = image.to(device=self.vae.device, dtype=self.vae.dtype)
451-
latents = self.vae.encode(image).latent_dist.sample(generator=generator)
452-
latents = latents * self.vae.config.scaling_factor
453-
return latents
449+
if latents is None:
450+
image = image.unsqueeze(2).to(device=self.vae.device, dtype=self.vae.dtype)
451+
latents = self.vae.encode(image).latent_dist.sample(generator=generator)
452+
latents = latents * self.vae.config.scaling_factor
453+
return latents.to(device=device, dtype=dtype)
454454

455455
def enable_vae_slicing(self):
456456
r"""
@@ -703,7 +703,7 @@ def __call__(
703703

704704
# 4. Prepare image
705705
image = self.video_processor.preprocess(image, height, width)
706-
image_embeds = self.encode_image(image, device=device)
706+
image_embeds = self.encode_image(image, device=device).to(transformer_dtype)
707707

708708
# 4. Prepare timesteps
709709
sigmas = np.linspace(1.0, 0.0, num_inference_steps + 1)[:-1] if sigmas is None else sigmas
@@ -715,25 +715,28 @@ def __call__(
715715
num_latent_sections = max(1, (num_frames + window_size - 1) // window_size)
716716
# Specific to the released checkpoint: https://huggingface.co/lllyasviel/FramePackI2V_HY
717717
# TODO: find a more generic way in future if there are more checkpoints
718-
history_sizes = [1, 9, 16]
718+
history_sizes = [1, 2, 16]
719719
history_latents = torch.zeros(
720720
batch_size,
721721
num_channels_latents,
722722
sum(history_sizes),
723723
height // self.vae_scale_factor_spatial,
724724
width // self.vae_scale_factor_spatial,
725+
device=device,
725726
dtype=torch.float32,
726727
)
727728
history_video = None
728729

729-
image_latents = self.prepare_image_latentss(image, generator=generator, latents=image_latents)
730+
image_latents = self.prepare_image_latents(
731+
image, dtype=torch.float32, device=device, generator=generator, latents=image_latents
732+
)
730733

731734
latent_paddings = list(reversed(range(num_latent_sections)))
732735
if num_latent_sections > 4:
733736
latent_paddings = [3] + [2] * (num_latent_sections - 3) + [1, 0]
734737

735738
# 6. Prepare guidance condition
736-
guidance = torch.tensor([guidance_scale] * latents.shape[0], dtype=transformer_dtype, device=device) * 1000.0
739+
guidance = torch.tensor([guidance_scale] * batch_size, dtype=transformer_dtype, device=device) * 1000.0
737740

738741
# 7. Denoising loop
739742
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
@@ -749,15 +752,15 @@ def __call__(
749752
(
750753
indices_prefix,
751754
indices_padding,
752-
indices_latents_clean,
755+
indices_latents,
753756
indices_postfix,
754757
indices_latents_history_2x,
755758
indices_latents_history_4x,
756759
) = indices.split([1, latent_padding_size, latent_window_size, *history_sizes], dim=1)
757760
# Inverted anti-drifting sampling: Figure 2(c) in the paper
758761
indices_clean_latents = torch.cat([indices_prefix, indices_postfix], dim=1)
759762

760-
latents_prefix = image_latents.to(device=device, dtype=transformer_dtype)
763+
latents_prefix = image_latents
761764
latents_postfix, latents_history_2x, latents_history_4x = history_latents[
762765
:, :, : sum(history_sizes)
763766
].split(history_sizes, dim=2)
@@ -781,50 +784,50 @@ def __call__(
781784
continue
782785

783786
self._current_timestep = t
784-
latent_model_input = latents.to(transformer_dtype)
785-
# broadcast to batch dimension in a way that's compatible with ONNX/Core ML
786-
timestep = t.expand(latents.shape[0]).to(latents.dtype)
787+
timestep = t.expand(latents.shape[0])
787788

788789
noise_pred = self.transformer(
789-
hidden_states=latent_model_input,
790+
hidden_states=latents.to(transformer_dtype),
790791
timestep=timestep,
791792
encoder_hidden_states=prompt_embeds,
792793
encoder_attention_mask=prompt_attention_mask,
793794
pooled_projections=pooled_prompt_embeds,
794-
guidance=guidance,
795795
image_embeds=image_embeds,
796-
latents_clean=latents_clean,
796+
indices_latents=indices_latents,
797+
guidance=guidance,
798+
latents_clean=latents_clean.to(transformer_dtype),
797799
indices_latents_clean=indices_clean_latents,
798-
latents_history_2x=latents_history_2x,
800+
latents_history_2x=latents_history_2x.to(transformer_dtype),
799801
indices_latents_history_2x=indices_latents_history_2x,
800-
latents_history_4x=latents_history_4x,
802+
latents_history_4x=latents_history_4x.to(transformer_dtype),
801803
indices_latents_history_4x=indices_latents_history_4x,
802804
attention_kwargs=attention_kwargs,
803805
return_dict=False,
804806
)[0]
805807

806808
if do_true_cfg:
807809
neg_noise_pred = self.transformer(
808-
hidden_states=latent_model_input,
810+
hidden_states=latents.to(transformer_dtype),
809811
timestep=timestep,
810812
encoder_hidden_states=negative_prompt_embeds,
811813
encoder_attention_mask=negative_prompt_attention_mask,
812814
pooled_projections=negative_pooled_prompt_embeds,
813815
image_embeds=image_embeds,
814-
latents_clean=latents_clean,
816+
indices_latents=indices_latents,
817+
guidance=guidance,
818+
latents_clean=latents_clean.to(transformer_dtype),
815819
indices_latents_clean=indices_clean_latents,
816-
latents_history_2x=latents_history_2x,
820+
latents_history_2x=latents_history_2x.to(transformer_dtype),
817821
indices_latents_history_2x=indices_latents_history_2x,
818-
latents_history_4x=latents_history_4x,
822+
latents_history_4x=latents_history_4x.to(transformer_dtype),
819823
indices_latents_history_4x=indices_latents_history_4x,
820-
guidance=guidance,
821824
attention_kwargs=attention_kwargs,
822825
return_dict=False,
823826
)[0]
824827
noise_pred = neg_noise_pred + true_cfg_scale * (noise_pred - neg_noise_pred)
825828

826829
# compute the previous noisy sample x_t -> x_t-1
827-
latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
830+
latents = self.scheduler.step(noise_pred.float(), t, latents, return_dict=False)[0]
828831

829832
if callback_on_step_end is not None:
830833
callback_kwargs = {}
@@ -846,7 +849,7 @@ def __call__(
846849
latents = torch.cat([latents_prefix, latents])
847850

848851
total_generated_latent_frames += latents.shape[2]
849-
history_latents = torch.cat([latents.to(history_latents), history_latents], dim=2)
852+
history_latents = torch.cat([latents, history_latents], dim=2)
850853

851854
real_history_latents = history_latents[:, :, :total_generated_latent_frames]
852855

@@ -874,6 +877,7 @@ def __call__(
874877
self._current_timestep = None
875878

876879
if not output_type == "latent":
880+
history_video = history_video[:, :, :num_frames]
877881
video = self.video_processor.postprocess_video(history_video, output_type=output_type)
878882
else:
879883
video = history_video

0 commit comments

Comments
 (0)