linoyts HF Staff commited on
Commit
302a179
·
verified ·
1 Parent(s): 441fff1

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -33,3 +33,5 @@ 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/landscape_dry.mp4 filter=lfs diff=lfs merge=lfs -text
37
+ examples/man_dancing_dry.mp4 filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,13 +1,20 @@
1
  ---
2
- title: Ltx 2.3 Water Simulation
3
- emoji: 🦀
4
- colorFrom: red
5
- colorTo: yellow
6
  sdk: gradio
7
- sdk_version: 6.18.0
8
- python_version: '3.12'
9
  app_file: app.py
10
  pinned: false
 
 
 
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
1
  ---
2
+ title: LTX-2.3 Water Simulation
3
+ emoji: 🌊
4
+ colorFrom: blue
5
+ colorTo: green
6
  sdk: gradio
7
+ sdk_version: 6.13.0
8
+ python_version: "3.12"
9
  app_file: app.py
10
  pinned: false
11
+ hardware: zero-a10g
12
+ short_description: Add water VFX to video with an LTX-2.3 IC-LoRA
13
+ models:
14
+ - diffusers/LTX-2.3-Distilled-Diffusers
15
+ - ltx-community/LTX-2.3-loras
16
  ---
17
 
18
+ # 🌊 LTX-2.3 Water Simulation
19
+ Adds naturally-moving water (rivers, surf, rain, floods, splashes) to a dry clip while preserving subject,
20
+ framing and camera. IC-LoRA on distilled LTX-2.3 (`LTX2InContextPipeline`, 8-step, `ADD WATER` trigger, strength ~1.2).
app.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ os.environ.setdefault("TORCH_COMPILE_DISABLE", "1")
4
+ os.environ.setdefault("TORCHDYNAMO_DISABLE", "1")
5
+
6
+ import random
7
+ import tempfile
8
+
9
+ import numpy as np
10
+ import imageio.v3 as iio
11
+ import spaces
12
+ import torch
13
+ import gradio as gr
14
+ from PIL import Image, ImageOps
15
+ from huggingface_hub import hf_hub_download
16
+ from safetensors.torch import load_file
17
+
18
+ from diffusers import LTX2InContextPipeline
19
+ from diffusers.pipelines.ltx2.pipeline_ltx2_ic_lora import LTX2ReferenceCondition
20
+ from diffusers.pipelines.ltx2.utils import DISTILLED_SIGMA_VALUES
21
+ from diffusers.utils import load_video, encode_video
22
+
23
+ # --- Config -----------------------------------------------------------------
24
+ # Water-simulation IC-LoRA — distilled recipe (8 sigmas, CFG off), strength sweet-spot ~1.2.
25
+ BASE_MODEL = "diffusers/LTX-2.3-Distilled-Diffusers"
26
+ LORA_REPO = "ltx-community/LTX-2.3-loras"
27
+ LORA_FILE = "ltx-2.3-22b-ic-lora-water-simulation-0.9.safetensors"
28
+ LORA_SCALE = 1.2
29
+ FPS = 24
30
+ NUM_STEPS = len(DISTILLED_SIGMA_VALUES)
31
+ MAX_SEED = np.iinfo(np.int32).max
32
+ HF_TOKEN = os.environ.get("HF_TOKEN")
33
+
34
+ RES_PRESETS = {"Fast (768×448)": (768, 448), "Quality (960×544)": (960, 544)}
35
+ FRAME_CHOICES = [49, 73, 97, 121]
36
+
37
+ pipe = LTX2InContextPipeline.from_pretrained(BASE_MODEL, torch_dtype=torch.bfloat16)
38
+ pipe.to("cuda")
39
+ pipe.vae.enable_tiling()
40
+ _lora_path = hf_hub_download(LORA_REPO, LORA_FILE, token=HF_TOKEN)
41
+ pipe.load_lora_weights(load_file(_lora_path), adapter_name="water")
42
+ pipe.set_adapters("water", LORA_SCALE)
43
+
44
+
45
+ def _src_fps(path, default=FPS):
46
+ try:
47
+ return float(iio.immeta(path, plugin="pyav").get("fps", default)) or default
48
+ except Exception:
49
+ return default
50
+
51
+
52
+ def _load_frames(path, num_frames, width, height):
53
+ frames = load_video(path)
54
+ if not frames:
55
+ return []
56
+ fps = _src_fps(path)
57
+ out = []
58
+ for i in range(num_frames):
59
+ idx = min(int(round(i / FPS * fps)), len(frames) - 1)
60
+ out.append(ImageOps.fit(frames[idx].convert("RGB"), (width, height), Image.LANCZOS))
61
+ return out
62
+
63
+
64
+ def _pick_resolution(first_frame, preset):
65
+ w, h = RES_PRESETS[preset]
66
+ if first_frame.height > first_frame.width:
67
+ w, h = h, w
68
+ return w, h
69
+
70
+
71
+ def _build_prompt(prompt):
72
+ desc = prompt.strip() or "a flowing stream of clear water"
73
+ return (
74
+ "Reference shows the dry scene. Edited shows the same scene with water added. "
75
+ f"ADD WATER {desc}. "
76
+ "Subject identity, clothing, framing, and background geometry are identical to the reference; "
77
+ "only water-related elements differ between reference and edited."
78
+ )
79
+
80
+
81
+ def _export(video_np, audio, path):
82
+ kw = {}
83
+ if audio is not None:
84
+ kw = dict(audio=audio[0].float().cpu(), audio_sample_rate=pipe.vocoder.config.output_sampling_rate)
85
+ encode_video(video_np, fps=FPS, output_path=path, **kw)
86
+
87
+
88
+ def _duration(*args, **kwargs):
89
+ preset = next((a for a in args if isinstance(a, str) and a in RES_PRESETS), "Fast")
90
+ num_frames = next((a for a in args if isinstance(a, int) and a in FRAME_CHOICES), 73)
91
+ per_frame = 1.6 if "Quality" in str(preset) else 1.0
92
+ return int(70 + int(num_frames) * per_frame)
93
+
94
+
95
+ @spaces.GPU(duration=_duration)
96
+ def add_water(video, prompt, strength, preset, num_frames, seed, randomize,
97
+ progress=gr.Progress(track_tqdm=True)):
98
+ if video is None:
99
+ raise gr.Error("Please upload a 'dry' video to add water to.")
100
+ if randomize:
101
+ seed = random.randint(0, MAX_SEED)
102
+ seed = int(seed)
103
+ num_frames = int(num_frames)
104
+
105
+ probe = load_video(video)
106
+ if not probe:
107
+ raise gr.Error("Could not read any frames from that video.")
108
+ width, height = _pick_resolution(probe[0], preset)
109
+ ref = _load_frames(video, num_frames, width, height)
110
+ pipe.set_adapters("water", float(strength))
111
+ full_prompt = _build_prompt(prompt)
112
+
113
+ def _cb(p, i, t, kw):
114
+ progress((i + 1) / NUM_STEPS, desc=f"Adding water — step {i + 1}/{NUM_STEPS}")
115
+ return {}
116
+
117
+ video_out, audio_out = pipe(
118
+ prompt=full_prompt, negative_prompt="",
119
+ reference_conditions=[LTX2ReferenceCondition(frames=ref, strength=1.0)],
120
+ reference_downscale_factor=1,
121
+ width=width, height=height, num_frames=num_frames, frame_rate=FPS,
122
+ num_inference_steps=NUM_STEPS, sigmas=DISTILLED_SIGMA_VALUES,
123
+ guidance_scale=1.0, stg_scale=0.0, audio_guidance_scale=1.0, audio_stg_scale=0.0,
124
+ generator=torch.Generator(device="cuda").manual_seed(seed),
125
+ output_type="np", return_dict=False, callback_on_step_end=_cb,
126
+ )
127
+ out_path = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False).name
128
+ _export(video_out[0], audio_out, out_path)
129
+ return out_path, seed
130
+
131
+
132
+ with gr.Blocks(title="LTX-2.3 Water Simulation") as demo:
133
+ gr.Markdown(
134
+ "# 🌊 LTX-2.3 Water Simulation\n"
135
+ "Add believable, naturally-moving water to a dry clip — rivers, surf, rain, waterfalls, floods, "
136
+ "splashes — that interacts with the moving scene, while subject, clothing, framing and camera stay "
137
+ "exactly as shot. Describe the water (and any sounds) in one prompt; the trigger `ADD WATER` is added for you. "
138
+ "IC-LoRA: [`ltx-community/LTX-2.3-loras`](https://huggingface.co/ltx-community/LTX-2.3-loras) · base: distilled LTX-2.3."
139
+ )
140
+ with gr.Row():
141
+ with gr.Column():
142
+ video_in = gr.Video(label="Dry input video")
143
+ prompt = gr.Textbox(
144
+ label="Describe the water — type, motion, how it interacts, plus any sounds", lines=3,
145
+ placeholder="a clear shallow stream braiding around their legs with white foam crests and glistening wet ground; rushing water, gentle splashing",
146
+ )
147
+ with gr.Accordion("Settings", open=False):
148
+ strength = gr.Slider(1.0, 1.6, value=1.2, step=0.05,
149
+ label="Water strength (1.2–1.3 natural · 1.35+ hard surface→sea · ≥1.5 max drama)")
150
+ preset = gr.Dropdown(list(RES_PRESETS), value="Fast (768×448)", label="Resolution")
151
+ num_frames = gr.Dropdown(FRAME_CHOICES, value=73, label="Frames (24fps)")
152
+ randomize = gr.Checkbox(True, label="Randomize seed")
153
+ seed = gr.Slider(0, MAX_SEED, value=42, step=1, label="Seed")
154
+ run = gr.Button("Add water", variant="primary")
155
+ with gr.Column():
156
+ video_out = gr.Video(label="Result with water")
157
+ used_seed = gr.Number(label="Seed used", interactive=False)
158
+
159
+ run.click(add_water, inputs=[video_in, prompt, strength, preset, num_frames, seed, randomize],
160
+ outputs=[video_out, used_seed])
161
+
162
+ gr.Examples(
163
+ examples=[
164
+ ["examples/man_dancing_dry.mp4",
165
+ "a clear shallow stream rushing and braiding around their legs with white foam crests and glistening wet floor, splashing with each step; rushing water and rhythmic splashes",
166
+ 1.3, "Fast (768×448)", 73, 42, False],
167
+ ["examples/landscape_dry.mp4",
168
+ "a wide river flooding across the valley with rippling reflections and drifting foam, mist rising off the surface; flowing water and a distant waterfall",
169
+ 1.25, "Fast (768×448)", 73, 42, False],
170
+ ],
171
+ inputs=[video_in, prompt, strength, preset, num_frames, seed, randomize],
172
+ outputs=[video_out, used_seed], fn=add_water, cache_examples=True, cache_mode="lazy",
173
+ )
174
+
175
+ if __name__ == "__main__":
176
+ demo.launch(show_error=True)
examples/landscape_dry.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:52054b0a9af57517bbca8341a924a4cd941e9da07b2e4ee8287c808271a9d74d
3
+ size 349543
examples/man_dancing_dry.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a52822c2ebe58ed8e882fe7a80c29f8a3759d61f65ddb8da35062dedcf0b9387
3
+ size 292547
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ git+https://github.com/huggingface/diffusers
2
+ transformers
3
+ accelerate
4
+ peft
5
+ safetensors
6
+ sentencepiece
7
+ imageio
8
+ imageio-ffmpeg
9
+ av