svjack commited on
Commit
e89e9ac
·
verified ·
1 Parent(s): 6aab1a8

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -33,3 +33,9 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ examples/man_walking.mp4 filter=lfs diff=lfs merge=lfs -text
37
+ examples/leopard.mp4 filter=lfs diff=lfs merge=lfs -text
38
+ examples/woman.mp4 filter=lfs diff=lfs merge=lfs -text
39
+ examples/neon.mp4 filter=lfs diff=lfs merge=lfs -text
40
+ examples/painter.mp4 filter=lfs diff=lfs merge=lfs -text
41
+ .gradio/cached_examples/21/Edited[[:space:]]Video/ab83f4b76b6dbcfa450f/tmptcf9qsfp.mp4 filter=lfs diff=lfs merge=lfs -text
.gradio/cached_examples/21/Edited Video/ab83f4b76b6dbcfa450f/tmptcf9qsfp.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ccaae8023fdc0a0f8a6e2232922b5dd4e72ddcc26279a3e73c40f3d7aa655e48
3
+ size 396712
.gradio/cached_examples/21/indices.csv ADDED
@@ -0,0 +1 @@
 
 
1
+ 0
.gradio/cached_examples/21/log.csv ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ Edited Video,timestamp
2
+ "{""video"": {""path"": "".gradio/cached_examples/21/Edited Video/ab83f4b76b6dbcfa450f/tmptcf9qsfp.mp4"", ""url"": ""/gradio_api/file=/tmp/gradio/6f16fb9b267c48ec5aeba92557b8df29ac8eb49eb6c344d8b58c3c22e4b13948/tmptcf9qsfp.mp4"", ""size"": null, ""orig_name"": ""tmptcf9qsfp.mp4"", ""mime_type"": null, ""is_stream"": false, ""meta"": {""_type"": ""gradio.FileData""}}, ""subtitles"": null}",2025-09-19 03:00:58.163311
.gradio/certificate.pem ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN CERTIFICATE-----
2
+ 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
31
+ -----END CERTIFICATE-----
.ipynb_checkpoints/app-checkpoint.py ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ '''
2
+ pip uninstall torch torchvision torchaudio
3
+ pip install -U torch torchvision torchaudio
4
+ '''
5
+
6
+ import gradio as gr
7
+ import torch
8
+ import spaces
9
+ from typing import List
10
+ from PIL import Image
11
+ from diffusers import LucyEditPipeline, AutoencoderKLWan
12
+ from diffusers.utils import export_to_video, load_video
13
+ import tempfile
14
+ import os
15
+
16
+ model_id = "decart-ai/Lucy-Edit-Dev"
17
+ vae = AutoencoderKLWan.from_pretrained(model_id, subfolder="vae", torch_dtype=torch.float32)
18
+ pipe = LucyEditPipeline.from_pretrained(model_id, vae=vae, torch_dtype=torch.bfloat16)
19
+ #pipe.to("cuda")
20
+ pipe.enable_model_cpu_offload()
21
+
22
+ def calculate_resolution(input_width, input_height, min_dimension=480, max_dimension=832, compatible_round=32):
23
+ """Calculate optimal resolution preserving aspect ratio within bounds"""
24
+ # Ensure dimensions are multiples of the compatible rounding
25
+ def round_to(x, compatible_round):
26
+ return max(min_dimension, min(max_dimension, int(round(x / compatible_round) * compatible_round)))
27
+
28
+ # Get aspect ratio
29
+ aspect_ratio = input_width / input_height
30
+
31
+ # Square videos (aspect ratio close to 1:1)
32
+ if 0.98 <= aspect_ratio <= 1.02:
33
+ return 640, 640
34
+
35
+ # Landscape videos (width > height)
36
+ elif aspect_ratio > 1:
37
+ # Try to use max width
38
+ new_width = max_dimension
39
+ new_height = new_width / aspect_ratio
40
+
41
+ # If height would be too small, use min height
42
+ if new_height < min_dimension:
43
+ new_height = min_dimension
44
+ new_width = new_height * aspect_ratio
45
+ # If width exceeds max, clamp it
46
+ if new_width > max_dimension:
47
+ new_width = max_dimension
48
+
49
+ return round_to(new_width, compatible_round), round_to(new_height, compatible_round)
50
+
51
+ # Portrait videos (height > width)
52
+ else:
53
+ # Try to use max height
54
+ new_height = max_dimension
55
+ new_width = new_height * aspect_ratio
56
+
57
+ # If width would be too small, use min width
58
+ if new_width < min_dimension:
59
+ new_width = min_dimension
60
+ new_height = new_width / aspect_ratio
61
+ # If height exceeds max, clamp it
62
+ if new_height > max_dimension:
63
+ new_height = max_dimension
64
+
65
+ return round_to(new_width, compatible_round), round_to(new_height, compatible_round)
66
+
67
+
68
+ @spaces.GPU(duration=120)
69
+ def process_video(
70
+ video_path,
71
+ prompt,
72
+ negative_prompt="",
73
+ num_frames=81,
74
+ auto_resize=True,
75
+ manual_height=480,
76
+ manual_width=832,
77
+ guidance_scale=5,
78
+ progress=gr.Progress(track_tqdm=True)
79
+ ):
80
+ # Load and preprocess video
81
+ progress(0.2, desc="Loading video...")
82
+
83
+ # Get video dimensions
84
+ temp_video = load_video(video_path)
85
+ print(len(temp_video))
86
+ if temp_video and len(temp_video) > 0:
87
+ original_width, original_height = temp_video[0].size
88
+
89
+ # Calculate dimensions
90
+ if auto_resize:
91
+ width, height = calculate_resolution(original_width, original_height)
92
+ else:
93
+ width, height = manual_width, manual_height
94
+ else:
95
+ raise gr.Error("Could not load video or video is empty")
96
+
97
+ # Convert video function
98
+ def convert_video(video: List[Image.Image]) -> List[Image.Image]:
99
+ # Ensure we don't exceed the video length
100
+ frames_to_load = min(len(video), num_frames)
101
+ video_frames = video[:frames_to_load]
102
+ # Resize frames
103
+ video_frames = [frame.resize((width, height)) for frame in video_frames]
104
+ return video_frames
105
+
106
+ # Load video from file path
107
+ video = load_video(video_path, convert_method=convert_video)
108
+
109
+ # Ensure we have the right number of frames
110
+ if len(video) < num_frames:
111
+ num_frames = len(video)
112
+
113
+ # Generate edited video
114
+ progress(0.5, desc="Generating edited video...")
115
+ output = pipe(
116
+ prompt=prompt,
117
+ video=video,
118
+ negative_prompt=negative_prompt,
119
+ height=height,
120
+ width=width,
121
+ num_frames=num_frames,
122
+ guidance_scale=guidance_scale,
123
+ ).frames[0]
124
+
125
+ # Export to temporary file
126
+ progress(0.9, desc="Exporting video...")
127
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_file:
128
+ output_path = tmp_file.name
129
+
130
+ export_to_video(output, output_path, fps=24)
131
+
132
+ progress(1.0, desc="Complete!")
133
+ return output_path
134
+
135
+ css = '''
136
+ .fillable{max-width: 1100px !important}
137
+ '''
138
+ with gr.Blocks(title="Lucy Edit - Video Editing with Text", css=css) as demo:
139
+ gr.HTML(f"""<p align="center">
140
+ <img src="https://huggingface.co/decart-ai/Lucy-Edit-Dev/resolve/main/assets/logo.png" width="480" style="margin-top: -25px" alt="Lucy Edit Dev Logo"/>
141
+ </p>
142
+
143
+ <p align="center">
144
+ 🤗 <a href="https://github.com/DecartAI/lucy-edit-comfyui"><b>Model</b></a>
145
+ &nbsp;|&nbsp; 🧪 <a href="https://github.com/DecartAI/lucy-edit-comfyui"><b>ComfyUI</b></a>
146
+ &nbsp;|&nbsp; 📖 <a href="https://platform.decart.ai">Playground</a>
147
+ &nbsp;|&nbsp; 📑 <a href="#">arXiv (Coming soon)</a>
148
+ &nbsp;|&nbsp; 💬 <a href="https://discord.gg/decart">Discord</a>
149
+ </p>""")
150
+
151
+ with gr.Row():
152
+ with gr.Column(scale=1):
153
+ # Input controls
154
+ video_input = gr.Video(label="Input Video")
155
+
156
+ prompt = gr.Textbox(
157
+ label="Edit Prompt",
158
+ placeholder="Describe what you want to change in the video...",
159
+ lines=3
160
+ )
161
+
162
+ with gr.Accordion("Advanced Settings", open=False):
163
+ negative_prompt = gr.Textbox(
164
+ label="Negative Prompt (optional)",
165
+ placeholder="Describe what you DON'T want in the video...",
166
+ lines=2
167
+ )
168
+ auto_resize = gr.Checkbox(
169
+ label="Auto-resize (preserve aspect ratio)",
170
+ value=True,
171
+ info="Automatically calculate dimensions based on input video"
172
+ )
173
+
174
+ num_frames = gr.Slider(
175
+ label="Number of Frames",
176
+ minimum=1,
177
+ maximum=120,
178
+ value=81,
179
+ step=1,
180
+ info="More frames = longer processing time"
181
+ )
182
+
183
+ with gr.Row():
184
+ manual_height = gr.Slider(
185
+ label="Height (when auto-resize is off)",
186
+ minimum=256,
187
+ maximum=1024,
188
+ value=480,
189
+ step=32
190
+ )
191
+ manual_width = gr.Slider(
192
+ label="Width (when auto-resize is off)",
193
+ minimum=256,
194
+ maximum=1024,
195
+ value=832,
196
+ step=32
197
+ )
198
+
199
+ guidance_scale = gr.Slider(
200
+ label="Guidance Scale",
201
+ minimum=1.0,
202
+ maximum=20.0,
203
+ value=5.0,
204
+ step=0.5,
205
+ info="Higher values follow the prompt more strictly"
206
+ )
207
+
208
+ generate_btn = gr.Button("Edit Video", variant="primary")
209
+
210
+ with gr.Column(scale=1):
211
+ video_output = gr.Video(label="Edited Video", autoplay=True)
212
+
213
+ gr.Examples(
214
+ examples=[
215
+ ["examples/neon.mp4", "Add a colorful scarlet macaw parrot perched on the man's left shoulder, bright red and blue wing feathers with yellow accents, curved black beak, intelligent dark eyes, talons gripping fabric naturally, long tail feathers extending downward, glossy plumage catching light, slight wing adjustment for balance, natural weight distribution, soft shadow beneath bird."],
216
+ ["examples/painter.mp4", "Change the hair color to platinum blonde with natural highlights, subtle root shadowing, silky texture, gentle waves, soft shine, dimensional tones, strand definition, natural movement, professional color treatment, salon-quality finish, light-catching shimmer, varied blonde shades from honey to ash, realistic color gradation, healthy glossy appearance, volumetric lighting interaction."],
217
+ ],
218
+ inputs=[video_input, prompt],
219
+ outputs=video_output,
220
+ fn=process_video,
221
+ cache_examples="lazy",
222
+ )
223
+
224
+ # Event handlers
225
+ generate_btn.click(
226
+ fn=process_video,
227
+ inputs=[
228
+ video_input,
229
+ prompt,
230
+ negative_prompt,
231
+ num_frames,
232
+ auto_resize,
233
+ manual_height,
234
+ manual_width,
235
+ guidance_scale
236
+ ],
237
+ outputs=video_output
238
+ )
239
+
240
+ if __name__ == "__main__":
241
+ demo.launch(share=True)
.ipynb_checkpoints/requirements-checkpoint.txt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ git+https://github.com/huggingface/diffusers.git@8c72cd12ee65e420c86a0724f0182f966f339a7e
2
+ transformers
3
+ accelerate
4
+ safetensors
5
+ sentencepiece
6
+ imageio
7
+ imageio-ffmpeg
8
+ ftfy
9
+
10
+ huggingface_hub
11
+ gradio
12
+ httpx[socks]
13
+ spaces
README.md CHANGED
@@ -1,12 +1,13 @@
1
  ---
