matthewkram commited on
Commit
3bb6d5d
·
verified ·
1 Parent(s): 0ae0e8a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +54 -44
app.py CHANGED
@@ -12,6 +12,14 @@ import cv2
12
  import subprocess
13
  import tempfile
14
 
 
 
 
 
 
 
 
 
15
  class WanAnimateApp:
16
  def __init__(self):
17
  model_name = "stabilityai/stable-video-diffusion-img2vid-xt"
@@ -22,24 +30,45 @@ class WanAnimateApp:
22
  device_map="cpu"
23
  )
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  def predict(
26
  self,
27
- ref_img,
28
- video,
29
  model_id,
30
  model,
31
  ):
32
- if ref_img is None or video is None:
33
  return None, "Upload both image and video."
34
 
35
  try:
36
- # Local processing
37
- if isinstance(ref_img, Image.Image):
38
- ref_image = ref_img.convert("RGB").resize((576, 320))
39
- else:
40
- ref_image = Image.open(ref_img).convert("RGB").resize((576, 320))
41
-
42
- cap = cv2.VideoCapture(video)
43
  frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
44
  cap.release()
45
  motion_hint = f" with dynamic motion from {frame_count} frames"
@@ -68,7 +97,7 @@ class WanAnimateApp:
68
  temp_dir = tempfile.mkdtemp()
69
  for i, frame in enumerate(output):
70
  frame.save(f"{temp_dir}/frame_{i:04d}.png")
