alfredplpl's picture
Update app.py
51f18db verified
Raw
History Blame Contribute Delete
6.05 kB
# app.py
import uuid
import gc
from pathlib import Path
import numpy as np
import torch
import gradio as gr
import spaces
import os
from diffusers import (
WanImageToVideoPipeline,
WanTransformer3DModel,
FlowMatchEulerDiscreteScheduler,
AutoencoderKLWan,
)
from diffusers.utils import export_to_video
from copyright_classifier import contains_copyrighted_ip
BASE_MODEL_ID = "Wan-AI/Wan2.2-I2V-A14B-Diffusers"
I2V_TRANSFORMER_REPO = "aidealab/AnimeGen-I2V"
OUTPUT_DIR = Path("outputs")
OUTPUT_DIR.mkdir(exist_ok=True)
token = os.getenv("HF_TOKEN")
NG_WORDS=eval(os.getenv("NG_WORD"))
NG_WORDS.extend(eval(os.getenv("NG_WORD_JA")))
def clear_memory():
gc.collect()
if torch.cuda.is_available():
torch.cuda.empty_cache()
torch.cuda.ipc_collect()
def load_pipeline():
scheduler = FlowMatchEulerDiscreteScheduler(shift=3.0)
transformer_high = WanTransformer3DModel.from_pretrained(
I2V_TRANSFORMER_REPO,
subfolder="transformer",
torch_dtype=torch.bfloat16,
)
transformer_low = WanTransformer3DModel.from_pretrained(
I2V_TRANSFORMER_REPO,
subfolder="transformer_2",
torch_dtype=torch.bfloat16,
)
vae = AutoencoderKLWan.from_pretrained(
BASE_MODEL_ID,
subfolder="vae",
torch_dtype=torch.float32,
)
pipe = WanImageToVideoPipeline.from_pretrained(
BASE_MODEL_ID,
transformer=transformer_high,
transformer_2=transformer_low,
scheduler=scheduler,
vae=vae,
torch_dtype=torch.bfloat16,
)
pipe.load_lora_weights(
"lightx2v/Wan2.2-Lightning",
weight_name=(
"Wan2.2-I2V-A14B-4steps-lora-rank64-Seko-V1/"
"high_noise_model.safetensors"
),
adapter_name="high",
)
pipe.load_lora_weights(
"lightx2v/Wan2.2-Lightning",
weight_name=(
"Wan2.2-I2V-A14B-4steps-lora-rank64-Seko-V1/"
"low_noise_model.safetensors"
),
adapter_name="low",
load_into_transformer_2=True,
)
pipe.set_adapters(
["high", "low"],
adapter_weights=[1.0, 1.0],
)
transformer_high.enable_layerwise_casting(
storage_dtype=torch.float8_e4m3fn,
compute_dtype=torch.bfloat16,
)
transformer_low.enable_layerwise_casting(
storage_dtype=torch.float8_e4m3fn,
compute_dtype=torch.bfloat16,
)
pipe.enable_model_cpu_offload()
return pipe
def resize_first_and_last_images(first_image, last_image, max_area):
aspect_ratio = first_image.height / first_image.width
mod_value = pipe.vae_scale_factor_spatial * pipe.transformer.config.patch_size[1]
height = round(np.sqrt(max_area * aspect_ratio))
width = round(np.sqrt(max_area / aspect_ratio))
height = max(mod_value, height // mod_value * mod_value)
width = max(mod_value, width // mod_value * mod_value)
first_image = first_image.resize((width, height))
last_image = last_image.resize((width, height))
return first_image, last_image, width, height
# 起動時にロード
pipe = load_pipeline()
@spaces.GPU(duration=120)
def generate_video(
first_image,
last_image,
prompt,
negative_prompt,
):
if first_image is None:
raise gr.Error("Please upload the first image.")
if last_image is None:
raise gr.Error("Please upload the last image.")
clear_memory()
max_area = 832*480
first_image, last_image, width, height = resize_first_and_last_images(
first_image,
last_image,
max_area,
)
num_frames = int(16 * 3 + 1)
full_prompt = "Japanese anime style, " + prompt.strip()
# prompt filtering
print(NG_WORDS)
prompt_for_check = full_prompt.lower()
for word in NG_WORDS:
if word in prompt_for_check:
print(f"error: {word} .")
raise Exception()
# LLM filtering
result = contains_copyrighted_ip(full_prompt)
if(result):
print(f"error: {full_prompt} .")
raise Exception()
frames = pipe(
image=first_image,
last_image=last_image,
prompt=full_prompt,
negative_prompt=negative_prompt,
height=height,
width=width,
num_frames=num_frames,
guidance_scale=1.0,
num_inference_steps=4,
).frames[0]
output_path = OUTPUT_DIR / f"{uuid.uuid4().hex}.mp4"
export_to_video(frames, str(output_path), fps=16)
clear_memory()
return str(output_path)
default_prompt = "Make this character press both hands together in prayer and close their eyes."
default_negative_prompt = "3d, cg, photo, stop, wait"
with gr.Blocks(title="AnimeGen Frame Interpolation") as demo:
gr.Markdown("# AnimeGen Frame Interpolation")
gr.Markdown(
"Generate a video from a first frame and a last frame using AnimeGen I2V."
)
with gr.Row():
with gr.Column(scale=1):
with gr.Row():
first_image = gr.Image(
label="First image",
type="pil",
)
last_image = gr.Image(
label="Last image",
type="pil",
)
prompt = gr.Textbox(
label="Prompt",
value=default_prompt,
lines=4,
)
negative_prompt = gr.Textbox(
label="Negative prompt",
value=default_negative_prompt,
lines=2,
)
generate_button = gr.Button("Generate", variant="primary")
with gr.Column(scale=1):
video = gr.Video(label="Output")
generate_button.click(
fn=generate_video,
inputs=[
first_image,
last_image,
prompt,
negative_prompt,
],
outputs=[
video,
],
)
if __name__ == "__main__":
demo.queue(max_size=10).launch()