Skip to content

Commit 7f53942

Browse files
authored
[feat]: add Lucy Edit inference scaffold (#1363)
Co-authored-by: Aryan Kumar <aryan5v@users.noreply.github.com>
1 parent 19a838f commit 7f53942

12 files changed

Lines changed: 375 additions & 11 deletions

File tree

docs/design/inference_schema_parity_inventory.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ surfaces:
108108
vae_sp: generator.pipeline.preset_overrides.vae_sp
109109
dmd_denoising_steps: generator.pipeline.preset_overrides.dmd_denoising_steps
110110
ti2v_task: generator.pipeline.preset_overrides.ti2v_task
111+
lucy_edit_task: generator.pipeline.preset_overrides.lucy_edit_task
111112
boundary_ratio: generator.pipeline.preset_overrides.boundary_ratio
112113
compatibility_only:
113114
model_path: "Redundant with generator.model_path."

docs/inference/support_matrix.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ pipeline initialization and sampling.
5858
| FastWan2.1 T2V 1.3B | `FastVideo/FastWan2.1-T2V-1.3B-Diffusers` | 480P ||||||
5959
| FastWan2.2 TI2V 5B Full Attn* | `FastVideo/FastWan2.2-TI2V-5B-FullAttn-Diffusers` | 720P ||||||
6060
| Wan2.2 TI2V 5B | `Wan-AI/Wan2.2-TI2V-5B-Diffusers` | 720P ||||||
61+
| Lucy Edit Dev 5B*** | `decart-ai/Lucy-Edit-Dev` | 480P ||||||
6162
| Wan2.2 T2V A14B | `Wan-AI/Wan2.2-T2V-A14B-Diffusers` | 480P<br>720P ||||||
6263
| Wan2.2 I2V A14B | `Wan-AI/Wan2.2-I2V-A14B-Diffusers` | 480P<br>720P ||||||
6364
| HunyuanVideo | `hunyuanvideo-community/HunyuanVideo` | 720px1280p<br>544px960p ||||||
@@ -78,6 +79,9 @@ pipeline initialization and sampling.
7879

7980
**Note**: Wan2.2 TI2V 5B has some quality issues when performing I2V generation. We are working on fixing this issue.
8081

82+
***Lucy Edit Dev uses a non-commercial model license. FastVideo support is
83+
focused on inference integration for video editing workflows.
84+
8185
`Sliding Tile Attn (Legacy Branch)` entries refer to the archived
8286
`sta_do_not_delete` branch workflow, not active `main` inference wiring.
8387

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from fastvideo import VideoGenerator
2+
3+
OUTPUT_PATH = "video_samples_lucy_edit"
4+
5+
6+
def main():
7+
generator = VideoGenerator.from_pretrained(
8+
"decart-ai/Lucy-Edit-Dev",
9+
num_gpus=1,
10+
use_fsdp_inference=False,
11+
dit_cpu_offload=True,
12+
vae_cpu_offload=False,
13+
text_encoder_cpu_offload=True,
14+
pin_cpu_memory=True,
15+
)
16+
17+
prompt = ("Change the apron and blouse to a classic clown costume: satin "
18+
"polka-dot jumpsuit in bright primary colors, ruffled white collar, "
19+
"oversized pom-pom buttons, white gloves, oversized red shoes, red "
20+
"foam nose; soft window light from left, eye-level medium shot.")
21+
video_path = "https://d2drjpuinn46lb.cloudfront.net/painter_original_edit.mp4"
22+
23+
generator.generate_video(
24+
prompt,
25+
negative_prompt="",
26+
video_path=video_path,
27+
output_path=OUTPUT_PATH,
28+
save_video=True,
29+
height=480,
30+
width=832,
31+
num_frames=81,
32+
fps=24,
33+
guidance_scale=5.0,
34+
)
35+
36+
37+
if __name__ == "__main__":
38+
main()

fastvideo/configs/pipelines/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99
from fastvideo.configs.pipelines.matrixgame3 import MatrixGame3I2V720PConfig
1010
from fastvideo.pipelines.basic.ltx2.pipeline_configs import LTX2T2VConfig
1111
from fastvideo.registry import get_pipeline_config_cls_from_name
12-
from fastvideo.configs.pipelines.wan import (SelfForcingWanT2V480PConfig, WanI2V480PConfig, WanI2V720PConfig,
13-
WanT2V480PConfig, WanT2V720PConfig)
12+
from fastvideo.configs.pipelines.wan import (LucyEditDevConfig, SelfForcingWanT2V480PConfig, WanI2V480PConfig,
13+
WanI2V720PConfig, WanT2V480PConfig, WanT2V720PConfig)
1414

1515
__all__ = [
1616
"HunyuanConfig", "FastHunyuanConfig", "HunyuanGameCraftPipelineConfig", "PipelineConfig", "Hunyuan15T2V480PConfig",
1717
"Hunyuan15T2V720PConfig", "WanT2V480PConfig", "WanI2V480PConfig", "WanT2V720PConfig", "WanI2V720PConfig",
18-
"SelfForcingWanT2V480PConfig", "CosmosConfig", "Cosmos25Config", "LTX2T2VConfig", "HYWorldConfig",
19-
"MatrixGame2I2V480PConfig", "MatrixGame3I2V720PConfig", "get_pipeline_config_cls_from_name"
18+
"SelfForcingWanT2V480PConfig", "LucyEditDevConfig", "CosmosConfig", "Cosmos25Config", "LTX2T2VConfig",
19+
"HYWorldConfig", "MatrixGame2I2V480PConfig", "MatrixGame3I2V720PConfig", "get_pipeline_config_cls_from_name"
2020
]

fastvideo/configs/pipelines/base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,9 @@ class PipelineConfig:
6969
# DMD parameters
7070
dmd_denoising_steps: list[int] | None = field(default=None)
7171

72-
# Wan2.2 TI2V parameters
72+
# Wan2.2 task modifiers
7373
ti2v_task: bool = False
74+
lucy_edit_task: bool = False
7475
boundary_ratio: float | None = None
7576

7677
# Compilation

fastvideo/configs/pipelines/wan.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66

77
from fastvideo.configs.models import DiTConfig, EncoderConfig, VAEConfig
88
from fastvideo.configs.models.dits import WanVideoConfig
9+
from fastvideo.configs.models.dits.wanvideo import WanVideoArchConfig
910
from fastvideo.configs.models.encoders import (BaseEncoderOutput, CLIPVisionConfig, T5Config,
1011
WAN2_1ControlCLIPVisionConfig)
1112
from fastvideo.configs.models.vaes import WanVAEConfig
13+
from fastvideo.configs.models.vaes.wanvae import WanVAEArchConfig
1214
from fastvideo.configs.pipelines.base import PipelineConfig
1315

1416

@@ -120,6 +122,142 @@ class Wan2_2_TI2V_5B_Config(WanT2V480PConfig):
120122
expand_timesteps: bool = True
121123

122124
def __post_init__(self) -> None:
125+
assert not (self.ti2v_task and self.lucy_edit_task)
126+
self.vae_config.load_encoder = True
127+
self.vae_config.load_decoder = True
128+
self.dit_config.expand_timesteps = self.expand_timesteps
129+
130+
131+
@dataclass
132+
class LucyEditDevConfig(Wan2_2_TI2V_5B_Config):
133+
"""Configuration for Decart Lucy Edit Dev video editing."""
134+
135+
dit_config: DiTConfig = field(default_factory=lambda: WanVideoConfig(arch_config=WanVideoArchConfig(
136+
num_attention_heads=24,
137+
in_channels=96,
138+
out_channels=48,
139+
ffn_dim=14336,
140+
num_layers=30,
141+
)))
142+
vae_config: VAEConfig = field(default_factory=lambda: WanVAEConfig(arch_config=WanVAEArchConfig(
143+
base_dim=160,
144+
decoder_base_dim=256,
145+
z_dim=48,
146+
in_channels=12,
147+
out_channels=12,
148+
scale_factor_spatial=16,
149+
patch_size=2,
150+
is_residual=True,
151+
clip_output=False,
152+
latents_mean=(
153+
-0.2289,
154+
-0.0052,
155+
-0.1323,
156+
-0.2339,
157+
-0.2799,
158+
0.0174,
159+
0.1838,
160+
0.1557,
161+
-0.1382,
162+
0.0542,
163+
0.2813,
164+
0.0891,
165+
0.1570,
166+
-0.0098,
167+
0.0375,
168+
-0.1825,
169+
-0.2246,
170+
-0.1207,
171+
-0.0698,
172+
0.5109,
173+
0.2665,
174+
-0.2108,
175+
-0.2158,
176+
0.2502,
177+
-0.2055,
178+
-0.0322,
179+
0.1109,
180+
0.1567,
181+
-0.0729,
182+
0.0899,
183+
-0.2799,
184+
-0.1230,
185+
-0.0313,
186+
-0.1649,
187+
0.0117,
188+
0.0723,
189+
-0.2839,
190+
-0.2083,
191+
-0.0520,
192+
0.3748,
193+
0.0152,
194+
0.1957,
195+
0.1433,
196+
-0.2944,
197+
0.3573,
198+
-0.0548,
199+
-0.1681,
200+
-0.0667,
201+
),
202+
latents_std=(
203+
0.4765,
204+
1.0364,
205+
0.4514,
206+
1.1677,
207+
0.5313,
208+
0.4990,
209+
0.4818,
210+
0.5013,
211+
0.8158,
212+
1.0344,
213+
0.5894,
214+
1.0901,
215+
0.6885,
216+
0.6165,
217+
0.8454,
218+
0.4978,
219+
0.5759,
220+
0.3523,
221+
0.7135,
222+
0.6804,
223+
0.5833,
224+
1.4146,
225+
0.8986,
226+
0.5659,
227+
0.7069,
228+
0.5338,
229+
0.4889,
230+
0.4917,
231+
0.4069,
232+
0.4999,
233+
0.6866,
234+
0.4093,
235+
0.5709,
236+
0.6065,
237+
0.6415,
238+
0.4944,
239+
0.5726,
240+
1.2042,
241+
0.5458,
242+
1.6887,
243+
0.3971,
244+
1.0600,
245+
0.3943,
246+
0.5537,
247+
0.5444,
248+
0.4089,
249+
0.7468,
250+
0.7744,
251+
),
252+
)))
253+
ti2v_task: bool = False
254+
lucy_edit_task: bool = True
255+
256+
def __post_init__(self) -> None:
257+
assert not (self.ti2v_task and self.lucy_edit_task)
258+
# Lucy uses Wan2.2's enhanced 48-channel VAE latents. Denoising
259+
# concatenates noise + video latents, matching the 96-channel
260+
# transformer input declared above.
123261
self.vae_config.load_encoder = True
124262
self.vae_config.load_decoder = True
125263
self.dit_config.expand_timesteps = self.expand_timesteps
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
"""Lucy Edit video editing pipeline.
3+
4+
Lucy Edit uses a Wan2.2 5B transformer with an input video latent appended to
5+
the noisy latent channels. The stage topology is therefore closest to Wan V2V,
6+
but the model repo does not include CLIP image-encoder components.
7+
"""
8+
9+
from fastvideo.fastvideo_args import FastVideoArgs
10+
from fastvideo.logger import init_logger
11+
from fastvideo.pipelines.basic.wan.wan_v2v_pipeline import WanVideoToVideoPipeline
12+
from fastvideo.pipelines.stages import (
13+
ConditioningStage,
14+
DecodingStage,
15+
DenoisingStage,
16+
InputValidationStage,
17+
LatentPreparationStage,
18+
TextEncodingStage,
19+
TimestepPreparationStage,
20+
VideoVAEEncodingStage,
21+
)
22+
23+
logger = init_logger(__name__)
24+
25+
26+
class LucyEditPipeline(WanVideoToVideoPipeline):
27+
"""FastVideo pipeline for decart-ai/Lucy-Edit-Dev."""
28+
29+
_required_config_modules = [
30+
"text_encoder",
31+
"tokenizer",
32+
"vae",
33+
"transformer",
34+
"scheduler",
35+
]
36+
37+
def create_pipeline_stages(self, fastvideo_args: FastVideoArgs):
38+
self.add_stage(stage_name="input_validation_stage", stage=InputValidationStage())
39+
40+
self.add_stage(
41+
stage_name="prompt_encoding_stage",
42+
stage=TextEncodingStage(
43+
text_encoders=[self.get_module("text_encoder")],
44+
tokenizers=[self.get_module("tokenizer")],
45+
),
46+
)
47+
48+
self.add_stage(stage_name="conditioning_stage", stage=ConditioningStage())
49+
50+
self.add_stage(
51+
stage_name="timestep_preparation_stage",
52+
stage=TimestepPreparationStage(scheduler=self.get_module("scheduler")),
53+
)
54+
55+
self.add_stage(
56+
stage_name="latent_preparation_stage",
57+
stage=LatentPreparationStage(
58+
scheduler=self.get_module("scheduler"),
59+
transformer=self.get_module("transformer"),
60+
),
61+
)
62+
63+
self.add_stage(
64+
stage_name="video_latent_preparation_stage",
65+
stage=VideoVAEEncodingStage(vae=self.get_module("vae")),
66+
)
67+
68+
self.add_stage(
69+
stage_name="denoising_stage",
70+
stage=DenoisingStage(
71+
transformer=self.get_module("transformer"),
72+
transformer_2=self.get_module("transformer_2"),
73+
scheduler=self.get_module("scheduler"),
74+
),
75+
)
76+
77+
self.add_stage(stage_name="decoding_stage", stage=DecodingStage(vae=self.get_module("vae")))
78+
79+
80+
EntryClass = LucyEditPipeline

fastvideo/pipelines/basic/wan/presets.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,24 @@
268268
},
269269
)
270270

271+
LUCY_EDIT_DEV = InferencePreset(
272+
name="lucy_edit_dev",
273+
version=1,
274+
model_family="wan",
275+
description="Lucy Edit Dev 5B video editing",
276+
workload_type="t2v",
277+
stage_schemas=(_DENOISE_STAGE, ),
278+
defaults={
279+
"height": 480,
280+
"width": 832,
281+
"num_frames": 81,
282+
"fps": 24,
283+
"guidance_scale": 5.0,
284+
"num_inference_steps": 50,
285+
"negative_prompt": "",
286+
},
287+
)
288+
271289
# -------------------------------------------------------------------
272290
# Self-Forcing (causal) presets
273291
# -------------------------------------------------------------------
@@ -341,6 +359,7 @@
341359
FAST_WAN_T2V_480P,
342360
WAN_2_2_TI2V_5B,
343361
FAST_WAN_2_2_TI2V_5B,
362+
LUCY_EDIT_DEV,
344363
SF_WAN_T2V_1_3B,
345364
SF_WAN_2_2_T2V_A14B,
346365
SF_WAN_2_2_I2V_A14B,

0 commit comments

Comments
 (0)