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.
2929from ...callbacks import MultiPipelineCallbacks , PipelineCallback
3030from ...image_processor import PipelineImageInput
3131from ...loaders import HunyuanVideoLoraLoaderMixin
32- from ...models import AutoencoderKLHunyuanVideo , HunyuanVideoTransformer3DModel
32+ from ...models import AutoencoderKLHunyuanVideo , HunyuanVideoFramepackTransformer3DModel
3333from ...schedulers import FlowMatchEulerDiscreteScheduler
3434from ...utils import is_torch_xla_available , logging , replace_example_docstring
3535from ...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