multimodalart HF Staff commited on
Commit
182aa1d
·
verified ·
1 Parent(s): 55dc136

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +222 -0
app.py ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import torch
3
+ import spaces
4
+ from typing import List
5
+ from PIL import Image
6
+ from diffusers import AutoencoderKLWan, LucyEditPipeline
7
+ from diffusers.utils import export_to_video, load_video
8
+ import tempfile
9
+ import os
10
+
11
+ model_id = "decart-ai/Lucy-Edit-Dev"
12
+ vae = AutoencoderKLWan.from_pretrained(model_id, subfolder="vae", torch_dtype=torch.float32)
13
+ pipe = LucyEditPipeline.from_pretrained(model_id, vae=vae, torch_dtype=torch.bfloat16)
14
+ pipe.to("cuda")
15
+
16
+ def calculate_resolution(input_width, input_height, max_dimension=832):
17
+ """Calculate optimal resolution preserving aspect ratio"""
18
+ # Ensure dimensions are multiples of 16
19
+ def round_to_16(x):
20
+ return int(round(x / 16.0) * 16)
21
+
22
+ # Get aspect ratio
23
+ aspect_ratio = input_width / input_height
24
+
25
+ # Square videos
26
+ if 0.95 <= aspect_ratio <= 1.05:
27
+ return 512, 512
28
+
29
+ # Landscape videos (width > height)
30
+ elif aspect_ratio > 1:
31
+ if input_width > input_height:
32
+ # Fit to max width
33
+ new_width = min(max_dimension, input_width)
34
+ new_height = int(new_width / aspect_ratio)
35
+
36
+ # Ensure height doesn't exceed max
37
+ if new_height > 480:
38
+ new_height = 480
39
+ new_width = int(new_height * aspect_ratio)
40
+ else:
41
+ new_width = max_dimension
42
+ new_height = int(new_width / aspect_ratio)
43
+
44
+ # Portrait videos (height > width)
45
+ else:
46
+ if input_height > input_width:
47
+ # Fit to max height
48
+ new_height = min(max_dimension, input_height)
49
+ new_width = int(new_height * aspect_ratio)
50
+
51
+ # Ensure width doesn't exceed max
52
+ if new_width > 480:
53
+ new_width = 480
54
+ new_height = int(new_width / aspect_ratio)
55
+ else:
56
+ new_height = max_dimension
57
+ new_width = int(new_height * aspect_ratio)
58
+
59
+ # Round to multiples of 16
60
+ return round_to_16(new_width), round_to_16(new_height)
61
+
62
+ @spaces.GPU(duration=90)
63
+ def process_video(
64
+ video_path,
65
+ prompt,
66
+ negative_prompt,
67
+ num_frames,
68
+ auto_resize,
69
+ manual_height,
70
+ manual_width,
71
+ guidance_scale,
72
+ progress=gr.Progress()
73
+ ):
74
+ """Process the video with Lucy Edit"""
75
+ try:
76
+ # Load and preprocess video
77
+ progress(0.2, desc="Loading video...")
78
+
79
+ # Get video dimensions
80
+ temp_video = load_video(video_path)
81
+ if temp_video and len(temp_video) > 0:
82
+ original_width, original_height = temp_video[0].size
83
+
84
+ # Calculate dimensions
85
+ if auto_resize:
86
+ width, height = calculate_resolution(original_width, original_height)
87
+ gr.Info(f"Auto-resized from {original_width}x{original_height} to {width}x{height} (preserving aspect ratio)")
88
+ else:
89
+ width, height = manual_width, manual_height
90
+ if abs((original_width/original_height) - (width/height)) > 0.1:
91
+ gr.Warning(f"Output aspect ratio ({width}x{height}) differs significantly from input ({original_width}x{original_height}). Video may appear stretched.")
92
+ else:
93
+ raise ValueError("Could not load video or video is empty")
94
+
95
+ # Convert video function
96
+ def convert_video(video: List[Image.Image]) -> List[Image.Image]:
97
+ # Ensure we don't exceed the video length
98
+ frames_to_load = min(len(video), num_frames)
99
+ video_frames = video[:frames_to_load]
100
+ # Resize frames
101
+ video_frames = [frame.resize((width, height)) for frame in video_frames]
102
+ return video_frames
103
+
104
+ # Load video from file path
105
+ video = load_video(video_path, convert_method=convert_video)
106
+
107
+ # Ensure we have the right number of frames
108
+ if len(video) < num_frames:
109
+ gr.Warning(f"Video has only {len(video)} frames, using all available frames.")
110
+ num_frames = len(video)
111
+
112
+ # Generate edited video
113
+ progress(0.5, desc="Generating edited video...")
114
+ output = pipe(
115
+ prompt=prompt,
116
+ video=video,
117
+ negative_prompt=negative_prompt,
118
+ height=height,
119
+ width=width,
120
+ num_frames=num_frames,
121
+ guidance_scale=guidance_scale,
122
+ ).frames[0]
123
+
124
+ # Export to temporary file
125
+ progress(0.9, desc="Exporting video...")
126
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_file:
127
+ output_path = tmp_file.name
128
+
129
+ export_to_video(output, output_path, fps=24)
130
+
131
+ progress(1.0, desc="Complete!")
132
+ return output_path
133
+
134
+ except Exception as e:
135
+ gr.Error(f"An error occurred: {str(e)}")
136
+ return None
137
+
138
+ # Create Gradio interface
139
+ with gr.Blocks(title="Lucy Edit - Video Editing with Text") as demo:
140
+ gr.Markdown(f"""# 🎬 Lucy Edit - AI Video Editing""")
141
+
142
+ with gr.Row():
143
+ with gr.Column(scale=1):
144
+ # Input controls
145
+ video_input = gr.Video(label="Input Video")
146
+
147
+ prompt = gr.Textbox(
148
+ label="Edit Prompt",
149
+ placeholder="Describe what you want to change in the video...",
150
+ lines=3,
151
+ placeholder="Change the apron and blouse to a classic clown costume"
152
+ )
153
+
154
+ with gr.Accordion("Advanced Settings", open=False):
155
+ negative_prompt = gr.Textbox(
156
+ label="Negative Prompt (optional)",
157
+ placeholder="Describe what you DON'T want in the video...",
158
+ lines=2
159
+ )
160
+ auto_resize = gr.Checkbox(
161
+ label="Auto-resize (preserve aspect ratio)",
162
+ value=True,
163
+ info="Automatically calculate dimensions based on input video"
164
+ )
165
+
166
+ num_frames = gr.Slider(
167
+ label="Number of Frames",
168
+ minimum=1,
169
+ maximum=120,
170
+ value=81,
171
+ step=1,
172
+ info="More frames = longer processing time"
173
+ )
174
+
175
+ with gr.Row():
176
+ manual_height = gr.Slider(
177
+ label="Height (when auto-resize is off)",
178
+ minimum=256,
179
+ maximum=1024,
180
+ value=480,
181
+ step=32
182
+ )
183
+ manual_width = gr.Slider(
184
+ label="Width (when auto-resize is off)",
185
+ minimum=256,
186
+ maximum=1024,
187
+ value=832,
188
+ step=32
189
+ )
190
+
191
+ guidance_scale = gr.Slider(
192
+ label="Guidance Scale",
193
+ minimum=1.0,
194
+ maximum=20.0,
195
+ value=5.0,
196
+ step=0.5,
197
+ info="Higher values follow the prompt more strictly"
198
+ )
199
+
200
+ generate_btn = gr.Button("🎨 Generate Edited Video", variant="primary")
201
+
202
+ with gr.Column(scale=1):
203
+ video_output = gr.Video(label="Edited Video")
204
+
205
+ # Event handlers
206
+ generate_btn.click(
207
+ fn=process_video,
208
+ inputs=[
209
+ video_input,
210
+ prompt,
211
+ negative_prompt,
212
+ num_frames,
213
+ auto_resize,
214
+ manual_height,
215
+ manual_width,
216
+ guidance_scale
217
+ ],
218
+ outputs=video_output
219
+ )
220
+
221
+ if __name__ == "__main__":
222
+ demo.launch(share=True)