diff --git a/README.md b/README.md
index 881df4ed..03d3544f 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ https://github.com/user-attachments/assets/5fbc4596-56d6-43aa-98e0-da472cf8e26c
- 🤗 FastMochi | 🤗 FastHunyuan | 🔍 Discord
+ 🤗 FastMochi | 🤗 FastHunyuan | 🔍 Discord |
FastVideo currently offers: (with more to come)
diff --git a/cog.yaml b/cog.yaml
new file mode 100644
index 00000000..fe3ad816
--- /dev/null
+++ b/cog.yaml
@@ -0,0 +1,24 @@
+# Configuration for Cog ⚙️
+# Reference: https://cog.run/yaml
+
+build:
+ gpu: true
+ cuda: "12.1"
+ python_version: "3.10"
+ python_packages:
+ - "torch==2.4.0"
+ - "torchvision"
+ - "ninja==1.11.1.3"
+ - "transformers==4.46.1"
+ - "git+https://github.com/huggingface/diffusers.git@bf64b32652a63a1865a0528a73a13652b201698b"
+ - "accelerate==1.0.1"
+ - "safetensors==0.4.5"
+ - "peft==0.13.2"
+ - "packaging==24.2"
+ - "git+https://github.com/hao-ai-lab/FastVideo"
+
+ run:
+ - FLASH_ATTENTION_SKIP_CUDA_BUILD=TRUE pip install flash-attn --no-build-isolation
+ - curl -o /usr/local/bin/pget -L "https://github.com/replicate/pget/releases/latest/download/pget_$(uname -s)_$(uname -m)" && chmod +x /usr/local/bin/pget
+
+predict: "predict.py:Predictor"
diff --git a/predict.py b/predict.py
new file mode 100644
index 00000000..bbbf1c04
--- /dev/null
+++ b/predict.py
@@ -0,0 +1,130 @@
+# Prediction interface for Cog ⚙️
+# https://cog.run/python
+
+from cog import BasePredictor, Input, Path
+import os
+import time
+import torch
+import imageio
+import argparse
+import subprocess
+import torchvision
+import numpy as np
+from einops import rearrange
+
+MODEL_CACHE = 'FastHunyuan'
+os.environ['MODEL_BASE'] = './'+MODEL_CACHE
+from fastvideo.models.hunyuan.inference import HunyuanVideoSampler
+MODEL_URL = "https://weights.replicate.delivery/default/FastVideo/FastHunyuan/model.tar"
+
+def download_weights(url, dest):
+ start = time.time()
+ print("downloading url: ", url)
+ print("downloading to: ", dest)
+ subprocess.check_call(["pget", "-xf", url, dest], close_fds=False)
+ print("downloading took: ", time.time() - start)
+
+class Predictor(BasePredictor):
+ def setup(self):
+ """Load the model into memory"""
+ print("Model Base: " + os.environ['MODEL_BASE'])
+ # Download weights
+ if not os.path.exists(MODEL_CACHE):
+ download_weights(MODEL_URL, MODEL_CACHE)
+
+ self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
+ args = argparse.Namespace(
+ num_frames=125,
+ height=720,
+ width=1280,
+ num_inference_steps=6,
+ fps=24,
+ denoise_type='flow',
+ seed=1024,
+ neg_prompt=None,
+ guidance_scale=1.0,
+ embedded_cfg_scale=6.0,
+ flow_shift=17,
+ batch_size=1,
+ num_videos=1,
+ load_key='module',
+ use_cpu_offload=False,
+ dit_weight='FastHunyuan/hunyuan-video-t2v-720p/transformers/mp_rank_00_model_states.pt',
+ reproduce=True,
+ disable_autocast=False,
+ flow_reverse=True,
+ flow_solver='euler',
+ use_linear_quadratic_schedule=False,
+ linear_schedule_end=25,
+ model='HYVideo-T/2-cfgdistill',
+ latent_channels=16,
+ precision='bf16',
+ rope_theta=256,
+ vae='884-16c-hy',
+ vae_precision='fp16',
+ vae_tiling=True,
+ text_encoder='llm',
+ text_encoder_precision='fp16',
+ text_states_dim=4096,
+ text_len=256,
+ tokenizer='llm',
+ prompt_template='dit-llm-encode',
+ prompt_template_video='dit-llm-encode-video',
+ hidden_state_skip_layer=2,
+ apply_final_norm=False,
+ text_encoder_2='clipL',
+ text_encoder_precision_2='fp16',
+ text_states_dim_2=768,
+ tokenizer_2='clipL',
+ text_len_2=77,
+ model_path=MODEL_CACHE,
+ )
+ self.model = HunyuanVideoSampler.from_pretrained(MODEL_CACHE, args=args)
+
+ def predict(
+ self,
+ prompt: str = Input(description="Text prompt for video generation", default="A cat walks on the grass, realistic style."),
+ negative_prompt: str = Input(description="Text prompt to specify what you don't want in the video.", default=""),
+ width: int = Input(description="Width of output video", default=1280, ge=256),
+ height: int = Input(description="Height of output video", default=720, ge=256),
+ num_frames: int = Input(description="Number of frames to generate", default=125, ge=16),
+ num_inference_steps: int = Input(description="Number of denoising steps", default=6, ge=1, le=50),
+ guidance_scale: float = Input(description="Classifier free guidance scale", default=1.0, ge=0.1, le=10.0),
+ embedded_cfg_scale: float = Input(description="Embedded classifier free guidance scale", default=6.0, ge=0.1, le=10.0),
+ flow_shift: int = Input(description="Flow shift parameter", default=17, ge=1, le=20),
+ fps: int = Input(description="Frames per second of output video", default=24, ge=1, le=60),
+ seed: int = Input(description="0 for Random seed. Set for reproducible generation", default=0),
+ ) -> Path:
+ """Run video generation"""
+ if seed <=0:
+ seed = int.from_bytes(os.urandom(2), "big")
+ print(f"Using seed: {seed}")
+
+ outputs = self.model.predict(
+ prompt=prompt,
+ height=height,
+ width=width,
+ video_length=num_frames,
+ seed=seed,
+ negative_prompt=negative_prompt,
+ infer_steps=num_inference_steps,
+ guidance_scale=guidance_scale,
+ embedded_guidance_scale=embedded_cfg_scale,
+ flow_shift=flow_shift,
+ flow_reverse=True,
+ batch_size=1,
+ num_videos_per_prompt=1,
+ )
+
+ # Process output video
+ videos = rearrange(outputs["samples"], "b c t h w -> t b c h w")
+ frames = []
+ for x in videos:
+ x = torchvision.utils.make_grid(x, nrow=6)
+ x = x.transpose(0, 1).transpose(1, 2).squeeze(-1)
+ frames.append((x * 255).numpy().astype(np.uint8))
+
+ # Save video
+ output_path = Path("/tmp/output.mp4")
+ imageio.mimsave(str(output_path), frames, fps=fps)
+ return Path(output_path)