2
  title: Lucy Edit Dev
3
- emoji: 💻
4
- colorFrom: red
5
- colorTo: indigo
6
  sdk: gradio
7
  sdk_version: 5.46.0
8
  app_file: app.py
9
  pinned: false
 
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
  title: Lucy Edit Dev
3
+ emoji: ✂️🎥
4
+ colorFrom: blue
5
+ colorTo: green
6
  sdk: gradio
7
  sdk_version: 5.46.0
8
  app_file: app.py
9
  pinned: false
10
+ short_description: Video editing with a prompt
11
  ---
12
 
13
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ '''
2
+ pip uninstall torch torchvision torchaudio
3
+ pip install -U torch torchvision torchaudio
4
+ '''
5
+
6
+ import gradio as gr
7
+ import torch
8
+ import spaces
9
+ from typing import List
10
+ from PIL import Image
11
+ from diffusers import LucyEditPipeline, AutoencoderKLWan
12
+ from diffusers.utils import export_to_video, load_video
13
+ import tempfile
14
+ import os
15
+
16
+ model_id = "decart-ai/Lucy-Edit-Dev"
17
+ vae = AutoencoderKLWan.from_pretrained(model_id, subfolder="vae", torch_dtype=torch.float32)
18
+ pipe = LucyEditPipeline.from_pretrained(model_id, vae=vae, torch_dtype=torch.bfloat16)
19
+ #pipe.to("cuda")
20
+ pipe.enable_model_cpu_offload()
21
+
22
+ def calculate_resolution(input_width, input_height, min_dimension=480, max_dimension=832, compatible_round=32):
23
+ """Calculate optimal resolution preserving aspect ratio within bounds"""
24
+ # Ensure dimensions are multiples of the compatible rounding
25
+ def round_to(x, compatible_round):
26
+ return max(min_dimension, min(max_dimension, int(round(x / compatible_round) * compatible_round)))
27
+
28
+ # Get aspect ratio
29
+ aspect_ratio = input_width / input_height
30
+
31
+ # Square videos (aspect ratio close to 1:1)
32
+ if 0.98 <= aspect_ratio <= 1.02:
33
+ return 640, 640
34
+
35
+ # Landscape videos (width > height)
36
+ elif aspect_ratio > 1:
37
+ # Try to use max width
38
+ new_width = max_dimension
39
+ new_height = new_width / aspect_ratio
40
+
41
+ # If height would be too small, use min height
42
+ if new_height < min_dimension:
43
+ new_height = min_dimension
44
+ new_width = new_height * aspect_ratio
45
+ # If width exceeds max, clamp it
46
+ if new_width > max_dimension:
47
+ new_width = max_dimension
48
+
49
+ return round_to(new_width, compatible_round), round_to(new_height, compatible_round)
50
+
51
+ # Portrait videos (height > width)
52
+ else:
53
+ # Try to use max height
54
+ new_height = max_dimension
55
+ new_width = new_height * aspect_ratio
56
+
57
+ # If width would be too small, use min width
58
+ if new_width < min_dimension:
59
+ new_width = min_dimension
60
+ new_height = new_width / aspect_ratio
61
+ # If height exceeds max, clamp it
62
+ if new_height > max_dimension:
63
+ new_height = max_dimension
64
+
65
+ return round_to(new_width, compatible_round), round_to(new_height, compatible_round)
66
+
67
+
68
+ @spaces.GPU(duration=120)
69
+ def process_video(
70
+ video_path,
71
+ prompt,
72
+ negative_prompt="",
73
+ num_frames=81,
74
+ auto_resize=True,
75
+ manual_height=480,
76
+ manual_width=832,
77
+ guidance_scale=5,
78
+ progress=gr.Progress(track_tqdm=True)
79
+ ):
80
+ # Load and preprocess video
81
+ progress(0.2, desc="Loading video...")
82
+
83
+ # Get video dimensions
84
+ temp_video = load_video(video_path)
85
+ print(len(temp_video))
86
+ if temp_video and len(temp_video) > 0:
87
+ original_width, original_height = temp_video[0].size
88
+
89
+ # Calculate dimensions
90
+ if auto_resize:
91
+ width, height = calculate_resolution(original_width, original_height)
92
+ else:
93
+ width, height = manual_width, manual_height
94
+ else:
95
+ raise gr.Error("Could not load video or video is empty")
96
+
97
+ # Convert video function
98
+ def convert_video(video: List[Image.Image]) -> List[Image.Image]:
99
+ # Ensure we don't exceed the video length
100
+ frames_to_load = min(len(video), num_frames)
101
+ video_frames = video[:frames_to_load]
102
+ # Resize frames
103
+ video_frames = [frame.resize((width, height)) for frame in video_frames]
104
+ return video_frames
105
+
106
+ # Load video from file path
107
+ video = load_video(video_path, convert_method=convert_video)
108
+
109
+ # Ensure we have the right number of frames
110
+ if len(video) < num_frames:
111
+ num_frames = len(video)
112
+
113
+ # Generate edited video
114
+ progress(0.5, desc="Generating edited video...")
115
+ output = pipe(
116
+ prompt=prompt,
117
+ video=video,
118
+ negative_prompt=negative_prompt,
119
+ height=height,
120
+ width=width,
121
+ num_frames=num_frames,
122
+ guidance_scale=guidance_scale,
123
+ ).frames[0]
124
+
125
+ # Export to temporary file
126
+ progress(0.9, desc="Exporting video...")
127
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_file:
128
+ output_path = tmp_file.name
129
+
130
+ export_to_video(output, output_path, fps=24)
131
+
132
+ progress(1.0, desc="Complete!")
133
+ return output_path
134
+
135
+ css = '''
136
+ .fillable{max-width: 1100px !important}
137
+ '''
138
+ with gr.Blocks(title="Lucy Edit - Video Editing with Text", css=css) as demo:
139
+ gr.HTML(f"""<p align="center">
140
+ <img src="https://huggingface.co/decart-ai/Lucy-Edit-Dev/resolve/main/assets/logo.png" width="480" style="margin-top: -25px" alt="Lucy Edit Dev Logo"/>
141
+ </p>
142
+
143
+ <p align="center">
144
+ 🤗 <a href="https://github.com/DecartAI/lucy-edit-comfyui"><b>Model</b></a>
145
+ &nbsp;|&nbsp; 🧪 <a href="https://github.com/DecartAI/lucy-edit-comfyui"><b>ComfyUI</b></a>
146
+ &nbsp;|&nbsp; 📖 <a href="https://platform.decart.ai">Playground</a>
147
+ &nbsp;|&nbsp; 📑 <a href="#">arXiv (Coming soon)</a>
148
+ &nbsp;|&nbsp; 💬 <a href="https://discord.gg/decart">Discord</a>
149
+ </p>""")
150
+
151
+ with gr.Row():
152
+ with gr.Column(scale=1):
153
+ # Input controls
154
+ video_input = gr.Video(label="Input Video")
155
+
156
+ prompt = gr.Textbox(
157
+ label="Edit Prompt",
158
+ placeholder="Describe what you want to change in the video...",
159
+ lines=3
160
+ )
161
+
162
+ with gr.Accordion("Advanced Settings", open=False):
163
+ negative_prompt = gr.Textbox(
164
+ label="Negative Prompt (optional)",
165
+ placeholder="Describe what you DON'T want in the video...",
166
+ lines=2
167
+ )
168
+ auto_resize = gr.Checkbox(
169
+ label="Auto-resize (preserve aspect ratio)",
170
+ value=True,
171
+ info="Automatically calculate dimensions based on input video"
172
+ )
173
+
174
+ num_frames = gr.Slider(
175
+ label="Number of Frames",
176
+ minimum=1,
177
+ maximum=120,
178
+ value=81,
179
+ step=1,
180
+ info="More frames = longer processing time"
181
+ )
182
+
183
+ with gr.Row():
184
+ manual_height = gr.Slider(
185
+ label="Height (when auto-resize is off)",
186
+ minimum=256,
187
+ maximum=1024,
188
+ value=480,
189
+ step=32
190
+ )
191
+ manual_width = gr.Slider(
192
+ label="Width (when auto-resize is off)",
193
+ minimum=256,
194
+ maximum=1024,
195
+ value=832,
196
+ step=32
197
+ )
198
+
199
+ guidance_scale = gr.Slider(
200
+ label="Guidance Scale",
201
+ minimum=1.0,
202
+ maximum=20.0,
203
+ value=5.0,
204
+ step=0.5,
205
+ info="Higher values follow the prompt more strictly"
206
+ )
207
+
208
+ generate_btn = gr.Button("Edit Video", variant="primary")
209
+
210
+ with gr.Column(scale=1):
211
+ video_output = gr.Video(label="Edited Video", autoplay=True)
212
+
213
+ gr.Examples(
214
+ examples=[
215
+ ["examples/neon.mp4", "Add a colorful scarlet macaw parrot perched on the man's left shoulder, bright red and blue wing feathers with yellow accents, curved black beak, intelligent dark eyes, talons gripping fabric naturally, long tail feathers extending downward, glossy plumage catching light, slight wing adjustment for balance, natural weight distribution, soft shadow beneath bird."],
216
+ ["examples/painter.mp4", "Change the hair color to platinum blonde with natural highlights, subtle root shadowing, silky texture, gentle waves, soft shine, dimensional tones, strand definition, natural movement, professional color treatment, salon-quality finish, light-catching shimmer, varied blonde shades from honey to ash, realistic color gradation, healthy glossy appearance, volumetric lighting interaction."],
217
+ ],
218
+ inputs=[video_input, prompt],
219
+ outputs=video_output,
220
+ fn=process_video,
221
+ cache_examples="lazy",
222
+ )
223
+
224
+ # Event handlers
225
+ generate_btn.click(
226
+ fn=process_video,
227
+ inputs=[
228
+ video_input,
229
+ prompt,
230
+ negative_prompt,
231
+ num_frames,
232
+ auto_resize,
233
+ manual_height,
234
+ manual_width,
235
+ guidance_scale
236
+ ],
237
+ outputs=video_output
238
+ )
239
+
240
+ if __name__ == "__main__":
241
+ demo.launch(share=True)
examples/leopard.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:edcd64ede1c2e982d12bad891dfaf984330aafb9704065e7b3451837e6a1ab06
3
+ size 12796248
examples/man_walking.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a19fede8433fc08ffd9af62fb3594706d3db75e634a35e764ad3b66865c8b38a
3
+ size 16581071
examples/neon.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e62dc89173956d4d22defac1d24f0835c8d8d0490f7969dd13b38429d69165ca
3
+ size 3182890
examples/painter.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:37b385068db4c25d8f29104580b3267a4fafc2afbd4235f57a206a587a16e56f
3
+ size 2941421
examples/woman.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d70c43b33d8e8bfb33d3c7dc6e9bfbd5d4e655119026820c7ebdaceca6faeeed
3
+ size 18397860
requirements.txt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ git+https://github.com/huggingface/diffusers.git@8c72cd12ee65e420c86a0724f0182f966f339a7e
2
+ transformers
3
+ accelerate
4
+ safetensors
5
+ sentencepiece
6
+ imageio
7
+ imageio-ffmpeg
8
+ ftfy
9
+
10
+ huggingface_hub
11
+ gradio
12
+ httpx[socks]
13
+ spaces