71
- temp_video = f"/tmp/output_{uuid.uuid4()}.mp4"
72
  subprocess.run([
73
  'ffmpeg', '-y', '-framerate', '7', '-i', f"{temp_dir}/frame_%04d.png",
74
  '-c:v', 'libx264', '-pix_fmt', 'yuv420p', temp_video
@@ -111,68 +140,49 @@ def start_app():
111
 
112
  gr.HTML("""
113
  <details>
114
- <summary>‼️Usage (使用说明)</summary>
115
- Wan-Animate supports two modes:
116
- <ul>
117
- <li>Move Mode: animate the character in input image with movements from the input video</li>
118
- <li>Mix Mode: replace the character in input video with the character in input image</li>
119
- </ul>
120
- Wan-Animate supports two modes:
121
- <ul>
122
- <li>Move Mode: Use the movements extracted from the input video to drive the character in the input image</li>
123
- <li>Mix Mode: Use the character in the input image to replace the character in the input video</li>
124
- </ul>
125
- Currently, the following restrictions apply to inputs:
126
- <ul>
127
- <li>Video file size: Less than 200MB</li>
128
- <li>Video resolution: The shorter side must be greater than 200, and the longer side must be less than 2048</li>
129
- <li>Video duration: 2s to 30s</li>
130
- <li>Video aspect ratio: 1:3 to 3:1</li>
131
- <li>Video formats: mp4, avi, mov</li>
132
- <li>Image file size: Less than 5MB</li>
133
- <li>Image resolution: The shorter side must be greater than 200, and the longer side must be less than 4096</li>
134
- <li>Image formats: jpg, png, jpeg, webp, bmp</li>
135
- </ul>
136
- Current, the inference quality has two variants. You can use our open-source code for more flexible configuration.
137
  <ul>
138
- <li>wan-pro: 25fps, 720p</li>
139
- <li>wan-std: 15fps, 720p</li>
140
  </ul>
 
 
141
  </details>
142
  """)
143
 
144
  with gr.Row():
145
  with gr.Column():
146
  ref_img = gr.Image(
147
- label="Reference Image(参考图像)",
148
- type="pil",
149
  sources=["upload"],
150
  )
151
 
152
  video = gr.Video(
153
- label="Template Video(模版视频)",
154
  sources=["upload"],
155
  )
156
 
157
  with gr.Row():
158
  model_id = gr.Dropdown(
159
- label="Mode(模式)",
160
  choices=["wan2.2-animate-move", "wan2.2-animate-mix"],
161
  value="wan2.2-animate-move",
162
  info=""
163
  )
164
 
165
  model = gr.Dropdown(
166
- label="Inference Quality",
167
  choices=["wan-pro", "wan-std"],
168
  value="wan-pro",
169
  )
170
 
171
- run_button = gr.Button("Generate Video(生成视频)")
172
 
173
  with gr.Column():
174
- output_video = gr.Video(label="Output Video(输出视频)")
175
- output_status = gr.Textbox(label="Status(状态)")
176
 
177
  run_button.click(
178
  fn=app.predict,
 
12
  import subprocess
13
  import tempfile
14
 
15
+ # Папки для файлов
16
+ UPLOADS_IMAGES_DIR = './uploads/images'
17
+ UPLOADS_VIDEOS_DIR = './uploads/videos'
18
+ OUTPUT_DIR = './output'
19
+ os.makedirs(UPLOADS_IMAGES_DIR, exist_ok=True)
20
+ os.makedirs(UPLOADS_VIDEOS_DIR, exist_ok=True)
21
+ os.makedirs(OUTPUT_DIR, exist_ok=True)
22
+
23
  class WanAnimateApp:
24
  def __init__(self):
25
  model_name = "stabilityai/stable-video-diffusion-img2vid-xt"
 
30
  device_map="cpu"
31
  )
32
 
33
+ def save_uploaded_image(self, img):
34
+ """Сохраняет загруженное изображение в папку, возвращает путь"""
35
+ if img is not None:
36
+ filename = f"image_{uuid.uuid4()}.png"
37
+ path = os.path.join(UPLOADS_IMAGES_DIR, filename)
38
+ if isinstance(img, Image.Image):
39
+ img.save(path)
40
+ else:
41
+ Image.open(img).save(path)
42
+ return path
43
+ return None
44
+
45
+ def save_uploaded_video(self, vid):
46
+ """Сохраняет загруженное видео в папку, возвращает путь"""
47
+ if vid is not None:
48
+ filename = f"video_{uuid.uuid4()}.mp4"
49
+ path = os.path.join(UPLOADS_VIDEOS_DIR, filename)
50
+ if isinstance(vid, str):
51
+ shutil.copy(vid, path)
52
+ else:
53
+ with open(path, 'wb') as f:
54
+ f.write(vid)
55
+ return path
56
+ return None
57
+
58
  def predict(
59
  self,
60
+ ref_img_path,
61
+ video_path,
62
  model_id,
63
  model,
64
  ):
65
+ if ref_img_path is None or video_path is None:
66
  return None, "Upload both image and video."
67
 
68
  try:
69
+ # Local processing with saved paths
70
+ ref_image = Image.open(ref_img_path).convert("RGB").resize((576, 320))
71
+ cap = cv2.VideoCapture(video_path)
 
 
 
 
72
  frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
73
  cap.release()
74
  motion_hint = f" with dynamic motion from {frame_count} frames"
 
97
  temp_dir = tempfile.mkdtemp()
98
  for i, frame in enumerate(output):
99
  frame.save(f"{temp_dir}/frame_{i:04d}.png")
100
+ temp_video = os.path.join(OUTPUT_DIR, f"output_{uuid.uuid4()}.mp4")
101
  subprocess.run([
102
  'ffmpeg', '-y', '-framerate', '7', '-i', f"{temp_dir}/frame_%04d.png",
103
  '-c:v', 'libx264', '-pix_fmt', 'yuv420p', temp_video
 
140
 
141
  gr.HTML("""
142
  <details>
143
+ <summary>‼️Usage (использования)</summary>
144
+ Wan-Animate поддерживает два режима:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  <ul>
146
+ <li>Move Mode: анимирует перс��нажа в изображении движениями из видео</li>
147
+ <li>Mix Mode: заменяет персонажа в видео на персонажа из изображения</li>
148
  </ul>
149
+ Ограничения: Видео <200MB, 2–30s, 200–2048 res; Изображение <5MB, 200–4096 res.
150
+ Quality: wan-pro (25fps, 720p), wan-std (15fps, 720p).
151
  </details>
152
  """)
153
 
154
  with gr.Row():
155
  with gr.Column():
156
  ref_img = gr.Image(
157
+ label="Reference Image(изображение)",
158
+ type="filepath",
159
  sources=["upload"],
160
  )
161
 
162
  video = gr.Video(
163
+ label="Template Video(шаблонное видео)",
164
  sources=["upload"],
165
  )
166
 
167
  with gr.Row():
168
  model_id = gr.Dropdown(
169
+ label="Mode(режим)",
170
  choices=["wan2.2-animate-move", "wan2.2-animate-mix"],
171
  value="wan2.2-animate-move",
172
  info=""
173
  )
174
 
175
  model = gr.Dropdown(
176
+ label="Inference Quality(качество)",
177
  choices=["wan-pro", "wan-std"],
178
  value="wan-pro",
179
  )
180
 
181
+ run_button = gr.Button("Generate Video(генерировать видео)")
182
 
183
  with gr.Column():
184
+ output_video = gr.Video(label="Output Video(выходное видео)")
185
+ output_status = gr.Textbox(label="Status(статус)")
186
 
187
  run_button.click(
188
  fn=app.predict,