rahul7star commited on
Commit
bdfc92b
·
verified ·
1 Parent(s): 7901d19

Create app_14B.py

Browse files
Files changed (1) hide show
  1. app_14B.py +154 -0
app_14B.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import uuid
3
+ import torch
4
+ import logging
5
+ import tempfile
6
+ import numpy as np
7
+ import gradio as gr
8
+ from datetime import datetime
9
+ from diffusers import WanImageToVideoPipeline
10
+ from diffusers.utils import export_to_video
11
+ from huggingface_hub import upload_file
12
+ from PIL import Image
13
+
14
+ # ----------------- Setup -----------------
15
+ logging.basicConfig(level=logging.INFO)
16
+
17
+ HF_MODEL = "rahul7star/rahulAI" # 👈 change to your repo
18
+ dtype = torch.bfloat16
19
+ device = "cuda"
20
+
21
+ model_id = "FastDM/Wan2.2-I2V-A14B-Merge-Lightning-V1.0-Diffusers"
22
+ pipe = WanImageToVideoPipeline.from_pretrained(model_id, torch_dtype=dtype)
23
+ pipe.to(device)
24
+
25
+ default_negative_prompt = (
26
+ "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,"
27
+ "JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,"
28
+ "手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走"
29
+ )
30
+
31
+ # ----------------- Upload helper -----------------
32
+ def upscale_and_upload_4k(input_video_path: str, input_image, summary_text: str) -> str:
33
+ """
34
+ Upload video (4K), input image, and summary text to HF.
35
+ """
36
+ logging.info(f"Upscaling video to 4K for upload: {input_video_path}")
37
+
38
+ # Upscale video
39
+ with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmp_upscaled:
40
+ upscaled_path = tmp_upscaled.name
41
+
42
+ cmd = [
43
+ "ffmpeg", "-i", input_video_path,
44
+ "-vf", "scale=3840:2160:flags=lanczos",
45
+ "-c:v", "libx264", "-crf", "18", "-preset", "slow", "-y", upscaled_path,
46
+ ]
47
+ os.system(" ".join(cmd)) # safer: subprocess.run, but HF Spaces sometimes picky
48
+
49
+ # Create HF folder
50
+ today_str = datetime.now().strftime("%Y-%m-%d")
51
+ unique_subfolder = f"upload_{uuid.uuid4().hex[:8]}"
52
+ hf_folder = f"{today_str}-WAN-I2V/{unique_subfolder}"
53
+
54
+ # Upload video
55
+ video_filename = os.path.basename(input_video_path)
56
+ video_hf_path = f"{hf_folder}/{video_filename}"
57
+ upload_file(upscaled_path, video_hf_path, repo_id=HF_MODEL, repo_type="model",
58
+ token=os.environ.get("HUGGINGFACE_HUB_TOKEN"))
59
+
60
+ # Upload image
61
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_img:
62
+ if isinstance(input_image, str):
63
+ import shutil
64
+ shutil.copy(input_image, tmp_img.name)
65
+ else:
66
+ input_image.save(tmp_img.name, format="PNG")
67
+ tmp_img_path = tmp_img.name
68
+
69
+ image_hf_path = f"{hf_folder}/input_image.png"
70
+ upload_file(tmp_img_path, image_hf_path, repo_id=HF_MODEL, repo_type="model",
71
+ token=os.environ.get("HUGGINGFACE_HUB_TOKEN"))
72
+
73
+ # Upload summary
74
+ summary_file = tempfile.NamedTemporaryFile(delete=False, suffix=".txt").name
75
+ with open(summary_file, "w", encoding="utf-8") as f:
76
+ f.write(summary_text)
77
+
78
+ summary_hf_path = f"{hf_folder}/summary.txt"
79
+ upload_file(summary_file, summary_hf_path, repo_id=HF_MODEL, repo_type="model",
80
+ token=os.environ.get("HUGGINGFACE_HUB_TOKEN"))
81
+
82
+ # Cleanup
83
+ os.remove(upscaled_path)
84
+ os.remove(tmp_img_path)
85
+ os.remove(summary_file)
86
+
87
+ return hf_folder
88
+
89
+ # ----------------- Video generation -----------------
90
+ def generate_video(input_image, prompt, negative_prompt=default_negative_prompt,
91
+ duration_seconds=2, guidance_scale=3.5, steps=40, seed=0):
92
+ if input_image is None:
93
+ return None, "Please upload an image!"
94
+
95
+ # Ensure divisible by patch size
96
+ max_area = 480 * 832
97
+ aspect_ratio = input_image.height / input_image.width
98
+ mod_value = pipe.vae_scale_factor_spatial * pipe.transformer.config.patch_size[1]
99
+ height = round(np.sqrt(max_area * aspect_ratio)) // mod_value * mod_value
100
+ width = round(np.sqrt(max_area / aspect_ratio)) // mod_value * mod_value
101
+ input_image = input_image.resize((width, height))
102
+
103
+ generator = torch.Generator(device=device).manual_seed(int(seed))
104
+
105
+ with torch.inference_mode():
106
+ output_frames_list = pipe(
107
+ image=input_image,
108
+ prompt=prompt,
109
+ negative_prompt=negative_prompt,
110
+ height=height,
111
+ width=width,
112
+ num_frames=int(duration_seconds * 16), # 16 fps
113
+ guidance_scale=float(guidance_scale),
114
+ num_inference_steps=int(steps),
115
+ generator=generator,
116
+ ).frames[0]
117
+
118
+ # Save temp video
119
+ with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmpfile:
120
+ video_path = tmpfile.name
121
+ export_to_video(output_frames_list, video_path, fps=16)
122
+
123
+ # Upload to HF
124
+ hf_folder = upscale_and_upload_4k(video_path, input_image, prompt)
125
+
126
+ return video_path, f"✅ Uploaded to HF: {hf_folder}"
127
+
128
+ # ----------------- Gradio UI -----------------
129
+ with gr.Blocks() as demo:
130
+ gr.Markdown("# 🖼️➡️🎥 Image to Video with Wan 2.2 I2V (14B Lightning)")
131
+
132
+ with gr.Row():
133
+ with gr.Column():
134
+ input_image = gr.Image(type="pil", label="Upload an Image")
135
+ prompt = gr.Textbox(lines=4, label="Prompt")
136
+ negative_prompt = gr.Textbox(value=default_negative_prompt, lines=3, label="Negative Prompt")
137
+ duration = gr.Slider(1, 4, value=2, step=1, label="Duration (seconds)")
138
+ guidance_scale = gr.Slider(0, 10, value=3.5, step=0.5, label="Guidance Scale")
139
+ steps = gr.Slider(10, 50, value=40, step=1, label="Inference Steps")
140
+ seed = gr.Number(value=0, precision=0, label="Seed")
141
+ generate_btn = gr.Button("🚀 Generate Video")
142
+
143
+ with gr.Column():
144
+ output_video = gr.Video(label="Generated Video")
145
+ upload_status = gr.Textbox(label="Upload Status", interactive=False)
146
+
147
+ generate_btn.click(
148
+ generate_video,
149
+ inputs=[input_image, prompt, negative_prompt, duration, guidance_scale, steps, seed],
150
+ outputs=[output_video, upload_status],
151
+ )
152
+
153
+ if __name__ == "__main__":
154
+ demo.launch()