linoyts HF Staff commited on
Commit
8efc0db
·
verified ·
1 Parent(s): 4cbdbb3

Upload folder using huggingface_hub

Browse files
Files changed (6) hide show
  1. .gitattributes +2 -0
  2. README.md +13 -6
  3. app.py +171 -0
  4. examples/man_laughing.mp4 +3 -0
  5. examples/man_sad.mp4 +3 -0
  6. requirements.txt +9 -0
.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/man_laughing.mp4 filter=lfs diff=lfs merge=lfs -text
37
+ examples/man_sad.mp4 filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,13 +1,20 @@
1
  ---
2
- title: Ltx 2.3 Beard Removal
3
- emoji: 🏆
4
- colorFrom: yellow
5
  colorTo: gray
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 Beard Removal
3
+ emoji: 🪒
4
+ colorFrom: blue
5
  colorTo: gray
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: Remove beards from video with an LTX-2.3 IC-LoRA
13
+ models:
14
+ - diffusers/LTX-2.3-Diffusers
15
+ - linoyts/LTX-2.3-loras
16
  ---
17
 
18
+ # 🪒 LTX-2.3 Beard Removal (Instant Shave)
19
+ Removes beard, mustache and stubble from a person in a video while preserving identity, expression and motion.
20
+ IC-LoRA on LTX-2.3 (`LTX2InContextPipeline`, 30 steps, guidance 4.0, STG, 25fps, `REMOVEBEARD` trigger).
app.py ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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.utils import load_video, encode_video
21
+
22
+ # --- Config -----------------------------------------------------------------
23
+ # Beard-removal IC-LoRA — non-distilled recipe: 30 steps, guidance 4.0, STG, 25 fps.
24
+ BASE_MODEL = "diffusers/LTX-2.3-Diffusers"
25
+ LORA_REPO = "linoyts/LTX-2.3-loras"
26
+ LORA_FILE = "ltx-2.3-22b-ic-lora-instant-shave-0.9.safetensors"
27
+ LORA_SCALE = 1.0
28
+ FPS = 25 # the card recommends 25 fps
29
+ NUM_STEPS = 30
30
+ GUIDANCE = 4.0
31
+ STG_BLOCKS = [29]
32
+ NEGATIVE = ("beard, mustache, facial hair, stubble, worst quality, "
33
+ "inconsistent motion, blurry, jittery, distorted")
34
+ MAX_SEED = np.iinfo(np.int32).max
35
+ HF_TOKEN = os.environ.get("HF_TOKEN")
36
+
37
+ RES_PRESETS = {"Fast (768×448)": (768, 448), "Quality (960×544)": (960, 544)}
38
+ FRAME_CHOICES = [33, 49, 73, 97, 121]
39
+
40
+ pipe = LTX2InContextPipeline.from_pretrained(BASE_MODEL, torch_dtype=torch.bfloat16)
41
+ pipe.to("cuda")
42
+ pipe.vae.enable_tiling()
43
+ _lora_path = hf_hub_download(LORA_REPO, LORA_FILE, token=HF_TOKEN)
44
+ pipe.load_lora_weights(load_file(_lora_path), adapter_name="shave")
45
+ pipe.set_adapters("shave", LORA_SCALE)
46
+
47
+
48
+ def _src_fps(path, default=FPS):
49
+ try:
50
+ return float(iio.immeta(path, plugin="pyav").get("fps", default)) or default
51
+ except Exception:
52
+ return default
53
+
54
+
55
+ def _load_frames(path, num_frames, width, height):
56
+ frames = load_video(path)
57
+ if not frames:
58
+ return []
59
+ fps = _src_fps(path)
60
+ out = []
61
+ for i in range(num_frames):
62
+ idx = min(int(round(i / FPS * fps)), len(frames) - 1)
63
+ out.append(ImageOps.fit(frames[idx].convert("RGB"), (width, height), Image.LANCZOS))
64
+ return out
65
+
66
+
67
+ def _pick_resolution(first_frame, preset):
68
+ w, h = RES_PRESETS[preset]
69
+ if first_frame.height > first_frame.width:
70
+ w, h = h, w
71
+ return w, h
72
+
73
+
74
+ def _build_prompt(prompt):
75
+ desc = prompt.strip() or "the same person, completely clean-shaven"
76
+ return (f"REMOVEBEARD {desc}, completely smooth and clean-shaven face, bare skin, "
77
+ f"no beard, no stubble, no facial hair; identity, expression, motion, lighting and scene unchanged.")
78
+
79
+
80
+ def _export(video_np, audio, path):
81
+ kw = {}
82
+ if audio is not None:
83
+ kw = dict(audio=audio[0].float().cpu(), audio_sample_rate=pipe.vocoder.config.output_sampling_rate)
84
+ encode_video(video_np, fps=FPS, output_path=path, **kw)
85
+
86
+
87
+ def _duration(*args, **kwargs):
88
+ preset = next((a for a in args if isinstance(a, str) and a in RES_PRESETS), "Fast")
89
+ num_frames = next((a for a in args if isinstance(a, int) and a in FRAME_CHOICES), 49)
90
+ per_frame = 4.2 if "Quality" in str(preset) else 3.0 # 30 steps + CFG + STG
91
+ return int(120 + int(num_frames) * per_frame)
92
+
93
+
94
+ @spaces.GPU(duration=_duration)
95
+ def shave(video, prompt, preset, num_frames, seed, randomize,
96
+ progress=gr.Progress(track_tqdm=True)):
97
+ if video is None:
98
+ raise gr.Error("Please upload a video of a bearded subject.")
99
+ if randomize:
100
+ seed = random.randint(0, MAX_SEED)
101
+ seed = int(seed)
102
+ num_frames = int(num_frames)
103
+
104
+ probe = load_video(video)
105
+ if not probe:
106
+ raise gr.Error("Could not read any frames from that video.")
107
+ width, height = _pick_resolution(probe[0], preset)
108
+ ref = _load_frames(video, num_frames, width, height)
109
+ full_prompt = _build_prompt(prompt)
110
+
111
+ def _cb(p, i, t, kw):
112
+ progress((i + 1) / NUM_STEPS, desc=f"Removing beard — step {i + 1}/{NUM_STEPS}")
113
+ return {}
114
+
115
+ video_out, audio_out = pipe(
116
+ prompt=full_prompt, negative_prompt=NEGATIVE,
117
+ reference_conditions=[LTX2ReferenceCondition(frames=ref, strength=1.0)],
118
+ reference_downscale_factor=1,
119
+ width=width, height=height, num_frames=num_frames, frame_rate=FPS,
120
+ num_inference_steps=NUM_STEPS, guidance_scale=GUIDANCE,
121
+ spatio_temporal_guidance_blocks=STG_BLOCKS,
122
+ generator=torch.Generator(device="cuda").manual_seed(seed),
123
+ output_type="np", return_dict=False, callback_on_step_end=_cb,
124
+ )
125
+ out_path = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False).name
126
+ _export(video_out[0], audio_out, out_path)
127
+ return out_path, seed
128
+
129
+
130
+ with gr.Blocks(title="LTX-2.3 Beard Removal") as demo:
131
+ gr.Markdown(
132
+ "# 🪒 LTX-2.3 Beard Removal (Instant Shave)\n"
133
+ "Removes beard, mustache, and stubble from a person in a video while preserving identity, expression, "
134
+ "motion, lighting, and scene. Optionally describe the clean-shaven look and any sounds in one prompt. "
135
+ "IC-LoRA: [`linoyts/LTX-2.3-loras`](https://huggingface.co/linoyts/LTX-2.3-loras) · base: LTX-2.3."
136
+ )
137
+ with gr.Row():
138
+ with gr.Column():
139
+ video_in = gr.Video(label="Video of a bearded subject")
140
+ prompt = gr.Textbox(
141
+ label="Prompt — describe the clean-shaven subject/scene and any sounds (optional)", lines=3,
142
+ placeholder="a man with a completely smooth clean-shaven face, warm indoor light, laughing; warm hearty laughter and quiet room tone",
143
+ )
144
+ with gr.Accordion("Settings", open=False):
145
+ preset = gr.Dropdown(list(RES_PRESETS), value="Fast (768×448)", label="Resolution")
146
+ num_frames = gr.Dropdown(FRAME_CHOICES, value=49, label="Frames (25fps)")
147
+ randomize = gr.Checkbox(True, label="Randomize seed")
148
+ seed = gr.Slider(0, MAX_SEED, value=42, step=1, label="Seed")
149
+ run = gr.Button("Remove beard", variant="primary")
150
+ with gr.Column():
151
+ video_out = gr.Video(label="Clean-shaven result")
152
+ used_seed = gr.Number(label="Seed used", interactive=False)
153
+
154
+ run.click(shave, inputs=[video_in, prompt, preset, num_frames, seed, randomize],
155
+ outputs=[video_out, used_seed])
156
+
157
+ gr.Examples(
158
+ examples=[
159
+ ["examples/man_laughing.mp4",
160
+ "a man with a completely smooth clean-shaven face, no beard or stubble, laughing warmly in soft indoor light; warm hearty laughter and a quiet room tone",
161
+ "Fast (768×448)", 49, 42, False],
162
+ ["examples/man_sad.mp4",
163
+ "a young man with a completely smooth clean-shaven face, no stubble, neutral expression, soft daylight; quiet ambient room tone",
164
+ "Fast (768×448)", 49, 42, False],
165
+ ],
166
+ inputs=[video_in, prompt, preset, num_frames, seed, randomize],
167
+ outputs=[video_out, used_seed], fn=shave, cache_examples=True, cache_mode="lazy",
168
+ )
169
+
170
+ if __name__ == "__main__":
171
+ demo.launch(show_error=True)
examples/man_laughing.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d6ae85ecf6f52e2a196102b81fb1b0f9f05eb47e817b85c7c51f783aaad891cc
3
+ size 216129
examples/man_sad.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d3858d2f9e01d7a426e64f8c129752e67cb0897d92141f79eb0b1b32b4e6fa89
3
+ size 230152
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