multimodalart HF Staff commited on
Commit
4cbb825
·
verified ·
1 Parent(s): 6f0010f

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -33,3 +33,7 @@ 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/bg_beach.jpg filter=lfs diff=lfs merge=lfs -text
37
+ examples/bg_tent.jpg filter=lfs diff=lfs merge=lfs -text
38
+ examples/obj_cake.jpg filter=lfs diff=lfs merge=lfs -text
39
+ examples/obj_dog.jpg filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,13 +1,38 @@
1
  ---
2
- title: Direct Object Insertion
3
- emoji: 🏢
4
- colorFrom: purple
5
- colorTo: purple
6
  sdk: gradio
7
  sdk_version: 6.19.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: DIRECT 3D-Aware Object Insertion
3
+ emoji: 🪑
4
+ colorFrom: gray
5
+ colorTo: red
6
  sdk: gradio
7
  sdk_version: 6.19.0
 
8
  app_file: app.py
9
+ short_description: 3D-aware object insertion with the DIRECT model (ICML 2026)
10
+ python_version: "3.10"
11
+ startup_duration_timeout: 1h
12
  pinned: false
13
  ---
14
 
15
+ # DIRECT: Direct 3D-Aware Object Insertion via Decomposed Visual Proxies
16
+
17
+ Insert a reference object into a background scene with realistic, harmonized
18
+ results using the [DIRECT](https://huggingface.co/superGong/DIRECT) model
19
+ (ICML 2026), a FLUX.1-Fill-dev network guided by a decomposed visual proxy.
20
+
21
+ Upload a background image and an object image (its background is removed
22
+ automatically), choose where and how large to place the object, then click
23
+ **Insert**.
24
+
25
+ ## Note on scope
26
+
27
+ The full paper uses an interactive 3D viewer (TRELLIS + Viser) so users can pose
28
+ a reconstructed 3D proxy of the object. That live 3D websocket viewer cannot run
29
+ inside a single-port Hugging Face Space, so this demo drives the same DIRECT
30
+ model with a simpler **2D placement** (position + scale) as its geometric
31
+ guidance. The insertion / harmonization is performed by the real DIRECT weights.
32
+
33
+ ## Links
34
+
35
+ - Paper: https://arxiv.org/abs/2606.06601
36
+ - Project page: https://gong1130.github.io/DIRECT/
37
+ - Code: https://github.com/Gong1130/DIRECT
38
+ - Model weights: https://huggingface.co/superGong/DIRECT
app.py ADDED
@@ -0,0 +1,417 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
4
+
5
+ import spaces # noqa: E402 (must come before torch / CUDA-touching imports)
6
+ import math
7
+ import time
8
+ import random
9
+
10
+ import numpy as np
11
+ import torch
12
+ import gradio as gr
13
+ from PIL import Image
14
+
15
+ from direct import DirectPipeline
16
+
17
+ # ----------------------------------------------------------------------------
18
+ # Config
19
+ # ----------------------------------------------------------------------------
20
+ MODEL_INPUT_RESOLUTION = 1024
21
+ DIRECT_MODEL_PATH = "superGong/DIRECT"
22
+ FLUX_MODEL_PATH = "black-forest-labs/FLUX.1-Fill-dev"
23
+ SIGLIP_MODEL_PATH = "google/siglip2-so400m-patch14-384"
24
+
25
+ HF_TOKEN = os.environ.get("HF_TOKEN")
26
+
27
+ # ----------------------------------------------------------------------------
28
+ # Load models at module scope (ZeroGPU packs weights to disk after this)
29
+ # ----------------------------------------------------------------------------
30
+ print("Loading DIRECT pipeline (FLUX.1-Fill-dev + SigLIP2 + DIRECT adapters)...")
31
+ direct_pipeline = DirectPipeline.from_pretrained(
32
+ direct_model_path=DIRECT_MODEL_PATH,
33
+ flux_model_path=FLUX_MODEL_PATH,
34
+ siglip_model_path=SIGLIP_MODEL_PATH,
35
+ device=torch.device("cuda"),
36
+ torch_dtype=torch.bfloat16,
37
+ token=HF_TOKEN,
38
+ )
39
+ print("DIRECT pipeline loaded.")
40
+
41
+ # Background remover for the object image (ungated). Loaded lazily/cheaply.
42
+ _rembg_session = None
43
+
44
+
45
+ def _get_rembg_session():
46
+ global _rembg_session
47
+ if _rembg_session is None:
48
+ from rembg import new_session
49
+
50
+ _rembg_session = new_session("u2net")
51
+ return _rembg_session
52
+
53
+
54
+ # ----------------------------------------------------------------------------
55
+ # Image-preparation helpers (2D proxy construction).
56
+ #
57
+ # The full DIRECT paper uses an interactive 3D viewer (TRELLIS + Viser) to let
58
+ # users pose a reconstructed 3D proxy of the object. That live 3D websocket
59
+ # viewer cannot run inside a single-port HF Space, so here we build the model's
60
+ # geometric-guidance inputs from a simple 2D placement (position + scale). The
61
+ # underlying DIRECT model (real weights) then performs the 3D-aware harmonized
62
+ # insertion. See the notes in the UI for this limitation.
63
+ # ----------------------------------------------------------------------------
64
+
65
+ def segment_object(object_rgb: Image.Image) -> Image.Image:
66
+ """Return an RGBA image of the object with background removed."""
67
+ from rembg import remove
68
+
69
+ rgba = remove(object_rgb.convert("RGB"), session=_get_rembg_session())
70
+ return rgba.convert("RGBA")
71
+
72
+
73
+ def _tight_crop_rgba(rgba: Image.Image) -> Image.Image:
74
+ alpha = np.array(rgba.split()[-1])
75
+ ys, xs = np.where(alpha > 10)
76
+ if ys.size == 0:
77
+ return rgba
78
+ y1, y2, x1, x2 = ys.min(), ys.max() + 1, xs.min(), xs.max() + 1
79
+ return rgba.crop((x1, y1, x2, y2))
80
+
81
+
82
+ def center_reference(rgba: Image.Image, out_size: int = MODEL_INPUT_RESOLUTION) -> Image.Image:
83
+ """Object centered on black, square, with ~1.2 margin (model reference input)."""
84
+ obj = _tight_crop_rgba(rgba)
85
+ w, h = obj.size
86
+ side = max(int(math.ceil(max(w, h) * 1.2)), 1)
87
+ canvas = Image.new("RGB", (side, side), (0, 0, 0))
88
+ canvas.paste(obj, ((side - w) // 2, (side - h) // 2), obj)
89
+ return canvas.resize((out_size, out_size), Image.LANCZOS)
90
+
91
+
92
+ def place_object(bg: Image.Image, obj_rgba: Image.Image, cx: float, cy: float, scale: float):
93
+ """Paste the (tight-cropped) object onto a copy of the background.
94
+
95
+ cx, cy in [0, 1] (center), scale in [0, 1] (object longest side as a
96
+ fraction of the background's longest side). Returns (placed_rgb, mask_L).
97
+ """
98
+ bg = bg.convert("RGB")
99
+ W, H = bg.size
100
+ obj = _tight_crop_rgba(obj_rgba)
101
+ ow, oh = obj.size
102
+ target_long = max(1, int(scale * max(W, H)))
103
+ ratio = target_long / max(ow, oh)
104
+ new_w = max(1, int(ow * ratio))
105
+ new_h = max(1, int(oh * ratio))
106
+ obj_r = obj.resize((new_w, new_h), Image.LANCZOS)
107
+
108
+ center_x = int(cx * W)
109
+ center_y = int(cy * H)
110
+ x0 = center_x - new_w // 2
111
+ y0 = center_y - new_h // 2
112
+
113
+ placed_rgb = bg.copy()
114
+ placed_rgb.paste(obj_r, (x0, y0), obj_r)
115
+
116
+ mask = Image.new("L", (W, H), 0)
117
+ obj_alpha = obj_r.split()[-1]
118
+ mask.paste(obj_alpha, (x0, y0), obj_alpha)
119
+
120
+ # Geometry proxy: the object RGB on a black canvas at its placed location.
121
+ geometry_full = Image.new("RGB", (W, H), (0, 0, 0))
122
+ geometry_full.paste(obj_r, (x0, y0), obj_r)
123
+
124
+ return placed_rgb, mask, geometry_full
125
+
126
+
127
+ def get_mask_bbox(mask_pil, threshold=20):
128
+ arr = np.array(mask_pil)
129
+ ys, xs = np.where(arr > threshold)
130
+ if ys.size == 0:
131
+ return None
132
+ return (xs.min(), ys.min(), xs.max() + 1, ys.max() + 1)
133
+
134
+
135
+ def get_smart_crop_bbox(mask_pil, min_ratio=0.02, max_ratio=0.3):
136
+ bbox = get_mask_bbox(mask_pil)
137
+ if bbox is None:
138
+ s = MODEL_INPUT_RESOLUTION
139
+ return (0, 0, s, s), s
140
+ min_x, min_y, max_x, max_y = bbox
141
+ mask_w, mask_h = max_x - min_x, max_y - min_y
142
+ area = mask_w * mask_h
143
+ side = int(math.sqrt(area / ((min_ratio + max_ratio) / 2.0)))
144
+ side = max(side, max(mask_w, mask_h) + 40)
145
+ cx = (min_x + max_x) // 2
146
+ cy = (min_y + max_y) // 2
147
+ half = side // 2
148
+ return (cx - half, cy - half, cx - half + side, cy - half + side), side
149
+
150
+
151
+ def crop_and_pad(image, bbox, target_side):
152
+ x1, y1, x2, y2 = bbox
153
+ W, H = image.size
154
+ valid = image.crop((max(0, x1), max(0, y1), min(W, x2), min(H, y2)))
155
+ canvas = Image.new(image.mode, (target_side, target_side), 0)
156
+ canvas.paste(valid, (max(0, -x1), max(0, -y1)))
157
+ return canvas
158
+
159
+
160
+ def dilate_mask(mask_np, radius=10):
161
+ import cv2
162
+
163
+ m = (mask_np > 0).astype(np.uint8) * 255
164
+ k = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (radius * 2 + 1, radius * 2 + 1))
165
+ return (cv2.dilate(m, k, iterations=1) > 0).astype(np.uint8)
166
+
167
+
168
+ def refine_mask_holes(mask_bool, kernel_size=7):
169
+ import cv2
170
+
171
+ m = mask_bool.astype(np.uint8) * 255
172
+ k = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernel_size, kernel_size))
173
+ closed = cv2.morphologyEx(m, cv2.MORPH_CLOSE, k, iterations=2)
174
+ contours, _ = cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
175
+ filled = np.zeros_like(closed)
176
+ cv2.drawContours(filled, contours, -1, 255, thickness=cv2.FILLED)
177
+ return filled > 127
178
+
179
+
180
+ def adain_color_fix(target_pil, source_pil, mask_pil):
181
+ from torchvision.transforms import ToPILImage, ToTensor
182
+
183
+ to_tensor = ToTensor()
184
+ t = to_tensor(target_pil).unsqueeze(0)
185
+ s = to_tensor(source_pil).unsqueeze(0)
186
+ m = to_tensor(mask_pil).unsqueeze(0)
187
+ eps = 1e-5
188
+ res = t.clone()
189
+ for ch in range(3):
190
+ bg_idx = m[0, 0] < 0.1
191
+ if bg_idx.sum() < 10:
192
+ continue
193
+ s_pix = s[0, ch][bg_idx]
194
+ t_pix = t[0, ch][bg_idx]
195
+ s_mean, s_std = s_pix.mean(), s_pix.std() + eps
196
+ t_mean, t_std = t_pix.mean(), t_pix.std() + eps
197
+ res[0, ch] = (t[0, ch] - t_mean) * (s_std / t_std) + s_mean
198
+ return ToPILImage()(res.squeeze(0).clamp(0, 1))
199
+
200
+
201
+ def build_inputs(bg_pil, composite_full, mask_full, reference_ref, geometry_full):
202
+ """Produce the model's 1024x1024 conditioning tensors from full-frame inputs."""
203
+ target_res = MODEL_INPUT_RESOLUTION
204
+
205
+ mask_np = np.array(mask_full)
206
+ dilated01 = dilate_mask(mask_np, radius=10)
207
+ dilated_pil = Image.fromarray(dilated01 * 255, mode="L")
208
+
209
+ # Context image: full background with the (dilated) insertion region blacked.
210
+ full_bg = np.array(bg_pil.convert("RGB"))
211
+ context_image = Image.fromarray((full_bg * (1 - dilated01[:, :, None])).astype(np.uint8))
212
+
213
+ ideal_bbox, target_side = get_smart_crop_bbox(dilated_pil)
214
+
215
+ patch_composite = crop_and_pad(composite_full, ideal_bbox, target_side)
216
+ patch_mask = crop_and_pad(dilated_pil, ideal_bbox, target_side)
217
+ patch_geometry = crop_and_pad(geometry_full, ideal_bbox, target_side)
218
+ patch_bg_ref = crop_and_pad(bg_pil.convert("RGB"), ideal_bbox, target_side)
219
+ patch_mask_orig = crop_and_pad(Image.fromarray(mask_np), ideal_bbox, target_side)
220
+
221
+ comp_arr = np.array(patch_composite)
222
+ mask_dilated_arr = np.array(patch_mask) > 127
223
+ mask_orig_arr = refine_mask_holes(np.array(patch_mask_orig) > 127, kernel_size=7)
224
+ diff_region = mask_dilated_arr & (~mask_orig_arr)
225
+ comp_arr[diff_region] = [0, 0, 0]
226
+ patch_composite = Image.fromarray(comp_arr)
227
+
228
+ composite_image = patch_composite.resize((target_res, target_res), Image.LANCZOS)
229
+ model_input_mask = Image.fromarray(np.array(patch_mask).astype(np.uint8)).resize(
230
+ (target_res, target_res), Image.NEAREST
231
+ )
232
+ geometry_image = patch_geometry.resize((target_res, target_res), Image.LANCZOS)
233
+ background_reference_image = patch_bg_ref.resize((target_res, target_res), Image.LANCZOS)
234
+
235
+ inpaint_mask = Image.fromarray(((np.array(model_input_mask) > 0) * 255).astype(np.uint8))
236
+
237
+ return {
238
+ "composite_image": composite_image,
239
+ "inpaint_mask": inpaint_mask,
240
+ "reference_image": reference_ref,
241
+ "geometry_image": geometry_image,
242
+ "context_image": context_image,
243
+ "model_input_mask": model_input_mask,
244
+ "background_reference_image": background_reference_image,
245
+ "ideal_bbox": ideal_bbox,
246
+ "target_side": target_side,
247
+ }
248
+
249
+
250
+ def paste_back(bg_pil, generated_patch, inp):
251
+ fixed = adain_color_fix(
252
+ generated_patch, inp["background_reference_image"], inp["model_input_mask"]
253
+ )
254
+ fixed = fixed.resize((inp["target_side"], inp["target_side"]), Image.LANCZOS)
255
+ x1, y1, x2, y2 = inp["ideal_bbox"]
256
+ W, H = bg_pil.size
257
+ pad_left = max(0, -x1)
258
+ pad_top = max(0, -y1)
259
+ valid_w = min(W, x2) - max(0, x1)
260
+ valid_h = min(H, y2) - max(0, y1)
261
+ patch_valid = fixed.crop((pad_left, pad_top, pad_left + valid_w, pad_top + valid_h))
262
+ out = bg_pil.convert("RGB").copy()
263
+ out.paste(patch_valid, (max(0, x1), max(0, y1)))
264
+ return out
265
+
266
+
267
+ # ----------------------------------------------------------------------------
268
+ # Inference
269
+ # ----------------------------------------------------------------------------
270
+
271
+ def _estimate_duration(bg, obj, cx, cy, scale, seed, ref_scale, steps, *a, **k):
272
+ try:
273
+ steps = int(steps)
274
+ except Exception:
275
+ steps = 28
276
+ return min(230, 60 + int(steps * 4.5))
277
+
278
+
279
+ @spaces.GPU(duration=_estimate_duration)
280
+ def insert_object(
281
+ bg: Image.Image,
282
+ obj: Image.Image,
283
+ cx: float,
284
+ cy: float,
285
+ scale: float,
286
+ seed: int,
287
+ ref_scale: float,
288
+ steps: int,
289
+ progress=gr.Progress(track_tqdm=True),
290
+ ):
291
+ """Insert a reference object into a background image with 3D-aware harmonization.
292
+
293
+ Args:
294
+ bg: Background scene image.
295
+ obj: Reference object image (background is removed automatically).
296
+ cx: Horizontal placement of the object center (0=left, 1=right).
297
+ cy: Vertical placement of the object center (0=top, 1=bottom).
298
+ scale: Object size as a fraction of the background's longest side.
299
+ seed: Random seed for reproducibility.
300
+ ref_scale: Reference guidance scale (identity preservation strength).
301
+ steps: Number of inference steps.
302
+
303
+ Returns:
304
+ The composited image with the object inserted, and a preview of the raw
305
+ 2D placement used as geometric guidance.
306
+ """
307
+ if bg is None:
308
+ raise gr.Error("Please provide a background image.")
309
+ if obj is None:
310
+ raise gr.Error("Please provide an object image.")
311
+
312
+ t0 = time.perf_counter()
313
+ bg = bg.convert("RGB")
314
+ obj_rgba = segment_object(obj)
315
+
316
+ reference_ref = center_reference(obj_rgba, out_size=MODEL_INPUT_RESOLUTION)
317
+ placed_rgb, mask_full, geometry_full = place_object(bg, obj_rgba, cx, cy, scale)
318
+
319
+ inp = build_inputs(bg, placed_rgb, mask_full, reference_ref, geometry_full)
320
+
321
+ seed = int(seed)
322
+ final_images = direct_pipeline(
323
+ composite_image=inp["composite_image"],
324
+ inpaint_mask=inp["inpaint_mask"],
325
+ reference_image=inp["reference_image"],
326
+ geometry_image=inp["geometry_image"],
327
+ context_image=inp["context_image"],
328
+ seed=seed,
329
+ guidance_scale=30,
330
+ num_inference_steps=int(steps),
331
+ height=MODEL_INPUT_RESOLUTION,
332
+ width=MODEL_INPUT_RESOLUTION,
333
+ use_autocast=True,
334
+ reference_guidance_scale=float(ref_scale),
335
+ )
336
+ generated_patch = final_images[0]
337
+ result = paste_back(bg, generated_patch, inp)
338
+ print(f"[insert_object] done in {time.perf_counter() - t0:.1f}s (steps={steps})")
339
+ return result, placed_rgb
340
+
341
+
342
+ def randomize_seed():
343
+ return random.randint(0, 2**31 - 1)
344
+
345
+
346
+ # ----------------------------------------------------------------------------
347
+ # UI
348
+ # ----------------------------------------------------------------------------
349
+ CSS = """
350
+ #col-container { max-width: 1200px; margin: 0 auto; }
351
+ .dark .gradio-container { color: var(--body-text-color); }
352
+ """
353
+
354
+ INTRO = """
355
+ # DIRECT: 3D-Aware Object Insertion
356
+
357
+ Insert a reference **object** into a **background** scene with realistic,
358
+ harmonized results, powered by the [DIRECT](https://huggingface.co/superGong/DIRECT)
359
+ model (ICML 2026) — a FLUX.1-Fill-dev network guided by a decomposed visual proxy.
360
+
361
+ **How to use:** upload a background and an object image (its background is
362
+ removed automatically), choose *where* and *how big* to place it, then click **Insert**.
363
+
364
+ > **Note.** The full paper uses an interactive 3D viewer (TRELLIS + Viser) to pose a
365
+ > reconstructed 3D proxy of the object. That live 3D viewer cannot run inside a
366
+ > single-port Space, so this demo drives the same DIRECT model with a simpler
367
+ > **2D placement** (position + scale) as its geometric guidance.
368
+
369
+ [Paper](https://arxiv.org/abs/2606.06601) · [Project page](https://gong1130.github.io/DIRECT/) · [Code](https://github.com/Gong1130/DIRECT)
370
+ """
371
+
372
+ with gr.Blocks(theme=gr.themes.Citrus(), css=CSS) as demo:
373
+ with gr.Column(elem_id="col-container"):
374
+ gr.Markdown(INTRO)
375
+ with gr.Row():
376
+ with gr.Column(scale=1):
377
+ bg_input = gr.Image(label="Background image", type="pil", height=300)
378
+ obj_input = gr.Image(label="Object image", type="pil", height=300)
379
+ run_btn = gr.Button("Insert", variant="primary")
380
+ with gr.Column(scale=1):
381
+ out_result = gr.Image(label="Inserted result", type="pil", height=360)
382
+ out_preview = gr.Image(label="2D placement (geometric guidance)", type="pil", height=240)
383
+
384
+ with gr.Accordion("Placement & advanced settings", open=True):
385
+ with gr.Row():
386
+ cx = gr.Slider(0.0, 1.0, value=0.5, step=0.01, label="Horizontal position")
387
+ cy = gr.Slider(0.0, 1.0, value=0.6, step=0.01, label="Vertical position")
388
+ scale = gr.Slider(0.05, 0.9, value=0.35, step=0.01, label="Object size")
389
+ with gr.Row():
390
+ ref_scale = gr.Slider(1.0, 5.0, value=2.0, step=0.1, label="Reference guidance scale")
391
+ steps = gr.Slider(15, 50, value=28, step=1, label="Inference steps")
392
+ seed = gr.Number(label="Seed", value=42, precision=0)
393
+ rand_btn = gr.Button("🎲 Randomize seed")
394
+
395
+ gr.Examples(
396
+ examples=[
397
+ ["examples/bg_landscape.jpg", "examples/obj_ducks.jpg", 0.55, 0.70, 0.28, 42, 2.0, 28],
398
+ ["examples/bg_tent.jpg", "examples/obj_dog.jpg", 0.45, 0.68, 0.30, 7, 2.0, 28],
399
+ ["examples/bg_beach.jpg", "examples/obj_cake.jpg", 0.50, 0.72, 0.22, 123, 2.5, 30],
400
+ ],
401
+ inputs=[bg_input, obj_input, cx, cy, scale, seed, ref_scale, steps],
402
+ outputs=[out_result, out_preview],
403
+ fn=insert_object,
404
+ cache_examples=True,
405
+ cache_mode="lazy",
406
+ )
407
+
408
+ rand_btn.click(fn=randomize_seed, outputs=seed)
409
+ run_btn.click(
410
+ fn=insert_object,
411
+ inputs=[bg_input, obj_input, cx, cy, scale, seed, ref_scale, steps],
412
+ outputs=[out_result, out_preview],
413
+ api_name="insert",
414
+ )
415
+
416
+ if __name__ == "__main__":
417
+ demo.launch(mcp_server=True)
direct/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from .pipeline import DirectPipeline
2
+
3
+ __all__ = ["DirectPipeline"]
direct/layers.py ADDED
@@ -0,0 +1,410 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional, Union
2
+
3
+ import torch
4
+ import torch.nn.functional as F
5
+ from torch import nn
6
+
7
+ from diffusers.models.attention_processor import Attention
8
+
9
+
10
+ class LoRALinearLayer(nn.Module):
11
+ def __init__(
12
+ self,
13
+ in_features: int,
14
+ out_features: int,
15
+ rank: int = 4,
16
+ network_alpha: Optional[float] = None,
17
+ device: Optional[Union[torch.device, str]] = None,
18
+ dtype: Optional[torch.dtype] = None,
19
+ number=0,
20
+ n_loras=1,
21
+ ):
22
+ super().__init__()
23
+ self.down = nn.Linear(in_features, rank, bias=False, device=device, dtype=dtype)
24
+ self.up = nn.Linear(rank, out_features, bias=False, device=device, dtype=dtype)
25
+ # This value has the same meaning as the `--network_alpha` option in the kohya-ss trainer script.
26
+ # See https://github.com/darkstorm2150/sd-scripts/blob/main/docs/train_network_README-en.md#execute-learning
27
+ self.network_alpha = network_alpha
28
+ self.rank = rank
29
+ self.out_features = out_features
30
+ self.in_features = in_features
31
+
32
+ nn.init.normal_(self.down.weight, std=1 / rank)
33
+ nn.init.zeros_(self.up.weight)
34
+
35
+ self.number = number
36
+ self.n_loras = n_loras
37
+
38
+ def forward(self, hidden_states: torch.Tensor, cond_seq_len: int = None) -> torch.Tensor:
39
+ orig_dtype = hidden_states.dtype
40
+ dtype = self.down.weight.dtype
41
+
42
+ batch_size = hidden_states.shape[0]
43
+ cond_size = cond_seq_len
44
+
45
+ block_size = hidden_states.shape[1] - cond_size * self.n_loras
46
+ shape = (batch_size, hidden_states.shape[1], 3072)
47
+ mask = torch.ones(shape, device=hidden_states.device, dtype=dtype)
48
+ mask[:, : block_size + self.number * cond_size, :] = 0
49
+ mask[:, block_size + (self.number + 1) * cond_size :, :] = 0
50
+ hidden_states = mask * hidden_states
51
+
52
+ down_hidden_states = self.down(hidden_states.to(dtype))
53
+ up_hidden_states = self.up(down_hidden_states)
54
+
55
+ if self.network_alpha is not None:
56
+ up_hidden_states *= self.network_alpha / self.rank
57
+
58
+ return up_hidden_states.to(orig_dtype)
59
+
60
+
61
+ class TextLoRALinearLayer(nn.Module):
62
+ def __init__(
63
+ self,
64
+ in_features: int,
65
+ out_features: int,
66
+ rank: int = 4,
67
+ network_alpha: Optional[float] = None,
68
+ device: Optional[Union[torch.device, str]] = None,
69
+ dtype: Optional[torch.dtype] = None,
70
+ token_length=512,
71
+ ):
72
+ super().__init__()
73
+ self.down = nn.Linear(in_features, rank, bias=False, device=device, dtype=dtype)
74
+ self.up = nn.Linear(rank, out_features, bias=False, device=device, dtype=dtype)
75
+ # This value has the same meaning as the `--network_alpha` option in the kohya-ss trainer script.
76
+ # See https://github.com/darkstorm2150/sd-scripts/blob/main/docs/train_network_README-en.md#execute-learning
77
+ self.network_alpha = network_alpha
78
+ self.rank = rank
79
+ self.out_features = out_features
80
+ self.in_features = in_features
81
+
82
+ nn.init.normal_(self.down.weight, std=1 / rank)
83
+ nn.init.zeros_(self.up.weight)
84
+
85
+ self.token_length = token_length
86
+
87
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
88
+ orig_dtype = hidden_states.dtype
89
+ dtype = self.down.weight.dtype
90
+
91
+ batch_size, seq_len, feature_dim = hidden_states.shape
92
+ if seq_len > self.token_length:
93
+ mask = torch.ones((batch_size, seq_len, feature_dim), device=hidden_states.device, dtype=dtype)
94
+ mask[:, self.token_length :, :] = 0
95
+ hidden_states = mask * hidden_states
96
+
97
+ down_hidden_states = self.down(hidden_states.to(dtype))
98
+ up_hidden_states = self.up(down_hidden_states)
99
+
100
+ if self.network_alpha is not None:
101
+ up_hidden_states *= self.network_alpha / self.rank
102
+
103
+ return up_hidden_states.to(orig_dtype)
104
+
105
+
106
+ class MultiSingleStreamBlockLoraProcessor(nn.Module):
107
+ def __init__(
108
+ self,
109
+ dim: int,
110
+ ranks=[],
111
+ lora_weights=[],
112
+ network_alphas=[],
113
+ device=None,
114
+ dtype=None,
115
+ n_loras=1,
116
+ text_lora_config=None,
117
+ ):
118
+ super().__init__()
119
+ self.n_loras = n_loras
120
+ if text_lora_config is not None:
121
+ self.text_len = text_lora_config.get("token_length", 512)
122
+ else:
123
+ self.text_len = 512
124
+
125
+ self.q_loras = nn.ModuleList(
126
+ [
127
+ LoRALinearLayer(
128
+ dim, dim, ranks[i], network_alphas[i], device=device, dtype=dtype, number=i, n_loras=n_loras
129
+ )
130
+ for i in range(n_loras)
131
+ ]
132
+ )
133
+ self.k_loras = nn.ModuleList(
134
+ [
135
+ LoRALinearLayer(
136
+ dim, dim, ranks[i], network_alphas[i], device=device, dtype=dtype, number=i, n_loras=n_loras
137
+ )
138
+ for i in range(n_loras)
139
+ ]
140
+ )
141
+ self.v_loras = nn.ModuleList(
142
+ [
143
+ LoRALinearLayer(
144
+ dim, dim, ranks[i], network_alphas[i], device=device, dtype=dtype, number=i, n_loras=n_loras
145
+ )
146
+ for i in range(n_loras)
147
+ ]
148
+ )
149
+ self.lora_weights = lora_weights
150
+
151
+ if text_lora_config is not None:
152
+ t_rank = text_lora_config.get("rank", 4)
153
+ t_alpha = text_lora_config.get("alpha", None)
154
+
155
+ self.text_q_lora = TextLoRALinearLayer(
156
+ dim, dim, t_rank, t_alpha, device=device, dtype=dtype, token_length=self.text_len
157
+ )
158
+ self.text_k_lora = TextLoRALinearLayer(
159
+ dim, dim, t_rank, t_alpha, device=device, dtype=dtype, token_length=self.text_len
160
+ )
161
+ self.text_v_lora = TextLoRALinearLayer(
162
+ dim, dim, t_rank, t_alpha, device=device, dtype=dtype, token_length=self.text_len
163
+ )
164
+
165
+ def __call__(
166
+ self,
167
+ attn: Attention,
168
+ hidden_states: torch.FloatTensor,
169
+ encoder_hidden_states: torch.FloatTensor = None,
170
+ attention_mask: Optional[torch.FloatTensor] = None,
171
+ image_rotary_emb: Optional[torch.Tensor] = None,
172
+ use_cond=False,
173
+ ) -> torch.FloatTensor:
174
+ batch_size, seq_len, _ = hidden_states.shape
175
+
176
+ total_img_seq_len = seq_len - self.text_len
177
+ assert total_img_seq_len % (1 + self.n_loras) == 0, (
178
+ f"total_img_seq_len:{total_img_seq_len}, n_loras:{self.n_loras}, "
179
+ f"seq_len:{seq_len}, text_len:{self.text_len}"
180
+ )
181
+ cond_seq_len = total_img_seq_len // (1 + self.n_loras)
182
+
183
+ query = attn.to_q(hidden_states)
184
+ key = attn.to_k(hidden_states)
185
+ value = attn.to_v(hidden_states)
186
+
187
+ for i in range(self.n_loras):
188
+ query = query + self.lora_weights[i] * self.q_loras[i](hidden_states, cond_seq_len=cond_seq_len)
189
+ key = key + self.lora_weights[i] * self.k_loras[i](hidden_states, cond_seq_len=cond_seq_len)
190
+ value = value + self.lora_weights[i] * self.v_loras[i](hidden_states, cond_seq_len=cond_seq_len)
191
+
192
+ if getattr(self, "text_q_lora", None) is not None:
193
+ query = query + self.text_q_lora(hidden_states)
194
+ if getattr(self, "text_k_lora", None) is not None:
195
+ key = key + self.text_k_lora(hidden_states)
196
+ if getattr(self, "text_v_lora", None) is not None:
197
+ value = value + self.text_v_lora(hidden_states)
198
+
199
+ inner_dim = key.shape[-1]
200
+ head_dim = inner_dim // attn.heads
201
+
202
+ query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
203
+ key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
204
+ value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
205
+
206
+ if attn.norm_q is not None:
207
+ query = attn.norm_q(query)
208
+ if attn.norm_k is not None:
209
+ key = attn.norm_k(key)
210
+
211
+ if image_rotary_emb is not None:
212
+ from diffusers.models.embeddings import apply_rotary_emb
213
+
214
+ query = apply_rotary_emb(query, image_rotary_emb)
215
+ key = apply_rotary_emb(key, image_rotary_emb)
216
+
217
+ cond_size = cond_seq_len
218
+ block_size = hidden_states.shape[1] - cond_size * self.n_loras
219
+
220
+ hidden_states = F.scaled_dot_product_attention(query, key, value, dropout_p=0.0, is_causal=False)
221
+
222
+ hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim)
223
+ hidden_states = hidden_states.to(query.dtype)
224
+
225
+ cond_hidden_states = hidden_states[:, block_size:, :]
226
+ hidden_states = hidden_states[:, :block_size, :]
227
+
228
+ return hidden_states if not use_cond else (hidden_states, cond_hidden_states)
229
+
230
+
231
+ class MultiDoubleStreamBlockLoraProcessor(nn.Module):
232
+ def __init__(
233
+ self,
234
+ dim: int,
235
+ ranks=[],
236
+ lora_weights=[],
237
+ network_alphas=[],
238
+ device=None,
239
+ dtype=None,
240
+ n_loras=1,
241
+ text_lora_config=None,
242
+ ):
243
+ super().__init__()
244
+ self.n_loras = n_loras
245
+ self.q_loras = nn.ModuleList(
246
+ [
247
+ LoRALinearLayer(
248
+ dim, dim, ranks[i], network_alphas[i], device=device, dtype=dtype, number=i, n_loras=n_loras
249
+ )
250
+ for i in range(n_loras)
251
+ ]
252
+ )
253
+ self.k_loras = nn.ModuleList(
254
+ [
255
+ LoRALinearLayer(
256
+ dim, dim, ranks[i], network_alphas[i], device=device, dtype=dtype, number=i, n_loras=n_loras
257
+ )
258
+ for i in range(n_loras)
259
+ ]
260
+ )
261
+ self.v_loras = nn.ModuleList(
262
+ [
263
+ LoRALinearLayer(
264
+ dim, dim, ranks[i], network_alphas[i], device=device, dtype=dtype, number=i, n_loras=n_loras
265
+ )
266
+ for i in range(n_loras)
267
+ ]
268
+ )
269
+ self.proj_loras = nn.ModuleList(
270
+ [
271
+ LoRALinearLayer(
272
+ dim, dim, ranks[i], network_alphas[i], device=device, dtype=dtype, number=i, n_loras=n_loras
273
+ )
274
+ for i in range(n_loras)
275
+ ]
276
+ )
277
+ self.lora_weights = lora_weights
278
+ if text_lora_config is not None:
279
+ t_rank = text_lora_config.get("rank", 4)
280
+ t_alpha = text_lora_config.get("alpha", None)
281
+ t_len = text_lora_config.get("token_length", 512)
282
+
283
+ self.text_q_lora = TextLoRALinearLayer(
284
+ dim, dim, t_rank, t_alpha, device=device, dtype=dtype, token_length=t_len
285
+ )
286
+ self.text_k_lora = TextLoRALinearLayer(
287
+ dim, dim, t_rank, t_alpha, device=device, dtype=dtype, token_length=t_len
288
+ )
289
+ self.text_v_lora = TextLoRALinearLayer(
290
+ dim, dim, t_rank, t_alpha, device=device, dtype=dtype, token_length=t_len
291
+ )
292
+ self.text_proj_lora = TextLoRALinearLayer(
293
+ dim, dim, t_rank, t_alpha, device=device, dtype=dtype, token_length=t_len
294
+ )
295
+
296
+ def __call__(
297
+ self,
298
+ attn: Attention,
299
+ hidden_states: torch.FloatTensor,
300
+ encoder_hidden_states: torch.FloatTensor = None,
301
+ attention_mask: Optional[torch.FloatTensor] = None,
302
+ image_rotary_emb: Optional[torch.Tensor] = None,
303
+ use_cond=False,
304
+ ) -> torch.FloatTensor:
305
+ batch_size, total_img_seq_len, _ = hidden_states.shape
306
+
307
+ # `context` projections.
308
+ inner_dim = 3072
309
+ head_dim = inner_dim // attn.heads
310
+ encoder_hidden_states_query_proj = attn.add_q_proj(encoder_hidden_states)
311
+ encoder_hidden_states_key_proj = attn.add_k_proj(encoder_hidden_states)
312
+ encoder_hidden_states_value_proj = attn.add_v_proj(encoder_hidden_states)
313
+
314
+ if getattr(self, "text_q_lora", None) is not None:
315
+ encoder_hidden_states_query_proj = encoder_hidden_states_query_proj + self.text_q_lora(
316
+ encoder_hidden_states
317
+ )
318
+ if getattr(self, "text_k_lora", None) is not None:
319
+ encoder_hidden_states_key_proj = encoder_hidden_states_key_proj + self.text_k_lora(encoder_hidden_states)
320
+ if getattr(self, "text_v_lora", None) is not None:
321
+ encoder_hidden_states_value_proj = encoder_hidden_states_value_proj + self.text_v_lora(
322
+ encoder_hidden_states
323
+ )
324
+
325
+ encoder_hidden_states_query_proj = encoder_hidden_states_query_proj.view(
326
+ batch_size, -1, attn.heads, head_dim
327
+ ).transpose(1, 2)
328
+ encoder_hidden_states_key_proj = encoder_hidden_states_key_proj.view(
329
+ batch_size, -1, attn.heads, head_dim
330
+ ).transpose(1, 2)
331
+ encoder_hidden_states_value_proj = encoder_hidden_states_value_proj.view(
332
+ batch_size, -1, attn.heads, head_dim
333
+ ).transpose(1, 2)
334
+
335
+ if attn.norm_added_q is not None:
336
+ encoder_hidden_states_query_proj = attn.norm_added_q(encoder_hidden_states_query_proj)
337
+ if attn.norm_added_k is not None:
338
+ encoder_hidden_states_key_proj = attn.norm_added_k(encoder_hidden_states_key_proj)
339
+
340
+ assert total_img_seq_len % (1 + self.n_loras) == 0, (
341
+ f"total_img_seq_len:{total_img_seq_len}, n_loras:{self.n_loras}"
342
+ )
343
+ cond_seq_len = total_img_seq_len // (1 + self.n_loras)
344
+
345
+ query = attn.to_q(hidden_states)
346
+ key = attn.to_k(hidden_states)
347
+ value = attn.to_v(hidden_states)
348
+
349
+ for i in range(self.n_loras):
350
+ query = query + self.lora_weights[i] * self.q_loras[i](hidden_states, cond_seq_len=cond_seq_len)
351
+ key = key + self.lora_weights[i] * self.k_loras[i](hidden_states, cond_seq_len=cond_seq_len)
352
+ value = value + self.lora_weights[i] * self.v_loras[i](hidden_states, cond_seq_len=cond_seq_len)
353
+
354
+ inner_dim = key.shape[-1]
355
+ head_dim = inner_dim // attn.heads
356
+ query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
357
+ key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
358
+ value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
359
+
360
+ if attn.norm_q is not None:
361
+ query = attn.norm_q(query)
362
+ if attn.norm_k is not None:
363
+ key = attn.norm_k(key)
364
+
365
+ # attention
366
+ query = torch.cat([encoder_hidden_states_query_proj, query], dim=2)
367
+ key = torch.cat([encoder_hidden_states_key_proj, key], dim=2)
368
+ value = torch.cat([encoder_hidden_states_value_proj, value], dim=2)
369
+
370
+ if image_rotary_emb is not None:
371
+ from diffusers.models.embeddings import apply_rotary_emb
372
+
373
+ query = apply_rotary_emb(query, image_rotary_emb)
374
+ key = apply_rotary_emb(key, image_rotary_emb)
375
+
376
+ cond_size = cond_seq_len
377
+ block_size = hidden_states.shape[1] - cond_size * self.n_loras
378
+
379
+ hidden_states = F.scaled_dot_product_attention(query, key, value, dropout_p=0.0, is_causal=False)
380
+
381
+ hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim)
382
+ hidden_states = hidden_states.to(query.dtype)
383
+
384
+ encoder_hidden_states, hidden_states = (
385
+ hidden_states[:, : encoder_hidden_states.shape[1]],
386
+ hidden_states[:, encoder_hidden_states.shape[1] :],
387
+ )
388
+
389
+ hidden_states_input = hidden_states
390
+ hidden_states = attn.to_out[0](hidden_states)
391
+ for i in range(self.n_loras):
392
+ hidden_states = hidden_states + self.lora_weights[i] * self.proj_loras[i](
393
+ hidden_states_input, cond_seq_len=cond_seq_len
394
+ )
395
+
396
+ hidden_states = attn.to_out[1](hidden_states)
397
+
398
+ encoder_input = encoder_hidden_states
399
+ encoder_hidden_states = attn.to_add_out(encoder_hidden_states)
400
+ if getattr(self, "text_proj_lora", None) is not None:
401
+ encoder_hidden_states = encoder_hidden_states + self.text_proj_lora(encoder_input)
402
+
403
+ cond_hidden_states = hidden_states[:, block_size:, :]
404
+ hidden_states = hidden_states[:, :block_size, :]
405
+
406
+ return (
407
+ (hidden_states, encoder_hidden_states, cond_hidden_states)
408
+ if use_cond
409
+ else (encoder_hidden_states, hidden_states)
410
+ )
direct/pipeline.py ADDED
@@ -0,0 +1,1507 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import inspect
2
+ import json
3
+ import os
4
+ import re
5
+ from typing import Any, Callable, Dict, List, Optional, Union
6
+
7
+ import numpy as np
8
+ import torch
9
+ from PIL import Image
10
+ from transformers import (
11
+ AutoModel,
12
+ AutoProcessor,
13
+ CLIPTextModel,
14
+ CLIPTokenizer,
15
+ T5EncoderModel,
16
+ T5TokenizerFast,
17
+ )
18
+
19
+ from diffusers.image_processor import VaeImageProcessor
20
+ from diffusers.loaders import FluxLoraLoaderMixin, FromSingleFileMixin, TextualInversionLoaderMixin
21
+ from diffusers.models import AutoencoderKL
22
+ from diffusers.schedulers import FlowMatchEulerDiscreteScheduler
23
+ from diffusers.utils import (
24
+ USE_PEFT_BACKEND,
25
+ is_torch_xla_available,
26
+ logging,
27
+ scale_lora_layers,
28
+ unscale_lora_layers,
29
+ )
30
+ from diffusers.utils.torch_utils import randn_tensor
31
+ from diffusers.pipelines.pipeline_utils import DiffusionPipeline
32
+ from diffusers.pipelines.flux.pipeline_output import FluxPipelineOutput
33
+ from contextlib import nullcontext
34
+ from huggingface_hub import hf_hub_download
35
+ from safetensors.torch import load_file
36
+
37
+ from .layers import MultiDoubleStreamBlockLoraProcessor, MultiSingleStreamBlockLoraProcessor
38
+ from .transformer_flux import FluxTransformer2DModelwithcond
39
+
40
+
41
+ if is_torch_xla_available():
42
+ import torch_xla.core.xla_model as xm
43
+
44
+ XLA_AVAILABLE = True
45
+ else:
46
+ XLA_AVAILABLE = False
47
+
48
+
49
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
50
+
51
+
52
+ # Copied from diffusers.pipelines.flux.pipeline_flux.calculate_shift
53
+ def calculate_shift(
54
+ image_seq_len,
55
+ base_seq_len: int = 256,
56
+ max_seq_len: int = 4096,
57
+ base_shift: float = 0.5,
58
+ max_shift: float = 1.15,
59
+ ):
60
+ m = (max_shift - base_shift) / (max_seq_len - base_seq_len)
61
+ b = base_shift - m * base_seq_len
62
+ mu = image_seq_len * m + b
63
+ return mu
64
+
65
+
66
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
67
+ def retrieve_timesteps(
68
+ scheduler,
69
+ num_inference_steps: Optional[int] = None,
70
+ device: Optional[Union[str, torch.device]] = None,
71
+ timesteps: Optional[List[int]] = None,
72
+ sigmas: Optional[List[float]] = None,
73
+ **kwargs,
74
+ ):
75
+ r"""
76
+ Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
77
+ custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
78
+
79
+ Args:
80
+ scheduler (`SchedulerMixin`):
81
+ The scheduler to get timesteps from.
82
+ num_inference_steps (`int`):
83
+ The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
84
+ must be `None`.
85
+ device (`str` or `torch.device`, *optional*):
86
+ The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
87
+ timesteps (`List[int]`, *optional*):
88
+ Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
89
+ `num_inference_steps` and `sigmas` must be `None`.
90
+ sigmas (`List[float]`, *optional*):
91
+ Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
92
+ `num_inference_steps` and `timesteps` must be `None`.
93
+
94
+ Returns:
95
+ `Tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
96
+ second element is the number of inference steps.
97
+ """
98
+ if timesteps is not None and sigmas is not None:
99
+ raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
100
+ if timesteps is not None:
101
+ accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
102
+ if not accepts_timesteps:
103
+ raise ValueError(
104
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
105
+ f" timestep schedules. Please check whether you are using the correct scheduler."
106
+ )
107
+ scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
108
+ timesteps = scheduler.timesteps
109
+ num_inference_steps = len(timesteps)
110
+ elif sigmas is not None:
111
+ accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
112
+ if not accept_sigmas:
113
+ raise ValueError(
114
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
115
+ f" sigmas schedules. Please check whether you are using the correct scheduler."
116
+ )
117
+ scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
118
+ timesteps = scheduler.timesteps
119
+ num_inference_steps = len(timesteps)
120
+ else:
121
+ scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
122
+ timesteps = scheduler.timesteps
123
+ return timesteps, num_inference_steps
124
+
125
+
126
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img.retrieve_latents
127
+ def retrieve_latents(
128
+ encoder_output: torch.Tensor, generator: Optional[torch.Generator] = None, sample_mode: str = "sample"
129
+ ):
130
+ if hasattr(encoder_output, "latent_dist") and sample_mode == "sample":
131
+ return encoder_output.latent_dist.sample(generator)
132
+ elif hasattr(encoder_output, "latent_dist") and sample_mode == "argmax":
133
+ return encoder_output.latent_dist.mode()
134
+ elif hasattr(encoder_output, "latents"):
135
+ return encoder_output.latents
136
+ else:
137
+ raise AttributeError("Could not access latents of provided encoder_output")
138
+
139
+ def to_tensor_0_1(image_input: Union[Image.Image, np.ndarray, torch.Tensor]) -> torch.Tensor:
140
+ """
141
+ Converts a PIL Image or NumPy array image into a PyTorch tensor
142
+ with shape (1, C, H, W), float32 dtype, and values scaled to [0.0, 1.0].
143
+
144
+ Tensor inputs are assumed to already use CHW or BCHW layout and are returned unchanged
145
+ except for adding a batch dimension to CHW tensors.
146
+ """
147
+ if isinstance(image_input, torch.Tensor):
148
+ if image_input.ndim == 3:
149
+ return image_input.unsqueeze(0)
150
+ if image_input.ndim == 4:
151
+ return image_input
152
+ raise ValueError(f"Input tensor has unexpected dimensions: {image_input.ndim}. Expected 3 or 4.")
153
+
154
+ if isinstance(image_input, Image.Image):
155
+ pil_image = image_input
156
+
157
+ if pil_image.mode == 'RGB':
158
+ image_np = np.array(pil_image)
159
+ elif pil_image.mode == 'L':
160
+ image_np = np.expand_dims(np.array(pil_image), axis=-1)
161
+ elif pil_image.mode == 'RGBA':
162
+ pil_image = pil_image.convert('RGB')
163
+ image_np = np.array(pil_image)
164
+ else:
165
+ raise ValueError(f"Unsupported PIL image mode: {pil_image.mode}. Expected RGB, L, or RGBA.")
166
+
167
+ elif isinstance(image_input, np.ndarray):
168
+ image_np = image_input.copy()
169
+
170
+ if image_np.ndim == 2:
171
+ image_np = np.expand_dims(image_np, axis=-1)
172
+
173
+ if image_np.ndim != 3:
174
+ raise ValueError(f"Input NumPy array has unexpected dimensions: {image_np.ndim}. Expected 2 or 3.")
175
+
176
+ num_channels = image_np.shape[-1]
177
+ if num_channels == 4:
178
+ image_np = image_np[:, :, :3]
179
+ elif num_channels not in [1, 3]:
180
+ raise ValueError(f"Input NumPy array has {num_channels} channels, expected 1 (Grayscale) or 3/4 (RGB/RGBA).")
181
+
182
+ else:
183
+ raise TypeError(f"Input must be PIL Image, NumPy array, or torch.Tensor, got {type(image_input)}")
184
+
185
+ if image_np.dtype == np.uint8:
186
+ image_np_float = image_np.astype(np.float32) / 255.0
187
+ elif np.issubdtype(image_np.dtype, np.floating):
188
+ if image_np.max() > 1.0 + 1e-3:
189
+ print("Warning: Input float NumPy array has values > 1.0. Assuming range [0, 255] and scaling.")
190
+ image_np_float = image_np.astype(np.float32) / 255.0
191
+ else:
192
+ image_np_float = image_np.astype(np.float32)
193
+ image_np_float = np.clip(image_np_float, 0.0, 1.0)
194
+ else:
195
+ raise TypeError(f"Unsupported NumPy array dtype: {image_np.dtype}. Expected uint8 or float.")
196
+
197
+ tensor = torch.from_numpy(image_np_float.transpose((2, 0, 1)))
198
+ return tensor.unsqueeze(0)
199
+
200
+ class _DirectFluxFillPipeline(
201
+ DiffusionPipeline,
202
+ FluxLoraLoaderMixin,
203
+ FromSingleFileMixin,
204
+ TextualInversionLoaderMixin,
205
+ ):
206
+ r"""
207
+ The Flux Fill pipeline for image inpainting/outpainting.
208
+
209
+ Reference: https://blackforestlabs.ai/flux-1-tools/
210
+
211
+ Args:
212
+ transformer ([`FluxTransformer2DModelwithcond`]):
213
+ Conditional Transformer (MMDiT) architecture to denoise the encoded image latents.
214
+ scheduler ([`FlowMatchEulerDiscreteScheduler`]):
215
+ A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
216
+ vae ([`AutoencoderKL`]):
217
+ Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representations.
218
+ text_encoder ([`CLIPTextModel`]):
219
+ [CLIP](https://huggingface.co/docs/transformers/model_doc/clip#transformers.CLIPTextModel), specifically
220
+ the [clip-vit-large-patch14](https://huggingface.co/openai/clip-vit-large-patch14) variant.
221
+ text_encoder_2 ([`T5EncoderModel`]):
222
+ [T5](https://huggingface.co/docs/transformers/en/model_doc/t5#transformers.T5EncoderModel), specifically
223
+ the [google/t5-v1_1-xxl](https://huggingface.co/google/t5-v1_1-xxl) variant.
224
+ tokenizer (`CLIPTokenizer`):
225
+ Tokenizer of class
226
+ [CLIPTokenizer](https://huggingface.co/docs/transformers/en/model_doc/clip#transformers.CLIPTokenizer).
227
+ tokenizer_2 (`T5TokenizerFast`):
228
+ Second Tokenizer of class
229
+ [T5TokenizerFast](https://huggingface.co/docs/transformers/en/model_doc/t5#transformers.T5TokenizerFast).
230
+ """
231
+
232
+ model_cpu_offload_seq = "text_encoder->text_encoder_2->transformer->vae"
233
+ _optional_components = []
234
+ _callback_tensor_inputs = ["latents", "prompt_embeds"]
235
+
236
+ def __init__(
237
+ self,
238
+ scheduler: FlowMatchEulerDiscreteScheduler,
239
+ vae: AutoencoderKL,
240
+ text_encoder: CLIPTextModel,
241
+ tokenizer: CLIPTokenizer,
242
+ text_encoder_2: T5EncoderModel,
243
+ tokenizer_2: T5TokenizerFast,
244
+ transformer: FluxTransformer2DModelwithcond,
245
+ ):
246
+ super().__init__()
247
+
248
+ self.register_modules(
249
+ vae=vae,
250
+ text_encoder=text_encoder,
251
+ text_encoder_2=text_encoder_2,
252
+ tokenizer=tokenizer,
253
+ tokenizer_2=tokenizer_2,
254
+ transformer=transformer,
255
+ scheduler=scheduler,
256
+ )
257
+ self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1) if getattr(self, "vae", None) else 8
258
+ # Flux latents are turned into 2x2 patches and packed. This means the latent width and height has to be divisible
259
+ # by the patch size. So the vae scale factor is multiplied by the patch size to account for this
260
+ self.latent_channels = self.vae.config.latent_channels if getattr(self, "vae", None) else 16
261
+ self.image_processor = VaeImageProcessor(
262
+ vae_scale_factor=self.vae_scale_factor * 2, vae_latent_channels=self.latent_channels
263
+ )
264
+ self.mask_processor = VaeImageProcessor(
265
+ vae_scale_factor=self.vae_scale_factor * 2,
266
+ vae_latent_channels=self.latent_channels,
267
+ do_normalize=False,
268
+ do_binarize=False,
269
+ do_convert_grayscale=True,
270
+ )
271
+ self.tokenizer_max_length = (
272
+ self.tokenizer.model_max_length if hasattr(self, "tokenizer") and self.tokenizer is not None else 77
273
+ )
274
+ self.default_sample_size = 128
275
+
276
+ # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline._get_t5_prompt_embeds
277
+ def _get_t5_prompt_embeds(
278
+ self,
279
+ prompt: Union[str, List[str]] = None,
280
+ num_images_per_prompt: int = 1,
281
+ max_sequence_length: int = 512,
282
+ device: Optional[torch.device] = None,
283
+ dtype: Optional[torch.dtype] = None,
284
+ ):
285
+ device = device or self._execution_device
286
+ dtype = dtype or self.text_encoder.dtype
287
+
288
+ prompt = [prompt] if isinstance(prompt, str) else prompt
289
+ batch_size = len(prompt)
290
+
291
+ if isinstance(self, TextualInversionLoaderMixin):
292
+ prompt = self.maybe_convert_prompt(prompt, self.tokenizer_2)
293
+
294
+ text_inputs = self.tokenizer_2(
295
+ prompt,
296
+ padding="max_length",
297
+ max_length=max_sequence_length,
298
+ truncation=True,
299
+ return_length=False,
300
+ return_overflowing_tokens=False,
301
+ return_tensors="pt",
302
+ )
303
+ text_input_ids = text_inputs.input_ids
304
+ untruncated_ids = self.tokenizer_2(prompt, padding="longest", return_tensors="pt").input_ids
305
+
306
+ if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
307
+ removed_text = self.tokenizer_2.batch_decode(untruncated_ids[:, self.tokenizer_max_length - 1 : -1])
308
+ logger.warning(
309
+ "The following part of your input was truncated because `max_sequence_length` is set to "
310
+ f" {max_sequence_length} tokens: {removed_text}"
311
+ )
312
+
313
+ prompt_embeds = self.text_encoder_2(text_input_ids.to(device), output_hidden_states=False)[0]
314
+
315
+ dtype = self.text_encoder_2.dtype
316
+ prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
317
+
318
+ _, seq_len, _ = prompt_embeds.shape
319
+
320
+ # duplicate text embeddings and attention mask for each generation per prompt, using mps friendly method
321
+ prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
322
+ prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
323
+
324
+ return prompt_embeds
325
+
326
+ # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline._get_clip_prompt_embeds
327
+ def _get_clip_prompt_embeds(
328
+ self,
329
+ prompt: Union[str, List[str]],
330
+ num_images_per_prompt: int = 1,
331
+ device: Optional[torch.device] = None,
332
+ ):
333
+ device = device or self._execution_device
334
+
335
+ prompt = [prompt] if isinstance(prompt, str) else prompt
336
+ batch_size = len(prompt)
337
+
338
+ if isinstance(self, TextualInversionLoaderMixin):
339
+ prompt = self.maybe_convert_prompt(prompt, self.tokenizer)
340
+
341
+ text_inputs = self.tokenizer(
342
+ prompt,
343
+ padding="max_length",
344
+ max_length=self.tokenizer_max_length,
345
+ truncation=True,
346
+ return_overflowing_tokens=False,
347
+ return_length=False,
348
+ return_tensors="pt",
349
+ )
350
+
351
+ text_input_ids = text_inputs.input_ids
352
+ untruncated_ids = self.tokenizer(prompt, padding="longest", return_tensors="pt").input_ids
353
+ if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
354
+ removed_text = self.tokenizer.batch_decode(untruncated_ids[:, self.tokenizer_max_length - 1 : -1])
355
+ logger.warning(
356
+ "The following part of your input was truncated because CLIP can only handle sequences up to"
357
+ f" {self.tokenizer_max_length} tokens: {removed_text}"
358
+ )
359
+ prompt_embeds = self.text_encoder(text_input_ids.to(device), output_hidden_states=False)
360
+
361
+ # Use pooled output of CLIPTextModel
362
+ prompt_embeds = prompt_embeds.pooler_output
363
+ prompt_embeds = prompt_embeds.to(dtype=self.text_encoder.dtype, device=device)
364
+
365
+ # duplicate text embeddings for each generation per prompt, using mps friendly method
366
+ prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt)
367
+ prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, -1)
368
+
369
+ return prompt_embeds
370
+
371
+ def prepare_mask_latents(
372
+ self,
373
+ mask,
374
+ masked_image,
375
+ batch_size,
376
+ num_channels_latents,
377
+ num_images_per_prompt,
378
+ height,
379
+ width,
380
+ dtype,
381
+ device,
382
+ generator,
383
+ ):
384
+ # 1. calculate the height and width of the latents
385
+ # VAE applies 8x compression on images but we must also account for packing which requires
386
+ # latent height and width to be divisible by 2.
387
+ height = 2 * (int(height) // (self.vae_scale_factor * 2))
388
+ width = 2 * (int(width) // (self.vae_scale_factor * 2))
389
+
390
+ # 2. encode the masked image
391
+ if masked_image.shape[1] == num_channels_latents:
392
+ masked_image_latents = masked_image
393
+ else:
394
+ masked_image_latents = retrieve_latents(self.vae.encode(masked_image), generator=generator)
395
+
396
+ masked_image_latents = (masked_image_latents - self.vae.config.shift_factor) * self.vae.config.scaling_factor
397
+ masked_image_latents = masked_image_latents.to(device=device, dtype=dtype)
398
+
399
+ # 3. duplicate mask and masked_image_latents for each generation per prompt, using mps friendly method
400
+ batch_size = batch_size * num_images_per_prompt
401
+ if mask.shape[0] < batch_size:
402
+ if not batch_size % mask.shape[0] == 0:
403
+ raise ValueError(
404
+ "The passed mask and the required batch size don't match. Masks are supposed to be duplicated to"
405
+ f" a total batch size of {batch_size}, but {mask.shape[0]} masks were passed. Make sure the number"
406
+ " of masks that you pass is divisible by the total requested batch size."
407
+ )
408
+ mask = mask.repeat(batch_size // mask.shape[0], 1, 1, 1)
409
+ if masked_image_latents.shape[0] < batch_size:
410
+ if not batch_size % masked_image_latents.shape[0] == 0:
411
+ raise ValueError(
412
+ "The passed images and the required batch size don't match. Images are supposed to be duplicated"
413
+ f" to a total batch size of {batch_size}, but {masked_image_latents.shape[0]} images were passed."
414
+ " Make sure the number of images that you pass is divisible by the total requested batch size."
415
+ )
416
+ masked_image_latents = masked_image_latents.repeat(batch_size // masked_image_latents.shape[0], 1, 1, 1)
417
+
418
+ # 4. pack the masked_image_latents
419
+ # batch_size, num_channels_latents, height, width -> batch_size, height//2 * width//2 , num_channels_latents*4
420
+ masked_image_latents = self._pack_latents(
421
+ masked_image_latents,
422
+ batch_size,
423
+ num_channels_latents,
424
+ height,
425
+ width,
426
+ )
427
+
428
+ # 5.resize mask to latents shape we we concatenate the mask to the latents
429
+ mask = mask[:, 0, :, :] # batch_size, 8 * height, 8 * width (mask has not been 8x compressed)
430
+ mask = mask.view(
431
+ batch_size, height, self.vae_scale_factor, width, self.vae_scale_factor
432
+ ) # batch_size, height, 8, width, 8
433
+ mask = mask.permute(0, 2, 4, 1, 3) # batch_size, 8, 8, height, width
434
+ mask = mask.reshape(
435
+ batch_size, self.vae_scale_factor * self.vae_scale_factor, height, width
436
+ ) # batch_size, 8*8, height, width
437
+
438
+ # 6. pack the mask:
439
+ # batch_size, 64, height, width -> batch_size, height//2 * width//2 , 64*2*2
440
+ mask = self._pack_latents(
441
+ mask,
442
+ batch_size,
443
+ self.vae_scale_factor * self.vae_scale_factor,
444
+ height,
445
+ width,
446
+ )
447
+ mask = mask.to(device=device, dtype=dtype)
448
+
449
+ return mask, masked_image_latents
450
+
451
+ # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.encode_prompt
452
+ def encode_prompt(
453
+ self,
454
+ prompt: Union[str, List[str]],
455
+ prompt_2: Optional[Union[str, List[str]]] = None,
456
+ device: Optional[torch.device] = None,
457
+ num_images_per_prompt: int = 1,
458
+ prompt_embeds: Optional[torch.FloatTensor] = None,
459
+ pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
460
+ max_sequence_length: int = 512,
461
+ lora_scale: Optional[float] = None,
462
+ ):
463
+ r"""
464
+
465
+ Args:
466
+ prompt (`str` or `List[str]`, *optional*):
467
+ prompt to be encoded
468
+ prompt_2 (`str` or `List[str]`, *optional*):
469
+ The prompt or prompts to be sent to the `tokenizer_2` and `text_encoder_2`. If not defined, `prompt` is
470
+ used in all text-encoders
471
+ device: (`torch.device`):
472
+ torch device
473
+ num_images_per_prompt (`int`):
474
+ number of images that should be generated per prompt
475
+ prompt_embeds (`torch.FloatTensor`, *optional*):
476
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
477
+ provided, text embeddings will be generated from `prompt` input argument.
478
+ pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
479
+ Pre-generated pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting.
480
+ If not provided, pooled text embeddings will be generated from `prompt` input argument.
481
+ lora_scale (`float`, *optional*):
482
+ A lora scale that will be applied to all LoRA layers of the text encoder if LoRA layers are loaded.
483
+ """
484
+ device = device or self._execution_device
485
+
486
+ # set lora scale so that monkey patched LoRA
487
+ # function of text encoder can correctly access it
488
+ if lora_scale is not None and isinstance(self, FluxLoraLoaderMixin):
489
+ self._lora_scale = lora_scale
490
+
491
+ # dynamically adjust the LoRA scale
492
+ if self.text_encoder is not None and USE_PEFT_BACKEND:
493
+ scale_lora_layers(self.text_encoder, lora_scale)
494
+ if self.text_encoder_2 is not None and USE_PEFT_BACKEND:
495
+ scale_lora_layers(self.text_encoder_2, lora_scale)
496
+
497
+ prompt = [prompt] if isinstance(prompt, str) else prompt
498
+
499
+ if prompt_embeds is None:
500
+ prompt_2 = prompt_2 or prompt
501
+ prompt_2 = [prompt_2] if isinstance(prompt_2, str) else prompt_2
502
+
503
+ # We only use the pooled prompt output from the CLIPTextModel
504
+ pooled_prompt_embeds = self._get_clip_prompt_embeds(
505
+ prompt=prompt,
506
+ device=device,
507
+ num_images_per_prompt=num_images_per_prompt,
508
+ )
509
+ prompt_embeds = self._get_t5_prompt_embeds(
510
+ prompt=prompt_2,
511
+ num_images_per_prompt=num_images_per_prompt,
512
+ max_sequence_length=max_sequence_length,
513
+ device=device,
514
+ )
515
+
516
+ if self.text_encoder is not None:
517
+ if isinstance(self, FluxLoraLoaderMixin) and USE_PEFT_BACKEND:
518
+ # Retrieve the original scale by scaling back the LoRA layers
519
+ unscale_lora_layers(self.text_encoder, lora_scale)
520
+
521
+ if self.text_encoder_2 is not None:
522
+ if isinstance(self, FluxLoraLoaderMixin) and USE_PEFT_BACKEND:
523
+ # Retrieve the original scale by scaling back the LoRA layers
524
+ unscale_lora_layers(self.text_encoder_2, lora_scale)
525
+
526
+ dtype = self.text_encoder.dtype if self.text_encoder is not None else self.transformer.dtype
527
+ text_ids = torch.zeros(prompt_embeds.shape[1], 3).to(device=device, dtype=dtype)
528
+
529
+ return prompt_embeds, pooled_prompt_embeds, text_ids
530
+
531
+ # Copied from diffusers.pipelines.stable_diffusion_3.pipeline_stable_diffusion_3_inpaint.StableDiffusion3InpaintPipeline._encode_vae_image
532
+ def _encode_vae_image(self, image: torch.Tensor, generator: torch.Generator):
533
+ if isinstance(generator, list):
534
+ image_latents = [
535
+ retrieve_latents(self.vae.encode(image[i : i + 1]), generator=generator[i])
536
+ for i in range(image.shape[0])
537
+ ]
538
+ image_latents = torch.cat(image_latents, dim=0)
539
+ else:
540
+ image_latents = retrieve_latents(self.vae.encode(image), generator=generator)
541
+
542
+ image_latents = (image_latents - self.vae.config.shift_factor) * self.vae.config.scaling_factor
543
+
544
+ return image_latents
545
+
546
+ # Copied from diffusers.pipelines.stable_diffusion_3.pipeline_stable_diffusion_3_img2img.StableDiffusion3Img2ImgPipeline.get_timesteps
547
+ def get_timesteps(self, num_inference_steps, strength, device):
548
+ # get the original timestep using init_timestep
549
+ init_timestep = min(num_inference_steps * strength, num_inference_steps)
550
+
551
+ t_start = int(max(num_inference_steps - init_timestep, 0))
552
+ timesteps = self.scheduler.timesteps[t_start * self.scheduler.order :]
553
+ if hasattr(self.scheduler, "set_begin_index"):
554
+ self.scheduler.set_begin_index(t_start * self.scheduler.order)
555
+
556
+ return timesteps, num_inference_steps - t_start
557
+
558
+ def check_inputs(
559
+ self,
560
+ prompt,
561
+ prompt_2,
562
+ strength,
563
+ height,
564
+ width,
565
+ prompt_embeds=None,
566
+ pooled_prompt_embeds=None,
567
+ callback_on_step_end_tensor_inputs=None,
568
+ max_sequence_length=None,
569
+ image=None,
570
+ mask_image=None,
571
+ masked_image_latents=None,
572
+ ):
573
+ if strength < 0 or strength > 1:
574
+ raise ValueError(f"The value of strength should in [0.0, 1.0] but is {strength}")
575
+
576
+ if height % (self.vae_scale_factor * 2) != 0 or width % (self.vae_scale_factor * 2) != 0:
577
+ logger.warning(
578
+ f"`height` and `width` have to be divisible by {self.vae_scale_factor * 2} but are {height} and {width}. Dimensions will be resized accordingly"
579
+ )
580
+
581
+ if callback_on_step_end_tensor_inputs is not None and not all(
582
+ k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
583
+ ):
584
+ raise ValueError(
585
+ f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
586
+ )
587
+
588
+ if prompt is not None and prompt_embeds is not None:
589
+ raise ValueError(
590
+ f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
591
+ " only forward one of the two."
592
+ )
593
+ elif prompt_2 is not None and prompt_embeds is not None:
594
+ raise ValueError(
595
+ f"Cannot forward both `prompt_2`: {prompt_2} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
596
+ " only forward one of the two."
597
+ )
598
+ elif prompt is None and prompt_embeds is None:
599
+ raise ValueError(
600
+ "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
601
+ )
602
+ elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
603
+ raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
604
+ elif prompt_2 is not None and (not isinstance(prompt_2, str) and not isinstance(prompt_2, list)):
605
+ raise ValueError(f"`prompt_2` has to be of type `str` or `list` but is {type(prompt_2)}")
606
+
607
+ if prompt_embeds is not None and pooled_prompt_embeds is None:
608
+ raise ValueError(
609
+ "If `prompt_embeds` are provided, `pooled_prompt_embeds` also have to be passed. Make sure to generate `pooled_prompt_embeds` from the same text encoder that was used to generate `prompt_embeds`."
610
+ )
611
+
612
+ if max_sequence_length is not None and max_sequence_length > 512:
613
+ raise ValueError(f"`max_sequence_length` cannot be greater than 512 but is {max_sequence_length}")
614
+
615
+ if image is not None and masked_image_latents is not None:
616
+ raise ValueError(
617
+ "Please provide either `image` or `masked_image_latents`, `masked_image_latents` should not be passed."
618
+ )
619
+
620
+ if image is not None and mask_image is None:
621
+ raise ValueError("Please provide `mask_image` when passing `image`.")
622
+
623
+ @staticmethod
624
+ # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline._prepare_latent_image_ids
625
+ def _prepare_latent_image_ids(batch_size, height, width, device, dtype):
626
+ latent_image_ids = torch.zeros(height, width, 3)
627
+ latent_image_ids[..., 1] = latent_image_ids[..., 1] + torch.arange(height)[:, None]
628
+ latent_image_ids[..., 2] = latent_image_ids[..., 2] + torch.arange(width)[None, :]
629
+
630
+ latent_image_id_height, latent_image_id_width, latent_image_id_channels = latent_image_ids.shape
631
+
632
+ latent_image_ids = latent_image_ids.reshape(
633
+ latent_image_id_height * latent_image_id_width, latent_image_id_channels
634
+ )
635
+
636
+ return latent_image_ids.to(device=device, dtype=dtype)
637
+
638
+ @staticmethod
639
+ # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline._pack_latents
640
+ def _pack_latents(latents, batch_size, num_channels_latents, height, width):
641
+ latents = latents.view(batch_size, num_channels_latents, height // 2, 2, width // 2, 2)
642
+ latents = latents.permute(0, 2, 4, 1, 3, 5)
643
+ latents = latents.reshape(batch_size, (height // 2) * (width // 2), num_channels_latents * 4)
644
+
645
+ return latents
646
+
647
+ @staticmethod
648
+ # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline._unpack_latents
649
+ def _unpack_latents(latents, height, width, vae_scale_factor):
650
+ batch_size, num_patches, channels = latents.shape
651
+
652
+ # VAE applies 8x compression on images but we must also account for packing which requires
653
+ # latent height and width to be divisible by 2.
654
+ height = 2 * (int(height) // (vae_scale_factor * 2))
655
+ width = 2 * (int(width) // (vae_scale_factor * 2))
656
+
657
+ latents = latents.view(batch_size, height // 2, width // 2, channels // 4, 2, 2)
658
+ latents = latents.permute(0, 3, 1, 4, 2, 5)
659
+
660
+ latents = latents.reshape(batch_size, channels // (2 * 2), height, width)
661
+
662
+ return latents
663
+
664
+ def enable_vae_slicing(self):
665
+ r"""
666
+ Enable sliced VAE decoding. When this option is enabled, the VAE will split the input tensor in slices to
667
+ compute decoding in several steps. This is useful to save some memory and allow larger batch sizes.
668
+ """
669
+ self.vae.enable_slicing()
670
+
671
+ def disable_vae_slicing(self):
672
+ r"""
673
+ Disable sliced VAE decoding. If `enable_vae_slicing` was previously enabled, this method will go back to
674
+ computing decoding in one step.
675
+ """
676
+ self.vae.disable_slicing()
677
+
678
+ def enable_vae_tiling(self):
679
+ r"""
680
+ Enable tiled VAE decoding. When this option is enabled, the VAE will split the input tensor into tiles to
681
+ compute decoding and encoding in several steps. This is useful for saving a large amount of memory and to allow
682
+ processing larger images.
683
+ """
684
+ self.vae.enable_tiling()
685
+
686
+ def disable_vae_tiling(self):
687
+ r"""
688
+ Disable tiled VAE decoding. If `enable_vae_tiling` was previously enabled, this method will go back to
689
+ computing decoding in one step.
690
+ """
691
+ self.vae.disable_tiling()
692
+
693
+ # Copied from diffusers.pipelines.flux.pipeline_flux_img2img.FluxImg2ImgPipeline.prepare_latents
694
+ def prepare_latents(
695
+ self,
696
+ image,
697
+ reference_image,
698
+ geometry_image,
699
+ timestep,
700
+ batch_size,
701
+ num_channels_latents,
702
+ height,
703
+ width,
704
+ dtype,
705
+ device,
706
+ generator,
707
+ latents=None,
708
+ ):
709
+ if isinstance(generator, list) and len(generator) != batch_size:
710
+ raise ValueError(
711
+ f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
712
+ f" size of {batch_size}. Make sure the batch size matches the length of the generators."
713
+ )
714
+
715
+ # VAE applies 8x compression on images but we must also account for packing which requires
716
+ # latent height and width to be divisible by 2.
717
+ height = 2 * (int(height) // (self.vae_scale_factor * 2))
718
+ width = 2 * (int(width) // (self.vae_scale_factor * 2))
719
+ shape = (batch_size, num_channels_latents, height, width)
720
+
721
+ def _process_image_input(img: Optional[torch.Tensor], type_id: int):
722
+ """
723
+ Returns (packed_image_latents or None, image_ids or None).
724
+ type_id: integer marker to set in ids[..., 0]
725
+ """
726
+ img = img.to(device=device, dtype=dtype)
727
+ # if channels != latent_channels, assume it's pixel image -> encode
728
+ if img.shape[1] != self.latent_channels:
729
+ img_latents = self._encode_vae_image(image=img, generator=generator)
730
+ else:
731
+ img_latents = img
732
+ if batch_size > img_latents.shape[0]:
733
+ if batch_size % img_latents.shape[0] == 0:
734
+ additional = batch_size // img_latents.shape[0]
735
+ img_latents = torch.cat([img_latents] * additional, dim=0)
736
+ else:
737
+ raise ValueError(
738
+ f"Cannot duplicate `image` of batch size {img_latents.shape[0]} to {batch_size} text prompts."
739
+ )
740
+ else:
741
+ img_latents = torch.cat([img_latents], dim=0)
742
+
743
+ img_latents = self._pack_latents(img_latents, batch_size, num_channels_latents, height, width)
744
+ ids = self._prepare_latent_image_ids(batch_size, height // 2, width // 2, device, dtype)
745
+ # Mark token type in the ids: first element -> type id.
746
+ ids[..., 0] = type_id
747
+ return img_latents, ids
748
+
749
+ image_latents, image_ids = _process_image_input(image, type_id=0)
750
+ reference_latents, reference_ids = _process_image_input(reference_image, type_id=1)
751
+
752
+ add_img_latents_list = [reference_latents]
753
+ ids_list = [image_ids, reference_ids]
754
+
755
+ if geometry_image is not None:
756
+ geometry_latents, geometry_ids = _process_image_input(geometry_image, type_id=2)
757
+ add_img_latents_list.append(geometry_latents)
758
+ ids_list.append(geometry_ids)
759
+
760
+ noise = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
761
+ noise = self._pack_latents(noise, batch_size, num_channels_latents, height, width)
762
+ latents = self.scheduler.scale_noise(image_latents, timestep, noise)
763
+
764
+ return latents, add_img_latents_list, ids_list
765
+
766
+ @property
767
+ def guidance_scale(self):
768
+ return self._guidance_scale
769
+
770
+ @property
771
+ def joint_attention_kwargs(self):
772
+ return self._joint_attention_kwargs
773
+
774
+ @property
775
+ def num_timesteps(self):
776
+ return self._num_timesteps
777
+
778
+ @property
779
+ def interrupt(self):
780
+ return self._interrupt
781
+
782
+ @torch.no_grad()
783
+ def __call__(
784
+ self,
785
+ prompt: Union[str, List[str]] = None,
786
+ prompt_2: Optional[Union[str, List[str]]] = None,
787
+ image: Optional[torch.FloatTensor] = None,
788
+ mask_image: Optional[torch.FloatTensor] = None,
789
+ reference_image: Optional[torch.FloatTensor] = None,
790
+ geometry_image: Optional[torch.FloatTensor] = None,
791
+ masked_image_latents: Optional[torch.FloatTensor] = None,
792
+ height: Optional[int] = None,
793
+ width: Optional[int] = None,
794
+ strength: float = 1.0,
795
+ num_inference_steps: int = 50,
796
+ sigmas: Optional[List[float]] = None,
797
+ guidance_scale: float = 30.0,
798
+ num_images_per_prompt: Optional[int] = 1,
799
+ generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
800
+ latents: Optional[torch.FloatTensor] = None,
801
+ prompt_embeds: Optional[torch.FloatTensor] = None,
802
+ pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
803
+ output_type: Optional[str] = "pil",
804
+ return_dict: bool = True,
805
+ joint_attention_kwargs: Optional[Dict[str, Any]] = None,
806
+ callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
807
+ callback_on_step_end_tensor_inputs: List[str] = ["latents"],
808
+ max_sequence_length: int = 512,
809
+ condition_embedder=None,
810
+ reference_guidance_scale=1.0,
811
+ **kwargs
812
+ ):
813
+ r"""
814
+ Function invoked when calling the pipeline for generation.
815
+
816
+ Args:
817
+ prompt (`str` or `List[str]`, *optional*):
818
+ The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
819
+ instead.
820
+ prompt_2 (`str` or `List[str]`, *optional*):
821
+ The prompt or prompts to be sent to `tokenizer_2` and `text_encoder_2`. If not defined, `prompt` is
822
+ will be used instead
823
+ image (`torch.Tensor`, `PIL.Image.Image`, `np.ndarray`, `List[torch.Tensor]`, `List[PIL.Image.Image]`, or `List[np.ndarray]`):
824
+ `Image`, numpy array or tensor representing an image batch to be used as the starting point. For both
825
+ numpy array and pytorch tensor, the expected value range is between `[0, 1]` If it's a tensor or a list
826
+ or tensors, the expected shape should be `(B, C, H, W)` or `(C, H, W)`. If it is a numpy array or a
827
+ list of arrays, the expected shape should be `(B, H, W, C)` or `(H, W, C)`.
828
+ mask_image (`torch.Tensor`, `PIL.Image.Image`, `np.ndarray`, `List[torch.Tensor]`, `List[PIL.Image.Image]`, or `List[np.ndarray]`):
829
+ `Image`, numpy array or tensor representing an image batch to mask `image`. White pixels in the mask
830
+ are repainted while black pixels are preserved. If `mask_image` is a PIL image, it is converted to a
831
+ single channel (luminance) before use. If it's a numpy array or pytorch tensor, it should contain one
832
+ color channel (L) instead of 3, so the expected shape for pytorch tensor would be `(B, 1, H, W)`, `(B,
833
+ H, W)`, `(1, H, W)`, `(H, W)`. And for numpy array would be for `(B, H, W, 1)`, `(B, H, W)`, `(H, W,
834
+ 1)`, or `(H, W)`.
835
+ mask_image_latent (`torch.Tensor`, `List[torch.Tensor]`):
836
+ `Tensor` representing an image batch to mask `image` generated by VAE. If not provided, the mask
837
+ latents tensor will ge generated by `mask_image`.
838
+ height (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
839
+ The height in pixels of the generated image. This is set to 1024 by default for the best results.
840
+ width (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
841
+ The width in pixels of the generated image. This is set to 1024 by default for the best results.
842
+ strength (`float`, *optional*, defaults to 1.0):
843
+ Indicates extent to transform the reference `image`. Must be between 0 and 1. `image` is used as a
844
+ starting point and more noise is added the higher the `strength`. The number of denoising steps depends
845
+ on the amount of noise initially added. When `strength` is 1, added noise is maximum and the denoising
846
+ process runs for the full number of iterations specified in `num_inference_steps`. A value of 1
847
+ essentially ignores `image`.
848
+ num_inference_steps (`int`, *optional*, defaults to 50):
849
+ The number of denoising steps. More denoising steps usually lead to a higher quality image at the
850
+ expense of slower inference.
851
+ sigmas (`List[float]`, *optional*):
852
+ Custom sigmas to use for the denoising process with schedulers which support a `sigmas` argument in
853
+ their `set_timesteps` method. If not defined, the default behavior when `num_inference_steps` is passed
854
+ will be used.
855
+ guidance_scale (`float`, *optional*, defaults to 30.0):
856
+ Guidance scale as defined in [Classifier-Free Diffusion
857
+ Guidance](https://huggingface.co/papers/2207.12598). `guidance_scale` is defined as `w` of equation 2.
858
+ of [Imagen Paper](https://huggingface.co/papers/2205.11487). Guidance scale is enabled by setting
859
+ `guidance_scale > 1`. Higher guidance scale encourages to generate images that are closely linked to
860
+ the text `prompt`, usually at the expense of lower image quality.
861
+ num_images_per_prompt (`int`, *optional*, defaults to 1):
862
+ The number of images to generate per prompt.
863
+ generator (`torch.Generator` or `List[torch.Generator]`, *optional*):
864
+ One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)
865
+ to make generation deterministic.
866
+ latents (`torch.FloatTensor`, *optional*):
867
+ Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image
868
+ generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
869
+ tensor will ge generated by sampling using the supplied random `generator`.
870
+ prompt_embeds (`torch.FloatTensor`, *optional*):
871
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
872
+ provided, text embeddings will be generated from `prompt` input argument.
873
+ pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
874
+ Pre-generated pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting.
875
+ If not provided, pooled text embeddings will be generated from `prompt` input argument.
876
+ output_type (`str`, *optional*, defaults to `"pil"`):
877
+ The output format of the generate image. Choose between
878
+ [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`.
879
+ return_dict (`bool`, *optional*, defaults to `True`):
880
+ Whether or not to return a [`~pipelines.flux.FluxPipelineOutput`] instead of a plain tuple.
881
+ joint_attention_kwargs (`dict`, *optional*):
882
+ A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under
883
+ `self.processor` in
884
+ [diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
885
+ callback_on_step_end (`Callable`, *optional*):
886
+ A function that calls at the end of each denoising steps during the inference. The function is called
887
+ with the following arguments: `callback_on_step_end(self: DiffusionPipeline, step: int, timestep: int,
888
+ callback_kwargs: Dict)`. `callback_kwargs` will include a list of all tensors as specified by
889
+ `callback_on_step_end_tensor_inputs`.
890
+ callback_on_step_end_tensor_inputs (`List`, *optional*):
891
+ The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
892
+ will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
893
+ `._callback_tensor_inputs` attribute of your pipeline class.
894
+ max_sequence_length (`int` defaults to 512): Maximum sequence length to use with the `prompt`.
895
+
896
+ Examples:
897
+
898
+ Returns:
899
+ [`~pipelines.flux.FluxPipelineOutput`] or `tuple`: [`~pipelines.flux.FluxPipelineOutput`] if `return_dict`
900
+ is True, otherwise a `tuple`. When returning a tuple, the first element is a list with the generated
901
+ images.
902
+ """
903
+
904
+ height = height or self.default_sample_size * self.vae_scale_factor
905
+ width = width or self.default_sample_size * self.vae_scale_factor
906
+
907
+ # 1. Check inputs. Raise error if not correct
908
+ self.check_inputs(
909
+ prompt,
910
+ prompt_2,
911
+ strength,
912
+ height,
913
+ width,
914
+ prompt_embeds=prompt_embeds,
915
+ pooled_prompt_embeds=pooled_prompt_embeds,
916
+ callback_on_step_end_tensor_inputs=callback_on_step_end_tensor_inputs,
917
+ max_sequence_length=max_sequence_length,
918
+ image=image,
919
+ mask_image=mask_image,
920
+ masked_image_latents=masked_image_latents,
921
+ )
922
+
923
+ self._guidance_scale = guidance_scale
924
+ self._joint_attention_kwargs = joint_attention_kwargs
925
+ self._interrupt = False
926
+ do_reference_guidance = reference_guidance_scale > 1
927
+
928
+ init_image = self.image_processor.preprocess(image, height=height, width=width)
929
+ reference_image = self.image_processor.preprocess(reference_image, height=height, width=width)
930
+ if geometry_image is not None:
931
+ geometry_image = self.image_processor.preprocess(geometry_image, height=height, width=width)
932
+ init_image = init_image.to(dtype=torch.float32)
933
+
934
+ # 2. Define call parameters
935
+ if prompt is not None and isinstance(prompt, str):
936
+ batch_size = 1
937
+ elif prompt is not None and isinstance(prompt, list):
938
+ batch_size = len(prompt)
939
+ else:
940
+ batch_size = prompt_embeds.shape[0]
941
+
942
+ device = self._execution_device
943
+
944
+ # 3. Prepare prompt embeddings
945
+ lora_scale = (
946
+ self.joint_attention_kwargs.get("scale", None) if self.joint_attention_kwargs is not None else None
947
+ )
948
+ (
949
+ prompt_embeds,
950
+ pooled_prompt_embeds,
951
+ text_ids,
952
+ ) = self.encode_prompt(
953
+ prompt=prompt,
954
+ prompt_2=prompt_2,
955
+ prompt_embeds=prompt_embeds,
956
+ pooled_prompt_embeds=pooled_prompt_embeds,
957
+ device=device,
958
+ num_images_per_prompt=num_images_per_prompt,
959
+ max_sequence_length=max_sequence_length,
960
+ lora_scale=lora_scale,
961
+ )
962
+
963
+ # 4. Prepare timesteps
964
+ sigmas = np.linspace(1.0, 1 / num_inference_steps, num_inference_steps) if sigmas is None else sigmas
965
+ image_seq_len = (int(height) // self.vae_scale_factor // 2) * (int(width) // self.vae_scale_factor // 2)
966
+ mu = calculate_shift(
967
+ image_seq_len,
968
+ self.scheduler.config.get("base_image_seq_len", 256),
969
+ self.scheduler.config.get("max_image_seq_len", 4096),
970
+ self.scheduler.config.get("base_shift", 0.5),
971
+ self.scheduler.config.get("max_shift", 1.15),
972
+ )
973
+ timesteps, num_inference_steps = retrieve_timesteps(
974
+ self.scheduler,
975
+ num_inference_steps,
976
+ device,
977
+ sigmas=sigmas,
978
+ mu=mu,
979
+ )
980
+ timesteps, num_inference_steps = self.get_timesteps(num_inference_steps, strength, device)
981
+
982
+ if num_inference_steps < 1:
983
+ raise ValueError(
984
+ f"After adjusting the num_inference_steps by strength parameter: {strength}, the number of pipeline"
985
+ f"steps is {num_inference_steps} which is < 1 and not appropriate for this pipeline."
986
+ )
987
+ latent_timestep = timesteps[:1].repeat(batch_size * num_images_per_prompt)
988
+
989
+ # 5. Prepare latent variables
990
+ num_channels_latents = self.vae.config.latent_channels
991
+ latents, cond_img_latents_list, img_ids_list = self.prepare_latents(
992
+ init_image,
993
+ reference_image,
994
+ geometry_image,
995
+ latent_timestep,
996
+ batch_size * num_images_per_prompt,
997
+ num_channels_latents,
998
+ height,
999
+ width,
1000
+ prompt_embeds.dtype,
1001
+ device,
1002
+ generator,
1003
+ latents,
1004
+ )
1005
+ latent_image_ids = torch.cat(img_ids_list, dim=0) # dim 0 is sequence dimension
1006
+
1007
+ # 6. Prepare mask and masked image latents
1008
+ if masked_image_latents is not None:
1009
+ masked_image_latents = masked_image_latents.to(latents.device)
1010
+ else:
1011
+ mask_image = self.mask_processor.preprocess(mask_image, height=height, width=width)
1012
+
1013
+ masked_image = init_image
1014
+ masked_image = masked_image.to(device=device, dtype=prompt_embeds.dtype)
1015
+
1016
+ height, width = init_image.shape[-2:]
1017
+ mask, masked_image_latents = self.prepare_mask_latents(
1018
+ mask_image,
1019
+ masked_image,
1020
+ batch_size,
1021
+ num_channels_latents,
1022
+ num_images_per_prompt,
1023
+ height,
1024
+ width,
1025
+ prompt_embeds.dtype,
1026
+ device,
1027
+ generator,
1028
+ )
1029
+ masked_image_latents = torch.cat((masked_image_latents, mask), dim=-1)
1030
+
1031
+ num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
1032
+ self._num_timesteps = len(timesteps)
1033
+
1034
+ # handle guidance
1035
+ if self.transformer.config.guidance_embeds:
1036
+ guidance = torch.full([1], guidance_scale, device=device, dtype=torch.float32)
1037
+ guidance = guidance.expand(latents.shape[0])
1038
+ else:
1039
+ guidance = None
1040
+
1041
+ cond_latents = torch.cat(cond_img_latents_list, dim=1)
1042
+ cond_hidden_states = condition_embedder(cond_latents)
1043
+ if do_reference_guidance:
1044
+ neg_reference_image = torch.zeros_like(reference_image)
1045
+ neg_reference_latents = self._encode_vae_image(image=neg_reference_image, generator=generator)
1046
+ neg_reference_latents = self._pack_latents(
1047
+ neg_reference_latents,
1048
+ batch_size,
1049
+ num_channels_latents,
1050
+ 2 * (int(height) // (self.vae_scale_factor * 2)),
1051
+ 2 * (int(width) // (self.vae_scale_factor * 2)),
1052
+ )
1053
+
1054
+ neg_condition_latents = torch.cat([neg_reference_latents, *cond_img_latents_list[1:]], dim=1)
1055
+ neg_hidden_state = condition_embedder(neg_condition_latents)
1056
+ cond_hidden_states = torch.cat([neg_hidden_state, cond_hidden_states])
1057
+ masked_image_latents = torch.cat([masked_image_latents]*2)
1058
+ guidance = torch.cat([guidance]*2)
1059
+ pooled_prompt_embeds = torch.cat([pooled_prompt_embeds]*2)
1060
+ prompt_embeds = torch.cat([prompt_embeds]*2)
1061
+
1062
+ # 7. Denoising loop
1063
+ with self.progress_bar(total=num_inference_steps) as progress_bar:
1064
+ for i, t in enumerate(timesteps):
1065
+ if self.interrupt:
1066
+ continue
1067
+ latent_model_input = torch.cat([latents] * 2) if do_reference_guidance else latents
1068
+ # broadcast to batch dimension in a way that's compatible with ONNX/Core ML
1069
+ timestep = t.expand(latent_model_input.shape[0]).to(latents.dtype)
1070
+
1071
+ transformer_output = self.transformer(
1072
+ hidden_states=torch.cat((latent_model_input, masked_image_latents), dim=2),
1073
+ timestep=timestep / 1000,
1074
+ guidance=guidance,
1075
+ pooled_projections=pooled_prompt_embeds,
1076
+ encoder_hidden_states=prompt_embeds,
1077
+ txt_ids=text_ids,
1078
+ img_ids=latent_image_ids,
1079
+ joint_attention_kwargs=self.joint_attention_kwargs,
1080
+ return_dict=False,
1081
+ cond_hidden_states=cond_hidden_states,
1082
+ **kwargs
1083
+ )
1084
+ noise_pred = transformer_output[0]
1085
+
1086
+ if do_reference_guidance:
1087
+ noise_pred_uncond, noise_pred_cond = noise_pred.chunk(2)
1088
+ noise_pred = noise_pred_uncond + reference_guidance_scale * (noise_pred_cond - noise_pred_uncond)
1089
+
1090
+ # compute the previous noisy sample x_t -> x_t-1
1091
+ latents_dtype = latents.dtype
1092
+ latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
1093
+
1094
+ if latents.dtype != latents_dtype:
1095
+ if torch.backends.mps.is_available():
1096
+ # some platforms (eg. apple mps) misbehave due to a pytorch bug: https://github.com/pytorch/pytorch/pull/99272
1097
+ latents = latents.to(latents_dtype)
1098
+
1099
+ if callback_on_step_end is not None:
1100
+ callback_kwargs = {}
1101
+ for k in callback_on_step_end_tensor_inputs:
1102
+ callback_kwargs[k] = locals()[k]
1103
+ callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
1104
+
1105
+ latents = callback_outputs.pop("latents", latents)
1106
+ prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
1107
+
1108
+ # call the callback, if provided
1109
+ if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
1110
+ progress_bar.update()
1111
+
1112
+ if XLA_AVAILABLE:
1113
+ xm.mark_step()
1114
+
1115
+ # 8. Post-process the image
1116
+ if output_type == "latent":
1117
+ image = latents
1118
+
1119
+ else:
1120
+ latents = self._unpack_latents(latents, height, width, self.vae_scale_factor)
1121
+ latents = (latents / self.vae.config.scaling_factor) + self.vae.config.shift_factor
1122
+ image = self.vae.decode(latents, return_dict=False)[0]
1123
+ image = self.image_processor.postprocess(image, output_type=output_type)
1124
+
1125
+ # Offload all models
1126
+ self.maybe_free_model_hooks()
1127
+ if not return_dict:
1128
+ return (image,)
1129
+
1130
+ return FluxPipelineOutput(images=image)
1131
+
1132
+
1133
+ class DirectPipeline:
1134
+ def __init__(
1135
+ self,
1136
+ pipe: _DirectFluxFillPipeline,
1137
+ condition_embedder,
1138
+ image_encoder,
1139
+ siglip_processor,
1140
+ pooled_image_projector,
1141
+ image_projector,
1142
+ device,
1143
+ ):
1144
+ self.device = device
1145
+ self.pipe = pipe.to(device)
1146
+ self.condition_embedder = condition_embedder.to(device)
1147
+ self.image_encoder = image_encoder.to(device)
1148
+ self.siglip_processor = siglip_processor
1149
+ self.pooled_image_projector = pooled_image_projector.to(device)
1150
+ self.image_projector = image_projector.to(device)
1151
+
1152
+ @classmethod
1153
+ def from_training_components(
1154
+ cls,
1155
+ flux_model_path: Union[str, os.PathLike],
1156
+ transformer: FluxTransformer2DModelwithcond,
1157
+ vae: AutoencoderKL,
1158
+ condition_embedder: torch.nn.Module,
1159
+ image_encoder: torch.nn.Module,
1160
+ siglip_processor,
1161
+ pooled_image_projector: torch.nn.Module,
1162
+ image_projector: torch.nn.Module,
1163
+ device: Optional[Union[str, torch.device]] = "cuda",
1164
+ torch_dtype: Optional[Union[str, torch.dtype]] = None,
1165
+ cache_dir: Optional[Union[str, os.PathLike]] = None,
1166
+ local_files_only: bool = False,
1167
+ token: Optional[Union[str, bool]] = None,
1168
+ revision: Optional[str] = None,
1169
+ variant: Optional[str] = None,
1170
+ ) -> "DirectPipeline":
1171
+ torch_dtype = cls._resolve_torch_dtype(torch_dtype)
1172
+ hub_kwargs = {
1173
+ "cache_dir": cache_dir,
1174
+ "local_files_only": local_files_only,
1175
+ }
1176
+ if token is not None:
1177
+ hub_kwargs["token"] = token
1178
+ if revision is not None:
1179
+ hub_kwargs["revision"] = revision
1180
+ if variant is not None:
1181
+ hub_kwargs["variant"] = variant
1182
+
1183
+ flux_pipeline = _DirectFluxFillPipeline.from_pretrained(
1184
+ flux_model_path,
1185
+ vae=vae,
1186
+ text_encoder=None,
1187
+ text_encoder_2=None,
1188
+ transformer=transformer,
1189
+ torch_dtype=torch_dtype,
1190
+ **hub_kwargs,
1191
+ )
1192
+ return cls(
1193
+ flux_pipeline,
1194
+ condition_embedder.to(dtype=torch_dtype),
1195
+ image_encoder,
1196
+ siglip_processor,
1197
+ pooled_image_projector.to(dtype=torch_dtype),
1198
+ image_projector.to(dtype=torch_dtype),
1199
+ device,
1200
+ )
1201
+
1202
+ @staticmethod
1203
+ def _load_config(
1204
+ direct_model_path: Union[str, os.PathLike],
1205
+ config_name: str,
1206
+ cache_dir: Optional[Union[str, os.PathLike]],
1207
+ local_files_only: bool,
1208
+ token: Optional[Union[str, bool]],
1209
+ revision: Optional[str],
1210
+ ) -> Dict[str, Any]:
1211
+ direct_model_path = str(direct_model_path)
1212
+
1213
+ if os.path.isdir(direct_model_path):
1214
+ config_path = os.path.join(direct_model_path, config_name)
1215
+ if not os.path.exists(config_path):
1216
+ raise FileNotFoundError(f"Missing DIRECT config file: {config_path}")
1217
+ else:
1218
+ config_path = hf_hub_download(
1219
+ direct_model_path,
1220
+ filename=config_name,
1221
+ cache_dir=cache_dir,
1222
+ local_files_only=local_files_only,
1223
+ token=token,
1224
+ revision=revision,
1225
+ )
1226
+
1227
+ with open(config_path, "r") as f:
1228
+ return json.load(f)
1229
+
1230
+ @staticmethod
1231
+ def _resolve_torch_dtype(torch_dtype: Optional[Union[str, torch.dtype]]) -> torch.dtype:
1232
+ if isinstance(torch_dtype, torch.dtype):
1233
+ return torch_dtype
1234
+ if torch_dtype is None:
1235
+ return torch.bfloat16
1236
+ dtype_map = {
1237
+ "float32": torch.float32,
1238
+ "float": torch.float32,
1239
+ "float16": torch.float16,
1240
+ "fp16": torch.float16,
1241
+ "bfloat16": torch.bfloat16,
1242
+ "bf16": torch.bfloat16,
1243
+ }
1244
+ if torch_dtype not in dtype_map:
1245
+ raise ValueError(f"Unsupported torch dtype: {torch_dtype}")
1246
+ return dtype_map[torch_dtype]
1247
+
1248
+ @staticmethod
1249
+ def _resolve_model_file(
1250
+ model_path: Union[str, os.PathLike],
1251
+ filename: str,
1252
+ cache_dir: Optional[Union[str, os.PathLike]],
1253
+ local_files_only: bool,
1254
+ token: Optional[Union[str, bool]],
1255
+ revision: Optional[str],
1256
+ ) -> str:
1257
+ model_path = str(model_path)
1258
+ if os.path.isdir(model_path):
1259
+ file_path = os.path.join(model_path, filename)
1260
+ if not os.path.exists(file_path):
1261
+ raise FileNotFoundError(f"Missing DIRECT weight file: {file_path}")
1262
+ return file_path
1263
+ return hf_hub_download(
1264
+ model_path,
1265
+ filename=filename,
1266
+ cache_dir=cache_dir,
1267
+ local_files_only=local_files_only,
1268
+ token=token,
1269
+ revision=revision,
1270
+ )
1271
+
1272
+ @staticmethod
1273
+ def _load_lora_processors(
1274
+ transformer: FluxTransformer2DModelwithcond,
1275
+ lora_path: str,
1276
+ torch_dtype: torch.dtype,
1277
+ lora_config: Dict[str, Any],
1278
+ ) -> None:
1279
+ ranks = lora_config["ranks"]
1280
+ network_alphas = lora_config["alphas"]
1281
+ lora_weights = lora_config.get("weights", [1] * len(ranks))
1282
+ text_lora_config = lora_config["text"]
1283
+ double_blocks_idx = set(range(lora_config["double_blocks"]))
1284
+ single_blocks_idx = set(range(lora_config["single_blocks"]))
1285
+
1286
+ lora_attn_procs = {}
1287
+ for name, attn_processor in transformer.attn_processors.items():
1288
+ match = re.search(r"\.(\d+)\.", name)
1289
+ layer_index = int(match.group(1)) if match else -1
1290
+ if name.startswith("transformer_blocks") and layer_index in double_blocks_idx:
1291
+ lora_attn_procs[name] = MultiDoubleStreamBlockLoraProcessor(
1292
+ dim=3072,
1293
+ ranks=ranks,
1294
+ network_alphas=network_alphas,
1295
+ lora_weights=lora_weights,
1296
+ device=transformer.device,
1297
+ dtype=torch_dtype,
1298
+ n_loras=lora_config["n_loras"],
1299
+ text_lora_config=text_lora_config,
1300
+ )
1301
+ elif name.startswith("single_transformer_blocks") and layer_index in single_blocks_idx:
1302
+ lora_attn_procs[name] = MultiSingleStreamBlockLoraProcessor(
1303
+ dim=3072,
1304
+ ranks=ranks,
1305
+ network_alphas=network_alphas,
1306
+ lora_weights=lora_weights,
1307
+ device=transformer.device,
1308
+ dtype=torch_dtype,
1309
+ n_loras=lora_config["n_loras"],
1310
+ text_lora_config=text_lora_config,
1311
+ )
1312
+ else:
1313
+ lora_attn_procs[name] = attn_processor
1314
+
1315
+ transformer.set_attn_processor(lora_attn_procs)
1316
+ transformer.load_state_dict(load_file(lora_path), strict=False)
1317
+
1318
+ @staticmethod
1319
+ def _load_state_dict(module: torch.nn.Module, state_path: str, strict: bool = True) -> torch.nn.Module:
1320
+ module.load_state_dict(load_file(state_path), strict=strict)
1321
+ return module
1322
+
1323
+ @classmethod
1324
+ def _load_linear(
1325
+ cls,
1326
+ layer_config: Dict[str, int],
1327
+ state_path: str,
1328
+ torch_dtype: torch.dtype,
1329
+ ) -> torch.nn.Linear:
1330
+ layer = torch.nn.Linear(layer_config["input_dim"], layer_config["output_dim"]).to(dtype=torch_dtype)
1331
+ return cls._load_state_dict(layer, state_path)
1332
+
1333
+ @classmethod
1334
+ def _load_condition_embedder(
1335
+ cls,
1336
+ layer_config: Dict[str, int],
1337
+ output_dim: int,
1338
+ state_path: str,
1339
+ torch_dtype: torch.dtype,
1340
+ ) -> torch.nn.Linear:
1341
+ layer = torch.nn.Linear(layer_config["input_dim"], output_dim).to(dtype=torch_dtype)
1342
+ return cls._load_state_dict(layer, state_path)
1343
+
1344
+ @classmethod
1345
+ def from_pretrained(
1346
+ cls,
1347
+ direct_model_path: Union[str, os.PathLike],
1348
+ flux_model_path: Optional[Union[str, os.PathLike]] = None,
1349
+ siglip_model_path: Optional[Union[str, os.PathLike]] = None,
1350
+ device: Optional[Union[str, torch.device]] = "cuda",
1351
+ torch_dtype: Optional[Union[str, torch.dtype]] = None,
1352
+ config_name: str = "config.json",
1353
+ cache_dir: Optional[Union[str, os.PathLike]] = None,
1354
+ local_files_only: bool = False,
1355
+ token: Optional[Union[str, bool]] = None,
1356
+ revision: Optional[str] = None,
1357
+ ) -> "DirectPipeline":
1358
+ config = cls._load_config(direct_model_path, config_name, cache_dir, local_files_only, token, revision)
1359
+ torch_dtype = cls._resolve_torch_dtype(torch_dtype or config.get("torch_dtype"))
1360
+ device = torch.device(device)
1361
+ flux_model_path = str(flux_model_path or config["flux_model"])
1362
+ siglip_model_path = str(siglip_model_path or config["siglip_model"])
1363
+
1364
+ hub_kwargs = {
1365
+ "cache_dir": cache_dir,
1366
+ "local_files_only": local_files_only,
1367
+ }
1368
+ if token is not None:
1369
+ hub_kwargs["token"] = token
1370
+ if revision is not None:
1371
+ hub_kwargs["revision"] = revision
1372
+
1373
+ weight_files = config["weight_files"]
1374
+ get_weight = lambda key: cls._resolve_model_file(
1375
+ direct_model_path, weight_files[key], cache_dir, local_files_only, token, revision
1376
+ )
1377
+
1378
+ transformer = FluxTransformer2DModelwithcond.from_pretrained(
1379
+ flux_model_path,
1380
+ subfolder="transformer",
1381
+ torch_dtype=torch_dtype,
1382
+ **hub_kwargs,
1383
+ )
1384
+ vae = AutoencoderKL.from_pretrained(
1385
+ flux_model_path,
1386
+ subfolder="vae",
1387
+ torch_dtype=torch_dtype,
1388
+ **hub_kwargs,
1389
+ )
1390
+
1391
+ cls._load_lora_processors(transformer, get_weight("lora"), torch_dtype, config["lora"])
1392
+ cls._load_state_dict(transformer, get_weight("x_embedder"), strict=False)
1393
+ cls._load_state_dict(transformer, get_weight("time_text_embed"), strict=False)
1394
+ condition_embedder = cls._load_condition_embedder(
1395
+ config["condition_embedder"],
1396
+ transformer.inner_dim,
1397
+ get_weight("condition_embedder"),
1398
+ torch_dtype,
1399
+ )
1400
+ pooled_image_projector = cls._load_linear(
1401
+ config["pooled_image_projector"],
1402
+ get_weight("pooled_image_projector"),
1403
+ torch_dtype,
1404
+ )
1405
+ image_projector = cls._load_linear(
1406
+ config["image_projector"],
1407
+ get_weight("image_projector"),
1408
+ torch_dtype,
1409
+ )
1410
+
1411
+ image_encoder = AutoModel.from_pretrained(siglip_model_path, **hub_kwargs).vision_model.eval().to(dtype=torch_dtype)
1412
+ siglip_processor = AutoProcessor.from_pretrained(siglip_model_path, **hub_kwargs)
1413
+
1414
+ flux_pipeline = _DirectFluxFillPipeline.from_pretrained(
1415
+ flux_model_path,
1416
+ vae=vae,
1417
+ text_encoder=None,
1418
+ text_encoder_2=None,
1419
+ transformer=transformer,
1420
+ torch_dtype=torch_dtype,
1421
+ **hub_kwargs,
1422
+ )
1423
+ return cls(
1424
+ flux_pipeline,
1425
+ condition_embedder.to(dtype=torch_dtype),
1426
+ image_encoder,
1427
+ siglip_processor,
1428
+ pooled_image_projector,
1429
+ image_projector,
1430
+ device,
1431
+ )
1432
+
1433
+ def prepare_images(
1434
+ self,
1435
+ composite_image,
1436
+ inpaint_mask,
1437
+ reference_image,
1438
+ geometry_image,
1439
+ context_image,
1440
+ dtype,
1441
+ device,
1442
+ ):
1443
+ composite_image = to_tensor_0_1(composite_image).to(dtype).to(device)
1444
+ inpaint_mask = to_tensor_0_1(inpaint_mask).to(dtype).to(device)
1445
+ reference_image = to_tensor_0_1(reference_image).to(dtype).to(device)
1446
+ geometry_image = to_tensor_0_1(geometry_image).to(dtype).to(device)
1447
+ return composite_image, inpaint_mask, reference_image, geometry_image, context_image
1448
+
1449
+ def encode_prompt(
1450
+ self,
1451
+ image,
1452
+ dtype
1453
+ ):
1454
+ siglip_input = self.siglip_processor(images=image, return_tensors="pt").to(self.device)
1455
+ siglip_output = self.image_encoder(**siglip_input)
1456
+ pooled_prompt_embeds = self.pooled_image_projector(siglip_output.pooler_output).to(dtype)
1457
+ prompt_embeds = self.image_projector(siglip_output.last_hidden_state).to(dtype)
1458
+ return prompt_embeds, pooled_prompt_embeds
1459
+
1460
+ def __call__(
1461
+ self,
1462
+ composite_image,
1463
+ inpaint_mask,
1464
+ reference_image,
1465
+ geometry_image,
1466
+ context_image,
1467
+ seed=None,
1468
+ guidance_scale=7.5,
1469
+ num_inference_steps=30,
1470
+ height=512,
1471
+ width=512,
1472
+ use_autocast=True,
1473
+ **kwargs,
1474
+ ):
1475
+ generator = torch.Generator(self.device).manual_seed(seed) if seed is not None else None
1476
+ autocast_ctx = torch.autocast(self.device.type, dtype=torch.bfloat16) if use_autocast else nullcontext()
1477
+ dtype = torch.bfloat16 if use_autocast else torch.float32
1478
+ composite_image, inpaint_mask, reference_image, geometry_image, context_image = self.prepare_images(
1479
+ composite_image,
1480
+ inpaint_mask,
1481
+ reference_image,
1482
+ geometry_image,
1483
+ context_image,
1484
+ dtype,
1485
+ self.device,
1486
+ )
1487
+
1488
+ # Pre-calculate image prompt embeddings outside autocast.
1489
+ with torch.no_grad():
1490
+ prompt_embeds, pooled_prompt_embeds = self.encode_prompt(context_image, dtype)
1491
+ with autocast_ctx:
1492
+ images = self.pipe(
1493
+ image=composite_image,
1494
+ reference_image=reference_image,
1495
+ geometry_image=geometry_image,
1496
+ mask_image=inpaint_mask,
1497
+ prompt_embeds=prompt_embeds,
1498
+ pooled_prompt_embeds=pooled_prompt_embeds,
1499
+ num_inference_steps=num_inference_steps,
1500
+ guidance_scale=guidance_scale,
1501
+ generator=generator,
1502
+ height=height,
1503
+ width=width,
1504
+ condition_embedder=self.condition_embedder,
1505
+ **kwargs
1506
+ ).images
1507
+ return images
direct/transformer_flux.py ADDED
@@ -0,0 +1,561 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any, Dict, Optional, Tuple, Union
2
+
3
+ import numpy as np
4
+ import torch
5
+ import torch.nn as nn
6
+ import torch.nn.functional as F
7
+
8
+ from diffusers.configuration_utils import ConfigMixin, register_to_config
9
+ from diffusers.loaders import FluxTransformer2DLoadersMixin, FromOriginalModelMixin, PeftAdapterMixin
10
+ from diffusers.models.attention import FeedForward
11
+ from diffusers.models.attention_processor import (
12
+ Attention,
13
+ AttentionProcessor,
14
+ FluxAttnProcessor2_0,
15
+ FluxAttnProcessor2_0_NPU,
16
+ FusedFluxAttnProcessor2_0,
17
+ )
18
+ from diffusers.models.modeling_utils import ModelMixin
19
+ from diffusers.models.normalization import AdaLayerNormContinuous, AdaLayerNormZero, AdaLayerNormZeroSingle
20
+ from diffusers.utils import USE_PEFT_BACKEND, is_torch_version, logging, scale_lora_layers, unscale_lora_layers
21
+ from diffusers.utils.import_utils import is_torch_npu_available
22
+ from diffusers.utils.torch_utils import maybe_allow_in_graph
23
+ from diffusers.models.embeddings import CombinedTimestepGuidanceTextProjEmbeddings, CombinedTimestepTextProjEmbeddings, FluxPosEmbed
24
+ from diffusers.models.modeling_outputs import Transformer2DModelOutput
25
+ from diffusers import CacheMixin
26
+
27
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
28
+
29
+ @maybe_allow_in_graph
30
+ class FluxSingleTransformerBlock(nn.Module):
31
+
32
+ def __init__(self, dim, num_attention_heads, attention_head_dim, mlp_ratio=4.0):
33
+ super().__init__()
34
+ self.mlp_hidden_dim = int(dim * mlp_ratio)
35
+
36
+ self.norm = AdaLayerNormZeroSingle(dim)
37
+ self.proj_mlp = nn.Linear(dim, self.mlp_hidden_dim)
38
+ self.act_mlp = nn.GELU(approximate="tanh")
39
+ self.proj_out = nn.Linear(dim + self.mlp_hidden_dim, dim)
40
+
41
+ if is_torch_npu_available():
42
+ processor = FluxAttnProcessor2_0_NPU()
43
+ else:
44
+ processor = FluxAttnProcessor2_0()
45
+ self.attn = Attention(
46
+ query_dim=dim,
47
+ cross_attention_dim=None,
48
+ dim_head=attention_head_dim,
49
+ heads=num_attention_heads,
50
+ out_dim=dim,
51
+ bias=True,
52
+ processor=processor,
53
+ qk_norm="rms_norm",
54
+ eps=1e-6,
55
+ pre_only=True,
56
+ )
57
+
58
+ def forward(
59
+ self,
60
+ hidden_states: torch.Tensor,
61
+ cond_hidden_states: torch.Tensor,
62
+ temb: torch.Tensor,
63
+ cond_temb: torch.Tensor,
64
+ image_rotary_emb: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
65
+ joint_attention_kwargs: Optional[Dict[str, Any]] = None,
66
+ ) -> torch.Tensor:
67
+ use_cond = cond_hidden_states is not None
68
+
69
+ residual = hidden_states
70
+ norm_hidden_states, gate = self.norm(hidden_states, emb=temb)
71
+ mlp_hidden_states = self.act_mlp(self.proj_mlp(norm_hidden_states))
72
+
73
+ if use_cond:
74
+ residual_cond = cond_hidden_states
75
+ norm_cond_hidden_states, cond_gate = self.norm(cond_hidden_states, emb=cond_temb)
76
+ mlp_cond_hidden_states = self.act_mlp(self.proj_mlp(norm_cond_hidden_states))
77
+
78
+ norm_hidden_states_concat = torch.concat([norm_hidden_states, norm_cond_hidden_states], dim=-2)
79
+ else:
80
+ norm_hidden_states_concat = norm_hidden_states
81
+ joint_attention_kwargs = joint_attention_kwargs or {}
82
+ attn_output = self.attn(
83
+ hidden_states=norm_hidden_states_concat,
84
+ image_rotary_emb=image_rotary_emb,
85
+ use_cond=use_cond,
86
+ **joint_attention_kwargs,
87
+ )
88
+ if use_cond:
89
+ attn_output, cond_attn_output = attn_output
90
+
91
+ hidden_states = torch.cat([attn_output, mlp_hidden_states], dim=2)
92
+ gate = gate.unsqueeze(1)
93
+ hidden_states = gate * self.proj_out(hidden_states)
94
+ hidden_states = residual + hidden_states
95
+
96
+ if use_cond:
97
+ condition_latents = torch.cat([cond_attn_output, mlp_cond_hidden_states], dim=2)
98
+ cond_gate = cond_gate.unsqueeze(1)
99
+ condition_latents = cond_gate * self.proj_out(condition_latents)
100
+ condition_latents = residual_cond + condition_latents
101
+
102
+ if hidden_states.dtype == torch.float16:
103
+ hidden_states = hidden_states.clip(-65504, 65504)
104
+
105
+ return hidden_states, condition_latents if use_cond else None
106
+
107
+
108
+ @maybe_allow_in_graph
109
+ class FluxTransformerBlock(nn.Module):
110
+ def __init__(
111
+ self, dim: int, num_attention_heads: int, attention_head_dim: int, qk_norm: str = "rms_norm", eps: float = 1e-6
112
+ ):
113
+ super().__init__()
114
+
115
+ self.norm1 = AdaLayerNormZero(dim)
116
+
117
+ self.norm1_context = AdaLayerNormZero(dim)
118
+
119
+ if hasattr(F, "scaled_dot_product_attention"):
120
+ processor = FluxAttnProcessor2_0()
121
+ else:
122
+ raise ValueError(
123
+ "The current PyTorch version does not support the `scaled_dot_product_attention` function."
124
+ )
125
+ self.attn = Attention(
126
+ query_dim=dim,
127
+ cross_attention_dim=None,
128
+ added_kv_proj_dim=dim,
129
+ dim_head=attention_head_dim,
130
+ heads=num_attention_heads,
131
+ out_dim=dim,
132
+ context_pre_only=False,
133
+ bias=True,
134
+ processor=processor,
135
+ qk_norm=qk_norm,
136
+ eps=eps,
137
+ )
138
+
139
+ self.norm2 = nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6)
140
+ self.ff = FeedForward(dim=dim, dim_out=dim, activation_fn="gelu-approximate")
141
+
142
+ self.norm2_context = nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6)
143
+ self.ff_context = FeedForward(dim=dim, dim_out=dim, activation_fn="gelu-approximate")
144
+
145
+ # let chunk size default to None
146
+ self._chunk_size = None
147
+ self._chunk_dim = 0
148
+
149
+ def forward(
150
+ self,
151
+ hidden_states: torch.Tensor,
152
+ cond_hidden_states: torch.Tensor,
153
+ encoder_hidden_states: torch.Tensor,
154
+ temb: torch.Tensor,
155
+ cond_temb: torch.Tensor,
156
+ image_rotary_emb: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
157
+ joint_attention_kwargs: Optional[Dict[str, Any]] = None,
158
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
159
+ use_cond = cond_hidden_states is not None
160
+
161
+ norm_hidden_states, gate_msa, shift_mlp, scale_mlp, gate_mlp = self.norm1(hidden_states, emb=temb)
162
+ if use_cond:
163
+ (
164
+ norm_cond_hidden_states,
165
+ cond_gate_msa,
166
+ cond_shift_mlp,
167
+ cond_scale_mlp,
168
+ cond_gate_mlp,
169
+ ) = self.norm1(cond_hidden_states, emb=cond_temb)
170
+
171
+ norm_hidden_states = torch.concat([norm_hidden_states, norm_cond_hidden_states], dim=-2)
172
+
173
+
174
+ norm_encoder_hidden_states, c_gate_msa, c_shift_mlp, c_scale_mlp, c_gate_mlp = self.norm1_context(
175
+ encoder_hidden_states, emb=temb
176
+ )
177
+
178
+
179
+ joint_attention_kwargs = joint_attention_kwargs or {}
180
+ # Attention.
181
+ attention_outputs = self.attn(
182
+ hidden_states=norm_hidden_states,
183
+ encoder_hidden_states=norm_encoder_hidden_states,
184
+ image_rotary_emb=image_rotary_emb,
185
+ use_cond=use_cond,
186
+ **joint_attention_kwargs,
187
+ )
188
+
189
+ attn_output, context_attn_output = attention_outputs[:2]
190
+ cond_attn_output = attention_outputs[2] if use_cond else None
191
+
192
+ # Process attention outputs for the `hidden_states`.
193
+ attn_output = gate_msa.unsqueeze(1) * attn_output
194
+ hidden_states = hidden_states + attn_output
195
+
196
+ if use_cond:
197
+ cond_attn_output = cond_gate_msa.unsqueeze(1) * cond_attn_output
198
+ cond_hidden_states = cond_hidden_states + cond_attn_output
199
+
200
+ norm_hidden_states = self.norm2(hidden_states)
201
+ norm_hidden_states = norm_hidden_states * (1 + scale_mlp[:, None]) + shift_mlp[:, None]
202
+
203
+ if use_cond:
204
+ norm_cond_hidden_states = self.norm2(cond_hidden_states)
205
+ norm_cond_hidden_states = (
206
+ norm_cond_hidden_states * (1 + cond_scale_mlp[:, None])
207
+ + cond_shift_mlp[:, None]
208
+ )
209
+
210
+ ff_output = self.ff(norm_hidden_states)
211
+ ff_output = gate_mlp.unsqueeze(1) * ff_output
212
+ hidden_states = hidden_states + ff_output
213
+
214
+ if use_cond:
215
+ cond_ff_output = self.ff(norm_cond_hidden_states)
216
+ cond_ff_output = cond_gate_mlp.unsqueeze(1) * cond_ff_output
217
+ cond_hidden_states = cond_hidden_states + cond_ff_output
218
+
219
+ # Process attention outputs for the `encoder_hidden_states`.
220
+
221
+ context_attn_output = c_gate_msa.unsqueeze(1) * context_attn_output
222
+ encoder_hidden_states = encoder_hidden_states + context_attn_output
223
+
224
+ norm_encoder_hidden_states = self.norm2_context(encoder_hidden_states)
225
+ norm_encoder_hidden_states = norm_encoder_hidden_states * (1 + c_scale_mlp[:, None]) + c_shift_mlp[:, None]
226
+
227
+ context_ff_output = self.ff_context(norm_encoder_hidden_states)
228
+ encoder_hidden_states = encoder_hidden_states + c_gate_mlp.unsqueeze(1) * context_ff_output
229
+ if encoder_hidden_states.dtype == torch.float16:
230
+ encoder_hidden_states = encoder_hidden_states.clip(-65504, 65504)
231
+
232
+ return encoder_hidden_states, hidden_states, cond_hidden_states if use_cond else None
233
+
234
+
235
+ class FluxTransformer2DModelwithcond(
236
+ ModelMixin, ConfigMixin, PeftAdapterMixin, FromOriginalModelMixin, FluxTransformer2DLoadersMixin, CacheMixin
237
+ ):
238
+ _supports_gradient_checkpointing = True
239
+ _no_split_modules = ["FluxTransformerBlock", "FluxSingleTransformerBlock"]
240
+
241
+ @register_to_config
242
+ def __init__(
243
+ self,
244
+ patch_size: int = 1,
245
+ in_channels: int = 64,
246
+ out_channels: Optional[int] = None,
247
+ num_layers: int = 19,
248
+ num_single_layers: int = 38,
249
+ attention_head_dim: int = 128,
250
+ num_attention_heads: int = 24,
251
+ joint_attention_dim: int = 4096,
252
+ pooled_projection_dim: int = 768,
253
+ guidance_embeds: bool = False,
254
+ axes_dims_rope: Tuple[int] = (16, 56, 56),
255
+ ):
256
+ super().__init__()
257
+ self.out_channels = out_channels or in_channels
258
+ self.inner_dim = num_attention_heads * attention_head_dim
259
+
260
+ self.pos_embed = FluxPosEmbed(theta=10000, axes_dim=axes_dims_rope)
261
+
262
+ text_time_guidance_cls = (
263
+ CombinedTimestepGuidanceTextProjEmbeddings if guidance_embeds else CombinedTimestepTextProjEmbeddings
264
+ )
265
+ self.time_text_embed = text_time_guidance_cls(
266
+ embedding_dim=self.inner_dim, pooled_projection_dim=pooled_projection_dim
267
+ )
268
+
269
+ self.context_embedder = nn.Linear(joint_attention_dim, self.inner_dim)
270
+ self.x_embedder = nn.Linear(in_channels, self.inner_dim)
271
+
272
+ self.transformer_blocks = nn.ModuleList(
273
+ [
274
+ FluxTransformerBlock(
275
+ dim=self.inner_dim,
276
+ num_attention_heads=num_attention_heads,
277
+ attention_head_dim=attention_head_dim,
278
+ )
279
+ for _ in range(num_layers)
280
+ ]
281
+ )
282
+
283
+ self.single_transformer_blocks = nn.ModuleList(
284
+ [
285
+ FluxSingleTransformerBlock(
286
+ dim=self.inner_dim,
287
+ num_attention_heads=num_attention_heads,
288
+ attention_head_dim=attention_head_dim,
289
+ )
290
+ for _ in range(num_single_layers)
291
+ ]
292
+ )
293
+
294
+ self.norm_out = AdaLayerNormContinuous(self.inner_dim, self.inner_dim, elementwise_affine=False, eps=1e-6)
295
+ self.proj_out = nn.Linear(self.inner_dim, patch_size * patch_size * self.out_channels, bias=True)
296
+
297
+ self.gradient_checkpointing = False
298
+
299
+ @property
300
+ # Copied from diffusers.models.unets.unet_2d_condition.UNet2DConditionModel.attn_processors
301
+ def attn_processors(self) -> Dict[str, AttentionProcessor]:
302
+ r"""
303
+ Returns:
304
+ `dict` of attention processors: A dictionary containing all attention processors used in the model with
305
+ indexed by its weight name.
306
+ """
307
+ # set recursively
308
+ processors = {}
309
+
310
+ def fn_recursive_add_processors(name: str, module: torch.nn.Module, processors: Dict[str, AttentionProcessor]):
311
+ if hasattr(module, "get_processor"):
312
+ processors[f"{name}.processor"] = module.get_processor()
313
+
314
+ for sub_name, child in module.named_children():
315
+ fn_recursive_add_processors(f"{name}.{sub_name}", child, processors)
316
+
317
+ return processors
318
+
319
+ for name, module in self.named_children():
320
+ fn_recursive_add_processors(name, module, processors)
321
+
322
+ return processors
323
+
324
+ # Copied from diffusers.models.unets.unet_2d_condition.UNet2DConditionModel.set_attn_processor
325
+ def set_attn_processor(self, processor: Union[AttentionProcessor, Dict[str, AttentionProcessor]]):
326
+ r"""
327
+ Sets the attention processor to use to compute attention.
328
+
329
+ Parameters:
330
+ processor (`dict` of `AttentionProcessor` or only `AttentionProcessor`):
331
+ The instantiated processor class or a dictionary of processor classes that will be set as the processor
332
+ for **all** `Attention` layers.
333
+
334
+ If `processor` is a dict, the key needs to define the path to the corresponding cross attention
335
+ processor. This is strongly recommended when setting trainable attention processors.
336
+
337
+ """
338
+ count = len(self.attn_processors.keys())
339
+
340
+ if isinstance(processor, dict) and len(processor) != count:
341
+ raise ValueError(
342
+ f"A dict of processors was passed, but the number of processors {len(processor)} does not match the"
343
+ f" number of attention layers: {count}. Please make sure to pass {count} processor classes."
344
+ )
345
+
346
+ def fn_recursive_attn_processor(name: str, module: torch.nn.Module, processor):
347
+ if hasattr(module, "set_processor"):
348
+ if not isinstance(processor, dict):
349
+ module.set_processor(processor)
350
+ else:
351
+ module.set_processor(processor.pop(f"{name}.processor"))
352
+
353
+ for sub_name, child in module.named_children():
354
+ fn_recursive_attn_processor(f"{name}.{sub_name}", child, processor)
355
+
356
+ for name, module in self.named_children():
357
+ fn_recursive_attn_processor(name, module, processor)
358
+
359
+ # Copied from diffusers.models.unets.unet_2d_condition.UNet2DConditionModel.fuse_qkv_projections with FusedAttnProcessor2_0->FusedFluxAttnProcessor2_0
360
+ def fuse_qkv_projections(self):
361
+ """
362
+ Enables fused QKV projections. For self-attention modules, all projection matrices (i.e., query, key, value)
363
+ are fused. For cross-attention modules, key and value projection matrices are fused.
364
+
365
+ <Tip warning={true}>
366
+
367
+ This API is 🧪 experimental.
368
+
369
+ </Tip>
370
+ """
371
+ self.original_attn_processors = None
372
+
373
+ for _, attn_processor in self.attn_processors.items():
374
+ if "Added" in str(attn_processor.__class__.__name__):
375
+ raise ValueError("`fuse_qkv_projections()` is not supported for models having added KV projections.")
376
+
377
+ self.original_attn_processors = self.attn_processors
378
+
379
+ for module in self.modules():
380
+ if isinstance(module, Attention):
381
+ module.fuse_projections(fuse=True)
382
+
383
+ self.set_attn_processor(FusedFluxAttnProcessor2_0())
384
+
385
+ # Copied from diffusers.models.unets.unet_2d_condition.UNet2DConditionModel.unfuse_qkv_projections
386
+ def unfuse_qkv_projections(self):
387
+ """Disables the fused QKV projection if enabled.
388
+
389
+ <Tip warning={true}>
390
+
391
+ This API is 🧪 experimental.
392
+
393
+ </Tip>
394
+
395
+ """
396
+ if self.original_attn_processors is not None:
397
+ self.set_attn_processor(self.original_attn_processors)
398
+
399
+ def forward(
400
+ self,
401
+ hidden_states: torch.Tensor,
402
+ cond_hidden_states: torch.Tensor = None,
403
+ encoder_hidden_states: torch.Tensor = None,
404
+ pooled_projections: torch.Tensor = None,
405
+ timestep: torch.LongTensor = None,
406
+ img_ids: torch.Tensor = None,
407
+ txt_ids: torch.Tensor = None,
408
+ guidance: torch.Tensor = None,
409
+ joint_attention_kwargs: Optional[Dict[str, Any]] = None,
410
+ controlnet_block_samples=None,
411
+ controlnet_single_block_samples=None,
412
+ return_dict: bool = True,
413
+ controlnet_blocks_repeat: bool = False,
414
+ ) -> Union[torch.Tensor, Transformer2DModelOutput]:
415
+ if cond_hidden_states is not None:
416
+ use_condition = True
417
+ else:
418
+ use_condition = False
419
+
420
+ if joint_attention_kwargs is not None:
421
+ joint_attention_kwargs = joint_attention_kwargs.copy()
422
+ lora_scale = joint_attention_kwargs.pop("scale", 1.0)
423
+ else:
424
+ lora_scale = 1.0
425
+
426
+ if USE_PEFT_BACKEND:
427
+ # weight the lora layers by setting `lora_scale` for each PEFT layer
428
+ scale_lora_layers(self, lora_scale)
429
+ else:
430
+ if joint_attention_kwargs is not None and joint_attention_kwargs.get("scale", None) is not None:
431
+ logger.warning(
432
+ "Passing `scale` via `joint_attention_kwargs` when not using the PEFT backend is ineffective."
433
+ )
434
+
435
+ hidden_states = self.x_embedder(hidden_states)
436
+
437
+ timestep = timestep.to(hidden_states.dtype) * 1000
438
+ if guidance is not None:
439
+ guidance = guidance.to(hidden_states.dtype) * 1000
440
+ else:
441
+ guidance = None
442
+
443
+ temb = (
444
+ self.time_text_embed(timestep, pooled_projections)
445
+ if guidance is None
446
+ else self.time_text_embed(timestep, guidance, pooled_projections)
447
+ )
448
+
449
+ cond_temb = (
450
+ self.time_text_embed(torch.ones_like(timestep) * 0, pooled_projections)
451
+ if guidance is None
452
+ else self.time_text_embed(
453
+ torch.ones_like(timestep) * 0, guidance, pooled_projections
454
+ )
455
+ )
456
+
457
+ encoder_hidden_states = self.context_embedder(encoder_hidden_states)
458
+
459
+ if txt_ids.ndim == 3:
460
+ logger.warning(
461
+ "Passing `txt_ids` 3d torch.Tensor is deprecated."
462
+ "Please remove the batch dimension and pass it as a 2d torch Tensor"
463
+ )
464
+ txt_ids = txt_ids[0]
465
+ if img_ids.ndim == 3:
466
+ logger.warning(
467
+ "Passing `img_ids` 3d torch.Tensor is deprecated."
468
+ "Please remove the batch dimension and pass it as a 2d torch Tensor"
469
+ )
470
+ img_ids = img_ids[0]
471
+
472
+ ids = torch.cat((txt_ids, img_ids), dim=0)
473
+ image_rotary_emb = self.pos_embed(ids)
474
+
475
+ if joint_attention_kwargs is not None and "ip_adapter_image_embeds" in joint_attention_kwargs:
476
+ ip_adapter_image_embeds = joint_attention_kwargs.pop("ip_adapter_image_embeds")
477
+ ip_hidden_states = self.encoder_hid_proj(ip_adapter_image_embeds)
478
+ joint_attention_kwargs.update({"ip_hidden_states": ip_hidden_states})
479
+
480
+ if joint_attention_kwargs is None:
481
+ joint_attention_kwargs = {}
482
+
483
+ for index_block, block in enumerate(self.transformer_blocks):
484
+ if torch.is_grad_enabled() and self.gradient_checkpointing:
485
+ encoder_hidden_states, hidden_states, cond_hidden_states = self._gradient_checkpointing_func(
486
+ block,
487
+ hidden_states,
488
+ cond_hidden_states if use_condition else None,
489
+ encoder_hidden_states,
490
+ temb,
491
+ cond_temb if use_condition else None,
492
+ image_rotary_emb,
493
+ joint_attention_kwargs,
494
+ )
495
+ else:
496
+ encoder_hidden_states, hidden_states, cond_hidden_states = block(
497
+ hidden_states=hidden_states,
498
+ encoder_hidden_states=encoder_hidden_states,
499
+ cond_hidden_states=cond_hidden_states if use_condition else None,
500
+ temb=temb,
501
+ cond_temb=cond_temb if use_condition else None,
502
+ image_rotary_emb=image_rotary_emb,
503
+ joint_attention_kwargs=joint_attention_kwargs,
504
+ )
505
+
506
+ # controlnet residual
507
+ if controlnet_block_samples is not None:
508
+ interval_control = len(self.transformer_blocks) / len(controlnet_block_samples)
509
+ interval_control = int(np.ceil(interval_control))
510
+ # For Xlabs ControlNet.
511
+ if controlnet_blocks_repeat:
512
+ hidden_states = (
513
+ hidden_states + controlnet_block_samples[index_block % len(controlnet_block_samples)]
514
+ )
515
+ else:
516
+ hidden_states = hidden_states + controlnet_block_samples[index_block // interval_control]
517
+ hidden_states = torch.cat([encoder_hidden_states, hidden_states], dim=1)
518
+
519
+ for index_block, block in enumerate(self.single_transformer_blocks):
520
+ if torch.is_grad_enabled() and self.gradient_checkpointing:
521
+ hidden_states, cond_hidden_states = self._gradient_checkpointing_func(
522
+ block,
523
+ hidden_states,
524
+ cond_hidden_states if use_condition else None,
525
+ temb,
526
+ cond_temb if use_condition else None,
527
+ image_rotary_emb,
528
+ joint_attention_kwargs,
529
+ )
530
+ else:
531
+ hidden_states, cond_hidden_states = block(
532
+ hidden_states=hidden_states,
533
+ cond_hidden_states=cond_hidden_states if use_condition else None,
534
+ temb=temb,
535
+ cond_temb=cond_temb if use_condition else None,
536
+ image_rotary_emb=image_rotary_emb,
537
+ joint_attention_kwargs=joint_attention_kwargs,
538
+ )
539
+
540
+ # controlnet residual
541
+ if controlnet_single_block_samples is not None:
542
+ interval_control = len(self.single_transformer_blocks) / len(controlnet_single_block_samples)
543
+ interval_control = int(np.ceil(interval_control))
544
+ hidden_states[:, encoder_hidden_states.shape[1] :, ...] = (
545
+ hidden_states[:, encoder_hidden_states.shape[1] :, ...]
546
+ + controlnet_single_block_samples[index_block // interval_control]
547
+ )
548
+
549
+ hidden_states = hidden_states[:, encoder_hidden_states.shape[1] :, ...]
550
+
551
+ hidden_states = self.norm_out(hidden_states, temb)
552
+ output = self.proj_out(hidden_states)
553
+
554
+ if USE_PEFT_BACKEND:
555
+ # remove `lora_scale` from each PEFT layer
556
+ unscale_lora_layers(self, lora_scale)
557
+
558
+ if not return_dict:
559
+ return (output,)
560
+
561
+ return Transformer2DModelOutput(sample=output)
examples/bg_beach.jpg ADDED

Git LFS Details

  • SHA256: c06f6906ef2e94c56221cff67268e2a8243b2f0ff9b1626081bbbea729a6e15c
  • Pointer size: 131 Bytes
  • Size of remote file: 333 kB
examples/bg_landscape.jpg ADDED
examples/bg_tent.jpg ADDED

Git LFS Details

  • SHA256: 32a434795ef91e315435e927ed1568986391320351fc7c04f253e3b0074c08e3
  • Pointer size: 131 Bytes
  • Size of remote file: 175 kB
examples/obj_cake.jpg ADDED

Git LFS Details

  • SHA256: 67e9eb0f1c87af2e3b909b7a3f2c033ab3685ba33cce05a81de704923ac46947
  • Pointer size: 131 Bytes
  • Size of remote file: 135 kB
examples/obj_dog.jpg ADDED

Git LFS Details

  • SHA256: 6385da70b7991fc95287c5242c3e2addc982c35bd024582978b7edb2a1370838
  • Pointer size: 131 Bytes
  • Size of remote file: 114 kB
examples/obj_ducks.jpg ADDED
requirements.txt ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ diffusers==0.35.1
2
+ transformers==4.53.1
3
+ peft==0.17.1
4
+ accelerate==1.10.1
5
+ safetensors
6
+ sentencepiece
7
+ protobuf
8
+ einops
9
+ kornia
10
+ timm
11
+ numpy
12
+ pillow
13
+ opencv-python-headless
14
+ scipy
15
+ torchvision
16
+ rembg==2.0.67
17
+ onnxruntime==1.23.1