multimodalart HF Staff commited on
Commit
834d1cf
·
verified ·
1 Parent(s): 9007857

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +9 -0
  2. README.md +29 -6
  3. app.py +288 -0
  4. assets/exemplar_origin_0.png +3 -0
  5. assets/exemplar_origin_1.png +3 -0
  6. assets/exemplar_origin_2.png +3 -0
  7. assets/exemplar_retouched_0.png +3 -0
  8. assets/exemplar_retouched_1.png +3 -0
  9. assets/exemplar_retouched_2.png +3 -0
  10. assets/query_0.png +3 -0
  11. assets/query_1.png +3 -0
  12. assets/query_2.png +3 -0
  13. diffsynth/__init__.py +1 -0
  14. diffsynth/configs/__init__.py +2 -0
  15. diffsynth/configs/model_configs.py +70 -0
  16. diffsynth/configs/vram_management_module_maps.py +24 -0
  17. diffsynth/core/__init__.py +4 -0
  18. diffsynth/core/attention/__init__.py +1 -0
  19. diffsynth/core/attention/attention.py +121 -0
  20. diffsynth/core/gradient/__init__.py +1 -0
  21. diffsynth/core/gradient/gradient_checkpoint.py +34 -0
  22. diffsynth/core/loader/__init__.py +3 -0
  23. diffsynth/core/loader/config.py +119 -0
  24. diffsynth/core/loader/file.py +121 -0
  25. diffsynth/core/loader/model.py +79 -0
  26. diffsynth/core/vram/__init__.py +2 -0
  27. diffsynth/core/vram/disk_map.py +93 -0
  28. diffsynth/core/vram/initialization.py +21 -0
  29. diffsynth/core/vram/layers.py +475 -0
  30. diffsynth/diffusion/__init__.py +1 -0
  31. diffsynth/diffusion/base_pipeline.py +439 -0
  32. diffsynth/diffusion/flow_match.py +179 -0
  33. diffsynth/models/__init__.py +0 -0
  34. diffsynth/models/general_modules.py +139 -0
  35. diffsynth/models/model_loader.py +111 -0
  36. diffsynth/models/qwen_image_connector.py +260 -0
  37. diffsynth/models/qwen_image_controlnet.py +56 -0
  38. diffsynth/models/qwen_image_dit.py +572 -0
  39. diffsynth/models/qwen_image_extractor.py +415 -0
  40. diffsynth/models/qwen_image_learnable_query.py +19 -0
  41. diffsynth/models/qwen_image_mae.py +106 -0
  42. diffsynth/models/qwen_image_rformer.py +512 -0
  43. diffsynth/models/qwen_image_text_encoder.py +251 -0
  44. diffsynth/models/qwen_image_vae.py +876 -0
  45. diffsynth/pipelines/__init__.py +0 -0
  46. diffsynth/pipelines/qwen_image.py +1374 -0
  47. diffsynth/utils/__init__.py +0 -0
  48. diffsynth/utils/controlnet/__init__.py +1 -0
  49. diffsynth/utils/controlnet/controlnet_input.py +13 -0
  50. diffsynth/utils/lora/__init__.py +1 -0
.gitattributes CHANGED
@@ -33,3 +33,12 @@ 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
+ assets/exemplar_origin_0.png filter=lfs diff=lfs merge=lfs -text
37
+ assets/exemplar_origin_1.png filter=lfs diff=lfs merge=lfs -text
38
+ assets/exemplar_origin_2.png filter=lfs diff=lfs merge=lfs -text
39
+ assets/exemplar_retouched_0.png filter=lfs diff=lfs merge=lfs -text
40
+ assets/exemplar_retouched_1.png filter=lfs diff=lfs merge=lfs -text
41
+ assets/exemplar_retouched_2.png filter=lfs diff=lfs merge=lfs -text
42
+ assets/query_0.png filter=lfs diff=lfs merge=lfs -text
43
+ assets/query_1.png filter=lfs diff=lfs merge=lfs -text
44
+ assets/query_2.png filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,13 +1,36 @@
1
  ---
2
- title: Mirror Ppr
3
- emoji: 📈
4
  colorFrom: purple
5
- colorTo: blue
6
  sdk: gradio
7
  sdk_version: 6.20.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: MirrorPPR
3
+ emoji: 🪞
4
  colorFrom: purple
5
+ colorTo: indigo
6
  sdk: gradio
7
  sdk_version: 6.20.0
 
8
  app_file: app.py
9
+ short_description: Exemplar-based portrait photo retouching
10
+ python_version: "3.12"
11
+ startup_duration_timeout: 1h
12
  ---
13
 
14
+ # MirrorPPR: Exemplar-Based Portrait Photo Retouching
15
+
16
+ This Space demonstrates [MirrorPPR](https://arxiv.org/abs/2606.29308), a novel framework for
17
+ exemplar-based portrait photo retouching. Given an exemplar pair (an original face image and its
18
+ retouched version), the model infers the retouching operations and applies them to a new query image.
19
+
20
+ ## Usage
21
+
22
+ 1. Upload a query face image you want to retouch.
23
+ 2. Provide an exemplar pair: the original and retouched version of another face showing the desired
24
+ retouching style. You can quickly load a pre-packaged exemplar pair from the template gallery.
25
+ 3. Click "Retouch" to apply the exemplar's retouching operations to your query image.
26
+
27
+ ## Model
28
+
29
+ This Space uses the [SJTU-DENG-Lab/MirrorPPR-Face](https://huggingface.co/SJTU-DENG-Lab/MirrorPPR-Face)
30
+ model, built on top of Qwen-Image-Edit with a Retouching Operation Extractor (MAE + R-Former + Connector)
31
+ and LoRA adaptation.
32
+
33
+ ## References
34
+
35
+ - Paper: [MirrorPPR: Exemplar-Based Portrait Photo Retouching](https://arxiv.org/abs/2606.29308)
36
+ - GitHub: [SJTU-DENG-Lab/MirrorPPR](https://github.com/SJTU-DENG-Lab/MirrorPPR)
app.py ADDED
@@ -0,0 +1,288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
4
+
5
+ import spaces # MUST come before torch / any CUDA-touching import
6
+ import torch
7
+ import gradio as gr
8
+ import glob
9
+ from PIL import Image
10
+ from huggingface_hub import snapshot_download
11
+
12
+ from mirrorppr.data.image_ops import round_to_multiple
13
+ from diffsynth import load_state_dict
14
+ from diffsynth.pipelines.qwen_image import ModelConfig, QwenImagePipeline
15
+
16
+ MODEL_ID = "SJTU-DENG-Lab/MirrorPPR-Face"
17
+
18
+
19
+ def _glob_required(pattern):
20
+ files = sorted(glob.glob(pattern))
21
+ if not files:
22
+ raise FileNotFoundError(f"No files matched: {pattern}")
23
+ return files
24
+
25
+
26
+ def _build_paths(weights_root, qwen_root):
27
+ qwen = qwen_root or os.path.join(weights_root, "qwen_image_edit")
28
+ face = os.path.join(weights_root, "mirrorppr_face")
29
+ return {
30
+ "dit": _glob_required(os.path.join(qwen, "transformer", "diffusion_pytorch_model*.safetensors")),
31
+ "text_encoder": _glob_required(os.path.join(qwen, "text_encoder", "model*.safetensors")),
32
+ "vae": os.path.join(qwen, "vae", "diffusion_pytorch_model.safetensors"),
33
+ "processor": os.path.join(qwen, "processor"),
34
+ "mae": os.path.join(face, "mae", "mae_pretrained.safetensors"),
35
+ "rformer": os.path.join(face, "rformer", "rformer.safetensors"),
36
+ "connector": os.path.join(face, "connector", "connector.safetensors"),
37
+ "lora": os.path.join(face, "lora", "lora.safetensors"),
38
+ }
39
+
40
+
41
+ print("Downloading model weights from Hugging Face Hub...")
42
+ _local_root = snapshot_download(repo_id=MODEL_ID)
43
+ _paths = _build_paths(_local_root, None)
44
+ print(f"Model downloaded to: {_local_root}")
45
+
46
+ pipe = QwenImagePipeline.from_pretrained(
47
+ torch_dtype=torch.bfloat16,
48
+ device="cuda",
49
+ model_configs=[
50
+ ModelConfig(path=_paths["dit"]),
51
+ ModelConfig(path=_paths["text_encoder"]),
52
+ ModelConfig(path=_paths["vae"]),
53
+ ModelConfig(path=_paths["mae"]),
54
+ ModelConfig(path=_paths["rformer"]),
55
+ ModelConfig(path=_paths["connector"]),
56
+ ],
57
+ tokenizer_config=None,
58
+ processor_config=ModelConfig(path=_paths["processor"]),
59
+ )
60
+ if pipe.rformer is None:
61
+ raise RuntimeError("R-Former module failed to load.")
62
+ if not hasattr(pipe, "connector") or pipe.connector is None:
63
+ raise RuntimeError("Connector module failed to load.")
64
+ pipe.rformer.load_state_dict(load_state_dict(_paths["rformer"]))
65
+ pipe.connector.load_state_dict(load_state_dict(_paths["connector"]))
66
+ pipe.load_lora(pipe.dit, _paths["lora"])
67
+ print("MirrorPPR-Face pipeline loaded successfully.")
68
+
69
+
70
+ # Pre-packaged exemplar pairs for quick selection
71
+ EXEMPLAR_PAIRS = [
72
+ {
73
+ "name": "Style 1: Eye enlargement + mouth adjustments",
74
+ "origin": "assets/exemplar_origin_0.png",
75
+ "retouched": "assets/exemplar_retouched_0.png",
76
+ },
77
+ {
78
+ "name": "Style 2: Eye enlargement + nose lengthening",
79
+ "origin": "assets/exemplar_origin_1.png",
80
+ "retouched": "assets/exemplar_retouched_1.png",
81
+ },
82
+ {
83
+ "name": "Style 3: Eye enlargement + lip plump",
84
+ "origin": "assets/exemplar_origin_2.png",
85
+ "retouched": "assets/exemplar_retouched_2.png",
86
+ },
87
+ ]
88
+
89
+
90
+ def _on_exemplar_select(evt: gr.SelectData):
91
+ """Load a pre-packaged exemplar pair when the user clicks a gallery item."""
92
+ idx = evt.index
93
+ if isinstance(idx, list):
94
+ idx = idx[0] if idx else 0
95
+ idx = int(idx)
96
+ if 0 <= idx < len(EXEMPLAR_PAIRS):
97
+ pair = EXEMPLAR_PAIRS[idx]
98
+ return pair["origin"], pair["retouched"]
99
+ return None, None
100
+
101
+
102
+ @spaces.GPU(duration=180)
103
+ def retouch(
104
+ query_image,
105
+ exemplar_origin,
106
+ exemplar_retouched,
107
+ steps=40,
108
+ seed=123,
109
+ cfg_scale=4.0,
110
+ ):
111
+ """Apply exemplar-based portrait photo retouching to a query image.
112
+
113
+ Given an exemplar pair (an original face and its retouched version),
114
+ this function infers the retouching operations and applies them to
115
+ a new query face image.
116
+
117
+ Args:
118
+ query_image: The face image to be retouched.
119
+ exemplar_origin: The original (pre-retouch) exemplar image.
120
+ exemplar_retouched: The retouched exemplar image.
121
+ steps: Number of diffusion inference steps (default 40).
122
+ seed: Random seed for reproducibility (default 123).
123
+ cfg_scale: Classifier-free guidance scale (default 4.0).
124
+
125
+ Returns:
126
+ The retouched query image.
127
+ """
128
+ if query_image is None:
129
+ raise gr.Error("Please provide a query image.")
130
+ if exemplar_origin is None or exemplar_retouched is None:
131
+ raise gr.Error("Please provide both exemplar images (origin and retouched).")
132
+
133
+ query = Image.fromarray(query_image).convert("RGB")
134
+ ex_origin = Image.fromarray(exemplar_origin).convert("RGB")
135
+ ex_target = Image.fromarray(exemplar_retouched).convert("RGB")
136
+
137
+ width, height = query.size
138
+ width = round_to_multiple(width, 16)
139
+ height = round_to_multiple(height, 16)
140
+
141
+ result = pipe(
142
+ "",
143
+ example_origin=ex_origin,
144
+ example_target=ex_target,
145
+ edit_image=query,
146
+ seed=int(seed),
147
+ num_inference_steps=int(steps),
148
+ height=height,
149
+ width=width,
150
+ edit_image_auto_resize=False,
151
+ cfg_scale=cfg_scale,
152
+ )
153
+ return result
154
+
155
+
156
+ CSS = """
157
+ #col-container { max-width: 1200px; margin: 0 auto; }
158
+ .dark .gradio-container { color: var(--body-text-color); }
159
+ """
160
+
161
+ with gr.Blocks(theme=gr.themes.Citrus(), css=CSS) as demo:
162
+ gr.Markdown(
163
+ """
164
+ # MirrorPPR: Exemplar-Based Portrait Photo Retouching
165
+
166
+ Upload a face image (query) and provide an exemplar pair (original → retouched).
167
+ The model infers the retouching operations from the exemplar pair and applies
168
+ them to your query image. Try a pre-packaged exemplar from the gallery below.
169
+
170
+ [Paper](https://arxiv.org/abs/2606.29308) · [GitHub](https://github.com/SJTU-DENG-Lab/MirrorPPR) · [Model](https://huggingface.co/SJTU-DENG-Lab/MirrorPPR-Face)
171
+ """
172
+ )
173
+
174
+ with gr.Row():
175
+ # Left column: inputs
176
+ with gr.Column(scale=1):
177
+ gr.Markdown("### Query Image (to retouch)")
178
+ query_img = gr.Image(
179
+ label="Query Image",
180
+ type="numpy",
181
+ height=300,
182
+ )
183
+ gr.Markdown("### Exemplar Pair (reference retouching style)")
184
+ ex_origin_img = gr.Image(
185
+ label="Exemplar Original",
186
+ type="numpy",
187
+ height=200,
188
+ )
189
+ ex_retouched_img = gr.Image(
190
+ label="Exemplar Retouched",
191
+ type="numpy",
192
+ height=200,
193
+ )
194
+
195
+ gr.Markdown("### Quick Exemplar Templates")
196
+ exemplar_gallery = gr.Gallery(
197
+ label="Click a template to load an exemplar pair",
198
+ value=[
199
+ (pair["origin"], pair["name"])
200
+ for pair in EXEMPLAR_PAIRS
201
+ ],
202
+ columns=3,
203
+ height=150,
204
+ show_label=False,
205
+ allow_preview=False,
206
+ )
207
+
208
+ with gr.Accordion("Advanced settings", open=False):
209
+ steps_slider = gr.Slider(
210
+ label="Inference steps",
211
+ minimum=10,
212
+ maximum=80,
213
+ value=40,
214
+ step=1,
215
+ )
216
+ seed_input = gr.Number(
217
+ label="Seed",
218
+ value=123,
219
+ precision=0,
220
+ )
221
+ cfg_slider = gr.Slider(
222
+ label="CFG scale",
223
+ minimum=1.0,
224
+ maximum=10.0,
225
+ value=4.0,
226
+ step=0.5,
227
+ )
228
+
229
+ run_btn = gr.Button("Retouch", variant="primary", size="lg")
230
+
231
+ # Right column: output
232
+ with gr.Column(scale=1):
233
+ gr.Markdown("### Retouched Result")
234
+ output_img = gr.Image(
235
+ label="Retouched Query",
236
+ type="pil",
237
+ height=400,
238
+ )
239
+
240
+ # Wire up exemplar gallery selection
241
+ exemplar_gallery.select(
242
+ fn=_on_exemplar_select,
243
+ outputs=[ex_origin_img, ex_retouched_img],
244
+ )
245
+
246
+ # Wire up the run button
247
+ run_btn.click(
248
+ fn=retouch,
249
+ inputs=[query_img, ex_origin_img, ex_retouched_img, steps_slider, seed_input, cfg_slider],
250
+ outputs=output_img,
251
+ api_name="retouch",
252
+ )
253
+
254
+ gr.Examples(
255
+ examples=[
256
+ [
257
+ "assets/query_0.png",
258
+ "assets/exemplar_origin_0.png",
259
+ "assets/exemplar_retouched_0.png",
260
+ 40,
261
+ 123,
262
+ 4.0,
263
+ ],
264
+ [
265
+ "assets/query_1.png",
266
+ "assets/exemplar_origin_1.png",
267
+ "assets/exemplar_retouched_1.png",
268
+ 40,
269
+ 123,
270
+ 4.0,
271
+ ],
272
+ [
273
+ "assets/query_2.png",
274
+ "assets/exemplar_origin_2.png",
275
+ "assets/exemplar_retouched_2.png",
276
+ 40,
277
+ 123,
278
+ 4.0,
279
+ ],
280
+ ],
281
+ inputs=[query_img, ex_origin_img, ex_retouched_img, steps_slider, seed_input, cfg_slider],
282
+ outputs=output_img,
283
+ fn=retouch,
284
+ cache_examples=True,
285
+ cache_mode="lazy",
286
+ )
287
+
288
+ demo.launch(mcp_server=True)
assets/exemplar_origin_0.png ADDED

Git LFS Details

  • SHA256: 01a4f57f9de2d335f0ac3d79d0566407ca923f5a43f1ddb6ae5c08309e4380e6
  • Pointer size: 132 Bytes
  • Size of remote file: 1.42 MB
assets/exemplar_origin_1.png ADDED

Git LFS Details

  • SHA256: a0db68752cf2403480ae05f1c7f95b2f09e294f4478a1193d6448ef741463241
  • Pointer size: 132 Bytes
  • Size of remote file: 1.83 MB
assets/exemplar_origin_2.png ADDED

Git LFS Details

  • SHA256: 5ee357983d9b2b66b732b15fd49a7c51f8fcc97c19bb143d85e336f31e2e91ce
  • Pointer size: 132 Bytes
  • Size of remote file: 1.71 MB
assets/exemplar_retouched_0.png ADDED

Git LFS Details

  • SHA256: 7c1001b792ee846f4d0f6abae6696b7c8d96b86dc5b843b9dba472583eed2c2c
  • Pointer size: 132 Bytes
  • Size of remote file: 1.38 MB
assets/exemplar_retouched_1.png ADDED

Git LFS Details

  • SHA256: 2a8c2672f9eb3438ac3da4fb3db5a18ee5d5ad6aa43ec187064e8fda17a03f85
  • Pointer size: 132 Bytes
  • Size of remote file: 1.81 MB
assets/exemplar_retouched_2.png ADDED

Git LFS Details

  • SHA256: 9e4caaeea3cca2ff868571feeedc07656cecc84b2f3701c066838be0a78ad8ef
  • Pointer size: 132 Bytes
  • Size of remote file: 1.68 MB
assets/query_0.png ADDED

Git LFS Details

  • SHA256: 9cdb6992bceff0a0c23aff781b1cad53fa379ef4a7d752f39e3096785ce969ee
  • Pointer size: 132 Bytes
  • Size of remote file: 1.05 MB
assets/query_1.png ADDED

Git LFS Details

  • SHA256: 595d327414a16c9475d1a40e11ec2684207b9cfca1a97d9b50e1eeb0e3a9f348
  • Pointer size: 132 Bytes
  • Size of remote file: 1.66 MB
assets/query_2.png ADDED

Git LFS Details

  • SHA256: 29cb87235b3af3b090e1eaeee086d11f2b0600c776b763aa478491fbfd7b3087
  • Pointer size: 132 Bytes
  • Size of remote file: 1.69 MB
diffsynth/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .core import *
diffsynth/configs/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ from .model_configs import MODEL_CONFIGS
2
+ from .vram_management_module_maps import VRAM_MANAGEMENT_MODULE_MAPS
diffsynth/configs/model_configs.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ qwen_image_series = [
2
+ {
3
+ "model_hash": "0319a1cb19835fb510907dd3367c95ff",
4
+ "model_name": "qwen_image_dit",
5
+ "model_class": "diffsynth.models.qwen_image_dit.QwenImageDiT",
6
+ },
7
+ {
8
+ "model_hash": "8004730443f55db63092006dd9f7110e",
9
+ "model_name": "qwen_image_text_encoder",
10
+ "model_class": "diffsynth.models.qwen_image_text_encoder.QwenImageTextEncoder",
11
+ "state_dict_converter": "diffsynth.utils.state_dict_converters.qwen_image_text_encoder.QwenImageTextEncoderStateDictConverter",
12
+ },
13
+ {
14
+ "model_hash": "ed4ea5824d55ec3107b09815e318123a",
15
+ "model_name": "qwen_image_vae",
16
+ "model_class": "diffsynth.models.qwen_image_vae.QwenImageVAE",
17
+ },
18
+ {
19
+ "model_hash": "073bce9cf969e317e5662cd570c3e79c",
20
+ "model_name": "qwen_image_blockwise_controlnet",
21
+ "model_class": "diffsynth.models.qwen_image_controlnet.QwenImageBlockWiseControlNet",
22
+ },
23
+ {
24
+ "model_hash": "a9e54e480a628f0b956a688a81c33bab",
25
+ "model_name": "qwen_image_blockwise_controlnet",
26
+ "model_class": "diffsynth.models.qwen_image_controlnet.QwenImageBlockWiseControlNet",
27
+ "extra_kwargs": {"additional_in_dim": 4},
28
+ },
29
+ {
30
+ "model_hash": "49ae821b996dc7e19a5e8d9d4f3c5315",
31
+ "model_name": "qwen_image_connector",
32
+ "model_class": "diffsynth.models.qwen_image_connector.QwenImageConnector",
33
+ "extra_kwargs": {"input_dim": 768, "num_layers": 6},
34
+ },
35
+ {
36
+ "model_hash": "7dd3640ddce93380be19b6c86f9e7e64",
37
+ "model_name": "qwen_image_connector",
38
+ "model_class": "diffsynth.models.qwen_image_connector.QwenImageConnector",
39
+ "extra_kwargs": {"num_layers": 6},
40
+ },
41
+ {
42
+ "model_hash": "7b45ff1c1703e389ef61c8ab12babc4d",
43
+ "model_name": "qwen_image_learnable_query",
44
+ "model_class": "diffsynth.models.qwen_image_learnable_query.QwenImageLearnableQuery",
45
+ "extra_kwargs": {"query_length": 256},
46
+ },
47
+ {
48
+ "model_hash": "5dbd9a25c517478a8d2f35b01da53e9a",
49
+ "model_name": "qwen_image_extractor",
50
+ "model_class": "diffsynth.models.qwen_image_extractor.QwenImageExtractor",
51
+ },
52
+ {
53
+ "model_hash": "bc4b4d58554d31694168550738ae8286",
54
+ "model_name": "qwen_image_rformer",
55
+ "model_class": "diffsynth.models.qwen_image_rformer.RFormer",
56
+ },
57
+ {
58
+ "model_hash": "aea189b86b5df7cffc8ec81d76fde9a5",
59
+ "model_name": "qwen_image_rformer2D",
60
+ "model_class": "diffsynth.models.qwen_image_rformer.RFormer2D",
61
+ },
62
+ {
63
+ "model_hash": "bc4aeac1e11041ca661685e9ed1e3ead",
64
+ "model_name": "qwen_image_mae",
65
+ "model_class": "diffsynth.models.qwen_image_mae.QwenImageMAE",
66
+ "state_dict_converter": "diffsynth.utils.state_dict_converters.qwen_image_mae.QwenImageMAEStateDictConverter",
67
+ },
68
+ ]
69
+
70
+ MODEL_CONFIGS = qwen_image_series
diffsynth/configs/vram_management_module_maps.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ VRAM_MANAGEMENT_MODULE_MAPS = {
2
+ "diffsynth.models.qwen_image_dit.QwenImageDiT": {
3
+ "diffsynth.models.qwen_image_dit.RMSNorm": "diffsynth.core.vram.layers.AutoWrappedModule",
4
+ "torch.nn.Linear": "diffsynth.core.vram.layers.AutoWrappedLinear",
5
+ },
6
+ "diffsynth.models.qwen_image_text_encoder.QwenImageTextEncoder": {
7
+ "torch.nn.Linear": "diffsynth.core.vram.layers.AutoWrappedLinear",
8
+ "torch.nn.Embedding": "diffsynth.core.vram.layers.AutoWrappedModule",
9
+ "transformers.models.qwen2_5_vl.modeling_qwen2_5_vl.Qwen2_5_VLRotaryEmbedding": "diffsynth.core.vram.layers.AutoWrappedModule",
10
+ "transformers.models.qwen2_5_vl.modeling_qwen2_5_vl.Qwen2RMSNorm": "diffsynth.core.vram.layers.AutoWrappedModule",
11
+ "transformers.models.qwen2_5_vl.modeling_qwen2_5_vl.Qwen2_5_VisionPatchEmbed": "diffsynth.core.vram.layers.AutoWrappedModule",
12
+ "transformers.models.qwen2_5_vl.modeling_qwen2_5_vl.Qwen2_5_VisionRotaryEmbedding": "diffsynth.core.vram.layers.AutoWrappedModule",
13
+ },
14
+ "diffsynth.models.qwen_image_vae.QwenImageVAE": {
15
+ "torch.nn.Linear": "diffsynth.core.vram.layers.AutoWrappedLinear",
16
+ "torch.nn.Conv3d": "diffsynth.core.vram.layers.AutoWrappedModule",
17
+ "torch.nn.Conv2d": "diffsynth.core.vram.layers.AutoWrappedModule",
18
+ "diffsynth.models.qwen_image_vae.QwenImageRMS_norm": "diffsynth.core.vram.layers.AutoWrappedModule",
19
+ },
20
+ "diffsynth.models.qwen_image_controlnet.BlockWiseControlBlock": {
21
+ "diffsynth.models.qwen_image_dit.RMSNorm": "diffsynth.core.vram.layers.AutoWrappedModule",
22
+ "torch.nn.Linear": "diffsynth.core.vram.layers.AutoWrappedLinear",
23
+ },
24
+ }
diffsynth/core/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from .attention import *
2
+ from .gradient import *
3
+ from .loader import *
4
+ from .vram import *
diffsynth/core/attention/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .attention import attention_forward
diffsynth/core/attention/attention.py ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch, os
2
+ from einops import rearrange
3
+
4
+
5
+ try:
6
+ import flash_attn_interface
7
+ FLASH_ATTN_3_AVAILABLE = True
8
+ except ModuleNotFoundError:
9
+ FLASH_ATTN_3_AVAILABLE = False
10
+
11
+ try:
12
+ import flash_attn
13
+ FLASH_ATTN_2_AVAILABLE = True
14
+ except ModuleNotFoundError:
15
+ FLASH_ATTN_2_AVAILABLE = False
16
+
17
+ try:
18
+ from sageattention import sageattn
19
+ SAGE_ATTN_AVAILABLE = True
20
+ except ModuleNotFoundError:
21
+ SAGE_ATTN_AVAILABLE = False
22
+
23
+ try:
24
+ import xformers.ops as xops
25
+ XFORMERS_AVAILABLE = True
26
+ except ModuleNotFoundError:
27
+ XFORMERS_AVAILABLE = False
28
+
29
+
30
+ def initialize_attention_priority():
31
+ if os.environ.get('DIFFSYNTH_ATTENTION_IMPLEMENTATION') is not None:
32
+ return os.environ.get('DIFFSYNTH_ATTENTION_IMPLEMENTATION').lower()
33
+ elif FLASH_ATTN_3_AVAILABLE:
34
+ return "flash_attention_3"
35
+ elif FLASH_ATTN_2_AVAILABLE:
36
+ return "flash_attention_2"
37
+ elif SAGE_ATTN_AVAILABLE:
38
+ return "sage_attention"
39
+ elif XFORMERS_AVAILABLE:
40
+ return "xformers"
41
+ else:
42
+ return "torch"
43
+
44
+
45
+ ATTENTION_IMPLEMENTATION = initialize_attention_priority()
46
+
47
+
48
+ def rearrange_qkv(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", required_in_pattern="b n s d", dims=None):
49
+ dims = {} if dims is None else dims
50
+ if q_pattern != required_in_pattern:
51
+ q = rearrange(q, f"{q_pattern} -> {required_in_pattern}", **dims)
52
+ if k_pattern != required_in_pattern:
53
+ k = rearrange(k, f"{k_pattern} -> {required_in_pattern}", **dims)
54
+ if v_pattern != required_in_pattern:
55
+ v = rearrange(v, f"{q_pattern} -> {required_in_pattern}", **dims)
56
+ return q, k, v
57
+
58
+
59
+ def rearrange_out(out: torch.Tensor, out_pattern="b n s d", required_out_pattern="b n s d", dims=None):
60
+ dims = {} if dims is None else dims
61
+ if out_pattern != required_out_pattern:
62
+ out = rearrange(out, f"{required_out_pattern} -> {out_pattern}", **dims)
63
+ return out
64
+
65
+
66
+ def torch_sdpa(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, attn_mask=None, scale=None):
67
+ required_in_pattern, required_out_pattern= "b n s d", "b n s d"
68
+ q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
69
+ out = torch.nn.functional.scaled_dot_product_attention(q, k, v, attn_mask, scale=scale)
70
+ out = rearrange_out(out, out_pattern, required_out_pattern, dims)
71
+ return out
72
+
73
+
74
+ def flash_attention_3(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None):
75
+ required_in_pattern, required_out_pattern= "b s n d", "b s n d"
76
+ q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
77
+ out = flash_attn_interface.flash_attn_func(q, k, v, softmax_scale=scale)
78
+ if isinstance(out, tuple):
79
+ out = out[0]
80
+ out = rearrange_out(out, out_pattern, required_out_pattern, dims)
81
+ return out
82
+
83
+
84
+ def flash_attention_2(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None):
85
+ required_in_pattern, required_out_pattern= "b s n d", "b s n d"
86
+ q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
87
+ out = flash_attn.flash_attn_func(q, k, v, softmax_scale=scale)
88
+ out = rearrange_out(out, out_pattern, required_out_pattern, dims)
89
+ return out
90
+
91
+
92
+ def sage_attention(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None):
93
+ required_in_pattern, required_out_pattern= "b n s d", "b n s d"
94
+ q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
95
+ out = sageattn(q, k, v, sm_scale=scale)
96
+ out = rearrange_out(out, out_pattern, required_out_pattern, dims)
97
+ return out
98
+
99
+
100
+ def xformers_attention(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None):
101
+ required_in_pattern, required_out_pattern= "b s n d", "b s n d"
102
+ q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
103
+ out = xops.memory_efficient_attention(q, k, v, scale=scale)
104
+ out = rearrange_out(out, out_pattern, required_out_pattern, dims)
105
+ return out
106
+
107
+
108
+ def attention_forward(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, attn_mask=None, scale=None, compatibility_mode=False):
109
+ if compatibility_mode or (attn_mask is not None):
110
+ return torch_sdpa(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, attn_mask=attn_mask, scale=scale)
111
+ else:
112
+ if ATTENTION_IMPLEMENTATION == "flash_attention_3":
113
+ return flash_attention_3(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale)
114
+ elif ATTENTION_IMPLEMENTATION == "flash_attention_2":
115
+ return flash_attention_2(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale)
116
+ elif ATTENTION_IMPLEMENTATION == "sage_attention":
117
+ return sage_attention(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale)
118
+ elif ATTENTION_IMPLEMENTATION == "xformers":
119
+ return xformers_attention(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale)
120
+ else:
121
+ return torch_sdpa(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale)
diffsynth/core/gradient/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .gradient_checkpoint import gradient_checkpoint_forward
diffsynth/core/gradient/gradient_checkpoint.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+
4
+ def create_custom_forward(module):
5
+ def custom_forward(*inputs, **kwargs):
6
+ return module(*inputs, **kwargs)
7
+ return custom_forward
8
+
9
+
10
+ def gradient_checkpoint_forward(
11
+ model,
12
+ use_gradient_checkpointing,
13
+ use_gradient_checkpointing_offload,
14
+ *args,
15
+ **kwargs,
16
+ ):
17
+ if use_gradient_checkpointing_offload:
18
+ with torch.autograd.graph.save_on_cpu():
19
+ model_output = torch.utils.checkpoint.checkpoint(
20
+ create_custom_forward(model),
21
+ *args,
22
+ **kwargs,
23
+ use_reentrant=False,
24
+ )
25
+ elif use_gradient_checkpointing:
26
+ model_output = torch.utils.checkpoint.checkpoint(
27
+ create_custom_forward(model),
28
+ *args,
29
+ **kwargs,
30
+ use_reentrant=False,
31
+ )
32
+ else:
33
+ model_output = model(*args, **kwargs)
34
+ return model_output
diffsynth/core/loader/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from .file import load_state_dict, hash_state_dict_keys, hash_model_file
2
+ from .model import load_model, load_model_with_disk_offload
3
+ from .config import ModelConfig
diffsynth/core/loader/config.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch, glob, os
2
+ from typing import Optional, Union
3
+ from dataclasses import dataclass
4
+ from typing import Optional
5
+
6
+
7
+ @dataclass
8
+ class ModelConfig:
9
+ path: Union[str, list[str]] = None
10
+ model_id: str = None
11
+ origin_file_pattern: Union[str, list[str]] = None
12
+ download_source: str = None
13
+ local_model_path: str = None
14
+ skip_download: bool = None
15
+ offload_device: Optional[Union[str, torch.device]] = None
16
+ offload_dtype: Optional[torch.dtype] = None
17
+ onload_device: Optional[Union[str, torch.device]] = None
18
+ onload_dtype: Optional[torch.dtype] = None
19
+ preparing_device: Optional[Union[str, torch.device]] = None
20
+ preparing_dtype: Optional[torch.dtype] = None
21
+ computation_device: Optional[Union[str, torch.device]] = None
22
+ computation_dtype: Optional[torch.dtype] = None
23
+ clear_parameters: bool = False
24
+
25
+ def check_input(self):
26
+ if self.path is None and self.model_id is None:
27
+ raise ValueError(f"""No valid model files. Please use `ModelConfig(path="xxx")` or `ModelConfig(model_id="xxx/yyy", origin_file_pattern="zzz")`. `skip_download=True` only supports the first one.""")
28
+
29
+ def parse_original_file_pattern(self):
30
+ if self.origin_file_pattern is None or self.origin_file_pattern == "":
31
+ return "*"
32
+ elif self.origin_file_pattern.endswith("/"):
33
+ return self.origin_file_pattern + "*"
34
+ else:
35
+ return self.origin_file_pattern
36
+
37
+ def parse_download_source(self):
38
+ if self.download_source is None:
39
+ if os.environ.get('DIFFSYNTH_DOWNLOAD_SOURCE') is not None:
40
+ return os.environ.get('DIFFSYNTH_DOWNLOAD_SOURCE')
41
+ else:
42
+ return "modelscope"
43
+ else:
44
+ return self.download_source
45
+
46
+ def parse_skip_download(self):
47
+ if self.skip_download is None:
48
+ if os.environ.get('DIFFSYNTH_SKIP_DOWNLOAD') is not None:
49
+ if os.environ.get('DIFFSYNTH_SKIP_DOWNLOAD').lower() == "true":
50
+ return True
51
+ elif os.environ.get('DIFFSYNTH_SKIP_DOWNLOAD').lower() == "false":
52
+ return False
53
+ else:
54
+ return False
55
+ else:
56
+ return self.skip_download
57
+
58
+ def download(self):
59
+ origin_file_pattern = self.parse_original_file_pattern()
60
+ downloaded_files = glob.glob(origin_file_pattern, root_dir=os.path.join(self.local_model_path, self.model_id))
61
+ download_source = self.parse_download_source()
62
+ if download_source.lower() == "modelscope":
63
+ from modelscope import snapshot_download
64
+
65
+ snapshot_download(
66
+ self.model_id,
67
+ local_dir=os.path.join(self.local_model_path, self.model_id),
68
+ allow_file_pattern=origin_file_pattern,
69
+ ignore_file_pattern=downloaded_files,
70
+ local_files_only=False
71
+ )
72
+ elif download_source.lower() == "huggingface":
73
+ from huggingface_hub import snapshot_download as hf_snapshot_download
74
+
75
+ hf_snapshot_download(
76
+ self.model_id,
77
+ local_dir=os.path.join(self.local_model_path, self.model_id),
78
+ allow_patterns=origin_file_pattern,
79
+ ignore_patterns=downloaded_files,
80
+ local_files_only=False
81
+ )
82
+ else:
83
+ raise ValueError("`download_source` should be `modelscope` or `huggingface`.")
84
+
85
+ def require_downloading(self):
86
+ if self.path is not None:
87
+ return False
88
+ skip_download = self.parse_skip_download()
89
+ return not skip_download
90
+
91
+ def reset_local_model_path(self):
92
+ if os.environ.get('DIFFSYNTH_MODEL_BASE_PATH') is not None:
93
+ self.local_model_path = os.environ.get('DIFFSYNTH_MODEL_BASE_PATH')
94
+ elif self.local_model_path is None:
95
+ self.local_model_path = "./models"
96
+
97
+ def download_if_necessary(self):
98
+ self.check_input()
99
+ self.reset_local_model_path()
100
+ if self.require_downloading():
101
+ self.download()
102
+ if self.origin_file_pattern is None or self.origin_file_pattern == "":
103
+ self.path = os.path.join(self.local_model_path, self.model_id)
104
+ else:
105
+ self.path = glob.glob(os.path.join(self.local_model_path, self.model_id, self.origin_file_pattern))
106
+ if isinstance(self.path, list) and len(self.path) == 1:
107
+ self.path = self.path[0]
108
+
109
+ def vram_config(self):
110
+ return {
111
+ "offload_device": self.offload_device,
112
+ "offload_dtype": self.offload_dtype,
113
+ "onload_device": self.onload_device,
114
+ "onload_dtype": self.onload_dtype,
115
+ "preparing_device": self.preparing_device,
116
+ "preparing_dtype": self.preparing_dtype,
117
+ "computation_device": self.computation_device,
118
+ "computation_dtype": self.computation_dtype,
119
+ }
diffsynth/core/loader/file.py ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from safetensors import safe_open
2
+ import torch, hashlib
3
+
4
+
5
+ def load_state_dict(file_path, torch_dtype=None, device="cpu"):
6
+ if isinstance(file_path, list):
7
+ state_dict = {}
8
+ for file_path_ in file_path:
9
+ state_dict.update(load_state_dict(file_path_, torch_dtype, device))
10
+ return state_dict
11
+ if file_path.endswith(".safetensors"):
12
+ return load_state_dict_from_safetensors(file_path, torch_dtype=torch_dtype, device=device)
13
+ else:
14
+ return load_state_dict_from_bin(file_path, torch_dtype=torch_dtype, device=device)
15
+
16
+
17
+ def load_state_dict_from_safetensors(file_path, torch_dtype=None, device="cpu"):
18
+ state_dict = {}
19
+ with safe_open(file_path, framework="pt", device=str(device)) as f:
20
+ for k in f.keys():
21
+ state_dict[k] = f.get_tensor(k)
22
+ if torch_dtype is not None:
23
+ state_dict[k] = state_dict[k].to(torch_dtype)
24
+ return state_dict
25
+
26
+
27
+ def load_state_dict_from_bin(file_path, torch_dtype=None, device="cpu"):
28
+ state_dict = torch.load(file_path, map_location=device, weights_only=True)
29
+ if len(state_dict) == 1:
30
+ if "state_dict" in state_dict:
31
+ state_dict = state_dict["state_dict"]
32
+ elif "module" in state_dict:
33
+ state_dict = state_dict["module"]
34
+ elif "model_state" in state_dict:
35
+ state_dict = state_dict["model_state"]
36
+ if torch_dtype is not None:
37
+ for i in state_dict:
38
+ if isinstance(state_dict[i], torch.Tensor):
39
+ state_dict[i] = state_dict[i].to(torch_dtype)
40
+ return state_dict
41
+
42
+
43
+ def convert_state_dict_keys_to_single_str(state_dict, with_shape=True):
44
+ keys = []
45
+ for key, value in state_dict.items():
46
+ if isinstance(key, str):
47
+ if isinstance(value, torch.Tensor):
48
+ if with_shape:
49
+ shape = "_".join(map(str, list(value.shape)))
50
+ keys.append(key + ":" + shape)
51
+ keys.append(key)
52
+ elif isinstance(value, dict):
53
+ keys.append(key + "|" + convert_state_dict_keys_to_single_str(value, with_shape=with_shape))
54
+ keys.sort()
55
+ keys_str = ",".join(keys)
56
+ return keys_str
57
+
58
+
59
+ def hash_state_dict_keys(state_dict, with_shape=True):
60
+ keys_str = convert_state_dict_keys_to_single_str(state_dict, with_shape=with_shape)
61
+ keys_str = keys_str.encode(encoding="UTF-8")
62
+ return hashlib.md5(keys_str).hexdigest()
63
+
64
+
65
+ def load_keys_dict(file_path):
66
+ if isinstance(file_path, list):
67
+ state_dict = {}
68
+ for file_path_ in file_path:
69
+ state_dict.update(load_keys_dict(file_path_))
70
+ return state_dict
71
+ if file_path.endswith(".safetensors"):
72
+ return load_keys_dict_from_safetensors(file_path)
73
+ else:
74
+ return load_keys_dict_from_bin(file_path)
75
+
76
+
77
+ def load_keys_dict_from_safetensors(file_path):
78
+ keys_dict = {}
79
+ with safe_open(file_path, framework="pt", device="cpu") as f:
80
+ for k in f.keys():
81
+ keys_dict[k] = f.get_slice(k).get_shape()
82
+ return keys_dict
83
+
84
+
85
+ def convert_state_dict_to_keys_dict(state_dict):
86
+ keys_dict = {}
87
+ for k, v in state_dict.items():
88
+ if isinstance(v, torch.Tensor):
89
+ keys_dict[k] = list(v.shape)
90
+ else:
91
+ keys_dict[k] = convert_state_dict_to_keys_dict(v)
92
+ return keys_dict
93
+
94
+
95
+ def load_keys_dict_from_bin(file_path):
96
+ state_dict = load_state_dict_from_bin(file_path)
97
+ keys_dict = convert_state_dict_to_keys_dict(state_dict)
98
+ return keys_dict
99
+
100
+
101
+ def convert_keys_dict_to_single_str(state_dict, with_shape=True):
102
+ keys = []
103
+ for key, value in state_dict.items():
104
+ if isinstance(key, str):
105
+ if isinstance(value, dict):
106
+ keys.append(key + "|" + convert_keys_dict_to_single_str(value, with_shape=with_shape))
107
+ else:
108
+ if with_shape:
109
+ shape = "_".join(map(str, list(value)))
110
+ keys.append(key + ":" + shape)
111
+ keys.append(key)
112
+ keys.sort()
113
+ keys_str = ",".join(keys)
114
+ return keys_str
115
+
116
+
117
+ def hash_model_file(path, with_shape=True):
118
+ keys_dict = load_keys_dict(path)
119
+ keys_str = convert_keys_dict_to_single_str(keys_dict, with_shape=with_shape)
120
+ keys_str = keys_str.encode(encoding="UTF-8")
121
+ return hashlib.md5(keys_str).hexdigest()
diffsynth/core/loader/model.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ..vram.initialization import skip_model_initialization
2
+ from ..vram.disk_map import DiskMap
3
+ from ..vram.layers import enable_vram_management
4
+ from .file import load_state_dict
5
+ import torch
6
+
7
+
8
+ def load_model(model_class, path, config=None, torch_dtype=torch.bfloat16, device="cpu", state_dict_converter=None, use_disk_map=False, module_map=None, vram_config=None, vram_limit=None):
9
+ config = {} if config is None else config
10
+
11
+
12
+
13
+ with skip_model_initialization():
14
+ model = model_class(**config)
15
+
16
+
17
+ if module_map is not None:
18
+ devices = [vram_config["offload_device"], vram_config["onload_device"], vram_config["preparing_device"], vram_config["computation_device"]]
19
+ device = [d for d in devices if d != "disk"][0]
20
+ dtypes = [vram_config["offload_dtype"], vram_config["onload_dtype"], vram_config["preparing_dtype"], vram_config["computation_dtype"]]
21
+ dtype = [d for d in dtypes if d != "disk"][0]
22
+ if vram_config["offload_device"] != "disk":
23
+ state_dict = DiskMap(path, device, torch_dtype=dtype)
24
+ if state_dict_converter is not None:
25
+ state_dict = state_dict_converter(state_dict)
26
+ else:
27
+ state_dict = {i: state_dict[i] for i in state_dict}
28
+ model.load_state_dict(state_dict, assign=True)
29
+ model = enable_vram_management(model, module_map, vram_config=vram_config, disk_map=None, vram_limit=vram_limit)
30
+ else:
31
+ disk_map = DiskMap(path, device, state_dict_converter=state_dict_converter)
32
+ model = enable_vram_management(model, module_map, vram_config=vram_config, disk_map=disk_map, vram_limit=vram_limit)
33
+ else:
34
+
35
+
36
+
37
+
38
+ if use_disk_map:
39
+ state_dict = DiskMap(path, device, torch_dtype=torch_dtype)
40
+ else:
41
+ state_dict = load_state_dict(path, torch_dtype, device)
42
+
43
+
44
+
45
+ if state_dict_converter is not None:
46
+ state_dict = state_dict_converter(state_dict)
47
+ else:
48
+ state_dict = {i: state_dict[i] for i in state_dict}
49
+ model.load_state_dict(state_dict, assign=True)
50
+
51
+
52
+
53
+ model = model.to(dtype=torch_dtype, device=device)
54
+ if hasattr(model, "eval"):
55
+ model = model.eval()
56
+ return model
57
+
58
+
59
+ def load_model_with_disk_offload(model_class, path, config=None, torch_dtype=torch.bfloat16, device="cpu", state_dict_converter=None, module_map=None):
60
+ if isinstance(path, str):
61
+ path = [path]
62
+ config = {} if config is None else config
63
+ with skip_model_initialization():
64
+ model = model_class(**config)
65
+ if hasattr(model, "eval"):
66
+ model = model.eval()
67
+ disk_map = DiskMap(path, device, state_dict_converter=state_dict_converter)
68
+ vram_config = {
69
+ "offload_dtype": "disk",
70
+ "offload_device": "disk",
71
+ "onload_dtype": "disk",
72
+ "onload_device": "disk",
73
+ "preparing_dtype": torch.float8_e4m3fn,
74
+ "preparing_device": device,
75
+ "computation_dtype": torch_dtype,
76
+ "computation_device": device,
77
+ }
78
+ enable_vram_management(model, module_map, vram_config=vram_config, disk_map=disk_map, vram_limit=80)
79
+ return model
diffsynth/core/vram/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ from .initialization import skip_model_initialization
2
+ from .layers import *
diffsynth/core/vram/disk_map.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from safetensors import safe_open
2
+ import torch, os
3
+
4
+
5
+ class SafetensorsCompatibleTensor:
6
+ def __init__(self, tensor):
7
+ self.tensor = tensor
8
+
9
+ def get_shape(self):
10
+ return list(self.tensor.shape)
11
+
12
+
13
+ class SafetensorsCompatibleBinaryLoader:
14
+ def __init__(self, path, device):
15
+ print("Detected non-safetensors files, which may cause slower loading. It's recommended to convert it to a safetensors file.")
16
+ self.state_dict = torch.load(path, weights_only=True, map_location=device)
17
+
18
+ def keys(self):
19
+ return self.state_dict.keys()
20
+
21
+ def get_tensor(self, name):
22
+ return self.state_dict[name]
23
+
24
+ def get_slice(self, name):
25
+ return SafetensorsCompatibleTensor(self.state_dict[name])
26
+
27
+
28
+ class DiskMap:
29
+
30
+ def __init__(self, path, device, torch_dtype=None, state_dict_converter=None, buffer_size=10**9):
31
+ self.path = path if isinstance(path, list) else [path]
32
+ self.device = device
33
+ self.torch_dtype = torch_dtype
34
+ if os.environ.get('DIFFSYNTH_DISK_MAP_BUFFER_SIZE') is not None:
35
+ self.buffer_size = int(os.environ.get('DIFFSYNTH_DISK_MAP_BUFFER_SIZE'))
36
+ else:
37
+ self.buffer_size = buffer_size
38
+ self.files = []
39
+ self.flush_files()
40
+ self.name_map = {}
41
+ for file_id, file in enumerate(self.files):
42
+ for name in file.keys():
43
+ self.name_map[name] = file_id
44
+ self.rename_dict = self.fetch_rename_dict(state_dict_converter)
45
+
46
+ def flush_files(self):
47
+ if len(self.files) == 0:
48
+ for path in self.path:
49
+ if path.endswith(".safetensors"):
50
+ self.files.append(safe_open(path, framework="pt", device=str(self.device)))
51
+ else:
52
+ self.files.append(SafetensorsCompatibleBinaryLoader(path, device=self.device))
53
+ else:
54
+ for i, path in enumerate(self.path):
55
+ if path.endswith(".safetensors"):
56
+ self.files[i] = safe_open(path, framework="pt", device=str(self.device))
57
+ self.num_params = 0
58
+
59
+ def __getitem__(self, name):
60
+ if self.rename_dict is not None: name = self.rename_dict[name]
61
+ file_id = self.name_map[name]
62
+ param = self.files[file_id].get_tensor(name)
63
+ if self.torch_dtype is not None and isinstance(param, torch.Tensor):
64
+ param = param.to(self.torch_dtype)
65
+ if isinstance(param, torch.Tensor) and param.device == "cpu":
66
+ param = param.clone()
67
+ if isinstance(param, torch.Tensor):
68
+ self.num_params += param.numel()
69
+ if self.num_params > self.buffer_size:
70
+ self.flush_files()
71
+ return param
72
+
73
+ def fetch_rename_dict(self, state_dict_converter):
74
+ if state_dict_converter is None:
75
+ return None
76
+ state_dict = {}
77
+ for file in self.files:
78
+ for name in file.keys():
79
+ state_dict[name] = name
80
+ state_dict = state_dict_converter(state_dict)
81
+ return state_dict
82
+
83
+ def __iter__(self):
84
+ if self.rename_dict is not None:
85
+ return self.rename_dict.__iter__()
86
+ else:
87
+ return self.name_map.__iter__()
88
+
89
+ def __contains__(self, x):
90
+ if self.rename_dict is not None:
91
+ return x in self.rename_dict
92
+ else:
93
+ return x in self.name_map
diffsynth/core/vram/initialization.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from contextlib import contextmanager
3
+
4
+
5
+ @contextmanager
6
+ def skip_model_initialization(device=torch.device("meta")):
7
+
8
+ def register_empty_parameter(module, name, param):
9
+ old_register_parameter(module, name, param)
10
+ if param is not None:
11
+ param_cls = type(module._parameters[name])
12
+ kwargs = module._parameters[name].__dict__
13
+ kwargs["requires_grad"] = param.requires_grad
14
+ module._parameters[name] = param_cls(module._parameters[name].to(device), **kwargs)
15
+
16
+ old_register_parameter = torch.nn.Module.register_parameter
17
+ torch.nn.Module.register_parameter = register_empty_parameter
18
+ try:
19
+ yield
20
+ finally:
21
+ torch.nn.Module.register_parameter = old_register_parameter
diffsynth/core/vram/layers.py ADDED
@@ -0,0 +1,475 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch, copy
2
+ from typing import Union
3
+ from .initialization import skip_model_initialization
4
+ from .disk_map import DiskMap
5
+
6
+
7
+ class AutoTorchModule(torch.nn.Module):
8
+
9
+ def __init__(
10
+ self,
11
+ offload_dtype: torch.dtype = None,
12
+ offload_device: Union[str, torch.device] = None,
13
+ onload_dtype: torch.dtype = None,
14
+ onload_device: Union[str, torch.device] = None,
15
+ preparing_dtype: torch.dtype = None,
16
+ preparing_device: Union[str, torch.device] = None,
17
+ computation_dtype: torch.dtype = None,
18
+ computation_device: Union[str, torch.device] = None,
19
+ vram_limit: float = None,
20
+ ):
21
+ super().__init__()
22
+ self.set_dtype_and_device(
23
+ offload_dtype,
24
+ offload_device,
25
+ onload_dtype,
26
+ onload_device,
27
+ preparing_dtype,
28
+ preparing_device,
29
+ computation_dtype,
30
+ computation_device,
31
+ vram_limit,
32
+ )
33
+ self.state = 0
34
+ self.name = ""
35
+
36
+ def set_dtype_and_device(
37
+ self,
38
+ offload_dtype: torch.dtype = None,
39
+ offload_device: Union[str, torch.device] = None,
40
+ onload_dtype: torch.dtype = None,
41
+ onload_device: Union[str, torch.device] = None,
42
+ preparing_dtype: torch.dtype = None,
43
+ preparing_device: Union[str, torch.device] = None,
44
+ computation_dtype: torch.dtype = None,
45
+ computation_device: Union[str, torch.device] = None,
46
+ vram_limit: float = None,
47
+ ):
48
+ self.offload_dtype = offload_dtype or computation_dtype
49
+ self.offload_device = offload_device or computation_dtype
50
+ self.onload_dtype = onload_dtype or computation_dtype
51
+ self.onload_device = onload_device or computation_dtype
52
+ self.preparing_dtype = preparing_dtype or computation_dtype
53
+ self.preparing_device = preparing_device or computation_dtype
54
+ self.computation_dtype = computation_dtype
55
+ self.computation_device = computation_device
56
+ self.vram_limit = vram_limit
57
+
58
+ def cast_to(self, weight, dtype, device):
59
+ r = torch.empty_like(weight, dtype=dtype, device=device)
60
+ r.copy_(weight)
61
+ return r
62
+
63
+ def check_free_vram(self):
64
+ gpu_mem_state = torch.cuda.mem_get_info(self.computation_device)
65
+ used_memory = (gpu_mem_state[1] - gpu_mem_state[0]) / (1024**3)
66
+ return used_memory < self.vram_limit
67
+
68
+ def offload(self):
69
+ if self.state != 0:
70
+ self.to(dtype=self.offload_dtype, device=self.offload_device)
71
+ self.state = 0
72
+
73
+ def onload(self):
74
+ if self.state != 1:
75
+ self.to(dtype=self.onload_dtype, device=self.onload_device)
76
+ self.state = 1
77
+
78
+ def param_name(self, name):
79
+ if self.name == "":
80
+ return name
81
+ else:
82
+ return self.name + "." + name
83
+
84
+
85
+ class AutoWrappedModule(AutoTorchModule):
86
+
87
+ def __init__(
88
+ self,
89
+ module: torch.nn.Module,
90
+ offload_dtype: torch.dtype = None,
91
+ offload_device: Union[str, torch.device] = None,
92
+ onload_dtype: torch.dtype = None,
93
+ onload_device: Union[str, torch.device] = None,
94
+ preparing_dtype: torch.dtype = None,
95
+ preparing_device: Union[str, torch.device] = None,
96
+ computation_dtype: torch.dtype = None,
97
+ computation_device: Union[str, torch.device] = None,
98
+ vram_limit: float = None,
99
+ name: str = "",
100
+ disk_map: DiskMap = None,
101
+ **kwargs
102
+ ):
103
+ super().__init__(
104
+ offload_dtype,
105
+ offload_device,
106
+ onload_dtype,
107
+ onload_device,
108
+ preparing_dtype,
109
+ preparing_device,
110
+ computation_dtype,
111
+ computation_device,
112
+ vram_limit,
113
+ )
114
+ self.module = module
115
+ if offload_dtype == "disk":
116
+ self.name = name
117
+ self.disk_map = disk_map
118
+ self.required_params = [name for name, _ in self.module.named_parameters()]
119
+ self.disk_offload = True
120
+ else:
121
+ self.disk_offload = False
122
+
123
+ def load_from_disk(self, torch_dtype, device, copy_module=False):
124
+ if copy_module:
125
+ module = copy.deepcopy(self.module)
126
+ else:
127
+ module = self.module
128
+ state_dict = {}
129
+ for name in self.required_params:
130
+ param = self.disk_map[self.param_name(name)]
131
+ param = param.to(dtype=torch_dtype, device=device)
132
+ state_dict[name] = param
133
+ module.load_state_dict(state_dict, assign=True)
134
+ module.to(dtype=torch_dtype, device=device)
135
+ return module
136
+
137
+ def offload_to_disk(self, model: torch.nn.Module):
138
+ for buf in model.buffers():
139
+
140
+
141
+ for children in model.children():
142
+ self.offload_to_disk(children)
143
+ break
144
+ else:
145
+ model.to("meta")
146
+
147
+ def offload(self):
148
+
149
+ if self.state != 0:
150
+ if self.disk_offload:
151
+ self.offload_to_disk(self.module)
152
+ else:
153
+ self.to(dtype=self.offload_dtype, device=self.offload_device)
154
+ self.state = 0
155
+
156
+ def onload(self):
157
+
158
+ if self.state < 1:
159
+ if self.disk_offload and self.onload_device != "disk" and self.offload_device == "disk":
160
+ self.load_from_disk(self.onload_dtype, self.onload_device)
161
+ elif self.onload_device != "disk":
162
+ self.to(dtype=self.onload_dtype, device=self.onload_device)
163
+ self.state = 1
164
+
165
+ def preparing(self):
166
+
167
+ if self.state != 2:
168
+ if self.disk_offload and self.preparing_device != "disk" and self.onload_device == "disk":
169
+ self.load_from_disk(self.preparing_dtype, self.preparing_device)
170
+ elif self.preparing_device != "disk":
171
+ self.to(dtype=self.preparing_dtype, device=self.preparing_device)
172
+ self.state = 2
173
+
174
+ def cast_to(self, module, dtype, device):
175
+ return copy.deepcopy(module).to(dtype=dtype, device=device)
176
+
177
+ def computation(self):
178
+
179
+ if self.state == 2:
180
+ torch_dtype, device = self.preparing_dtype, self.preparing_device
181
+ else:
182
+ torch_dtype, device = self.onload_dtype, self.onload_device
183
+ if torch_dtype == self.computation_dtype and device == self.computation_device:
184
+ module = self.module
185
+ elif self.disk_offload and device == "disk":
186
+ module = self.load_from_disk(self.computation_dtype, self.computation_device, copy_module=True)
187
+ else:
188
+ module = self.cast_to(self.module, dtype=self.computation_dtype, device=self.computation_device)
189
+ return module
190
+
191
+ def forward(self, *args, **kwargs):
192
+ if self.state == 1 and (self.vram_limit is None or self.check_free_vram()):
193
+ self.preparing()
194
+ module = self.computation()
195
+ return module(*args, **kwargs)
196
+
197
+ def __getattr__(self, name):
198
+ if name in self.__dict__ or name == "module":
199
+ return super().__getattr__(name)
200
+ else:
201
+ return getattr(self.module, name)
202
+
203
+
204
+ class AutoWrappedNonRecurseModule(AutoWrappedModule):
205
+
206
+ def __init__(
207
+ self,
208
+ module: torch.nn.Module,
209
+ offload_dtype: torch.dtype = None,
210
+ offload_device: Union[str, torch.device] = None,
211
+ onload_dtype: torch.dtype = None,
212
+ onload_device: Union[str, torch.device] = None,
213
+ preparing_dtype: torch.dtype = None,
214
+ preparing_device: Union[str, torch.device] = None,
215
+ computation_dtype: torch.dtype = None,
216
+ computation_device: Union[str, torch.device] = None,
217
+ vram_limit: float = None,
218
+ name: str = "",
219
+ disk_map: DiskMap = None,
220
+ **kwargs
221
+ ):
222
+ super().__init__(
223
+ module,
224
+ offload_dtype,
225
+ offload_device,
226
+ onload_dtype,
227
+ onload_device,
228
+ preparing_dtype,
229
+ preparing_device,
230
+ computation_dtype,
231
+ computation_device,
232
+ vram_limit,
233
+ name,
234
+ disk_map,
235
+ **kwargs
236
+ )
237
+ if self.disk_offload:
238
+ self.required_params = [name for name, _ in self.module.named_parameters(recurse=False)]
239
+
240
+ def load_from_disk(self, torch_dtype, device, copy_module=False):
241
+ if copy_module:
242
+ module = copy.deepcopy(self.module)
243
+ else:
244
+ module = self.module
245
+ state_dict = {}
246
+ for name in self.required_params:
247
+ param = self.disk_map[self.param_name(name)]
248
+ param = param.to(dtype=torch_dtype, device=device)
249
+ state_dict[name] = param
250
+ module.load_state_dict(state_dict, assign=True, strict=False)
251
+ return module
252
+
253
+ def offload_to_disk(self, model: torch.nn.Module):
254
+ for name in self.required_params:
255
+ getattr(self, name).to("meta")
256
+
257
+ def cast_to(self, module, dtype, device):
258
+
259
+ return module
260
+
261
+ def __getattr__(self, name):
262
+ if name in self.__dict__ or name == "module":
263
+ return super().__getattr__(name)
264
+ else:
265
+ return getattr(self.module, name)
266
+
267
+
268
+ class AutoWrappedLinear(torch.nn.Linear, AutoTorchModule):
269
+ def __init__(
270
+ self,
271
+ module: torch.nn.Linear,
272
+ offload_dtype: torch.dtype = None,
273
+ offload_device: Union[str, torch.device] = None,
274
+ onload_dtype: torch.dtype = None,
275
+ onload_device: Union[str, torch.device] = None,
276
+ preparing_dtype: torch.dtype = None,
277
+ preparing_device: Union[str, torch.device] = None,
278
+ computation_dtype: torch.dtype = None,
279
+ computation_device: Union[str, torch.device] = None,
280
+ vram_limit: float = None,
281
+ name: str = "",
282
+ disk_map: DiskMap = None,
283
+ **kwargs
284
+ ):
285
+ with skip_model_initialization():
286
+ super().__init__(
287
+ in_features=module.in_features,
288
+ out_features=module.out_features,
289
+ bias=module.bias is not None,
290
+ )
291
+ self.set_dtype_and_device(
292
+ offload_dtype,
293
+ offload_device,
294
+ onload_dtype,
295
+ onload_device,
296
+ preparing_dtype,
297
+ preparing_device,
298
+ computation_dtype,
299
+ computation_device,
300
+ vram_limit,
301
+ )
302
+ self.weight = module.weight
303
+ self.bias = module.bias
304
+ self.state = 0
305
+ self.name = name
306
+ self.lora_A_weights = []
307
+ self.lora_B_weights = []
308
+ self.lora_merger = None
309
+ self.enable_fp8 = computation_dtype in [torch.float8_e4m3fn, torch.float8_e4m3fnuz]
310
+
311
+ if offload_dtype == "disk":
312
+ self.disk_map = disk_map
313
+ self.disk_offload = True
314
+ else:
315
+ self.disk_offload = False
316
+
317
+ def fp8_linear(
318
+ self,
319
+ input: torch.Tensor,
320
+ weight: torch.Tensor,
321
+ bias: torch.Tensor = None,
322
+ ) -> torch.Tensor:
323
+ device = input.device
324
+ origin_dtype = input.dtype
325
+ origin_shape = input.shape
326
+ input = input.reshape(-1, origin_shape[-1])
327
+
328
+ x_max = torch.max(torch.abs(input), dim=-1, keepdim=True).values
329
+ fp8_max = 448.0
330
+
331
+
332
+
333
+
334
+ if self.computation_dtype == torch.float8_e4m3fnuz:
335
+ fp8_max = fp8_max / 2.0
336
+ scale_a = torch.clamp(x_max / fp8_max, min=1.0).float().to(device=device)
337
+ scale_b = torch.ones((weight.shape[0], 1)).to(device=device)
338
+ input = input / (scale_a + 1e-8)
339
+ input = input.to(self.computation_dtype)
340
+ weight = weight.to(self.computation_dtype)
341
+ bias = bias.to(torch.bfloat16)
342
+
343
+ result = torch._scaled_mm(
344
+ input,
345
+ weight.T,
346
+ scale_a=scale_a,
347
+ scale_b=scale_b.T,
348
+ bias=bias,
349
+ out_dtype=origin_dtype,
350
+ )
351
+ new_shape = origin_shape[:-1] + result.shape[-1:]
352
+ result = result.reshape(new_shape)
353
+ return result
354
+
355
+ def load_from_disk(self, torch_dtype, device, assign=True):
356
+ weight = self.disk_map[self.name + ".weight"].to(dtype=torch_dtype, device=device)
357
+ bias = None if self.bias is None else self.disk_map[self.name + ".bias"].to(dtype=torch_dtype, device=device)
358
+ if assign:
359
+ state_dict = {"weight": weight}
360
+ if bias is not None: state_dict["bias"] = bias
361
+ self.load_state_dict(state_dict, assign=True)
362
+ return weight, bias
363
+
364
+ def offload(self):
365
+
366
+ if self.state != 0:
367
+ if self.disk_offload:
368
+ self.to("meta")
369
+ else:
370
+ self.to(dtype=self.offload_dtype, device=self.offload_device)
371
+ self.state = 0
372
+
373
+ def onload(self):
374
+
375
+ if self.state < 1:
376
+ if self.disk_offload and self.onload_device != "disk" and self.offload_device == "disk":
377
+ self.load_from_disk(self.onload_dtype, self.onload_device)
378
+ elif self.onload_device != "disk":
379
+ self.to(dtype=self.onload_dtype, device=self.onload_device)
380
+ self.state = 1
381
+
382
+ def preparing(self):
383
+
384
+ if self.state != 2:
385
+ if self.disk_offload and self.preparing_device != "disk" and self.onload_device == "disk":
386
+ self.load_from_disk(self.preparing_dtype, self.preparing_device)
387
+ elif self.preparing_device != "disk":
388
+ self.to(dtype=self.preparing_dtype, device=self.preparing_device)
389
+ self.state = 2
390
+
391
+ def computation(self):
392
+
393
+ if self.state == 2:
394
+ torch_dtype, device = self.preparing_dtype, self.preparing_device
395
+ else:
396
+ torch_dtype, device = self.onload_dtype, self.onload_device
397
+ if torch_dtype == self.computation_dtype and device == self.computation_device:
398
+ weight, bias = self.weight, self.bias
399
+ elif self.disk_offload and device == "disk":
400
+ weight, bias = self.load_from_disk(self.computation_dtype, self.computation_device, assign=False)
401
+ else:
402
+ weight = self.cast_to(self.weight, self.computation_dtype, self.computation_device)
403
+ bias = None if self.bias is None else self.cast_to(self.bias, self.computation_dtype, self.computation_device)
404
+ return weight, bias
405
+
406
+ def linear_forward(self, x, weight, bias):
407
+ if self.enable_fp8:
408
+ out = self.fp8_linear(x, weight, bias)
409
+ else:
410
+ out = torch.nn.functional.linear(x, weight, bias)
411
+ return out
412
+
413
+ def lora_forward(self, x, out):
414
+ if self.lora_merger is None:
415
+ for lora_A, lora_B in zip(self.lora_A_weights, self.lora_B_weights):
416
+ out = out + x @ lora_A.T @ lora_B.T
417
+ else:
418
+ lora_output = []
419
+ for lora_A, lora_B in zip(self.lora_A_weights, self.lora_B_weights):
420
+ lora_output.append(x @ lora_A.T @ lora_B.T)
421
+ lora_output = torch.stack(lora_output)
422
+ out = self.lora_merger(out, lora_output)
423
+ return out
424
+
425
+ def forward(self, x, *args, **kwargs):
426
+ if self.state == 1 and (self.vram_limit is None or self.check_free_vram()):
427
+ self.preparing()
428
+ weight, bias = self.computation()
429
+ out = self.linear_forward(x, weight, bias)
430
+ if len(self.lora_A_weights) > 0:
431
+ out = self.lora_forward(x, out)
432
+ return out
433
+
434
+
435
+ def enable_vram_management_recursively(model: torch.nn.Module, module_map: dict, vram_config: dict, vram_limit=None, name_prefix="", disk_map=None, **kwargs):
436
+ if isinstance(model, AutoWrappedNonRecurseModule):
437
+ model = model.module
438
+ for name, module in model.named_children():
439
+ layer_name = name if name_prefix == "" else name_prefix + "." + name
440
+ for source_module, target_module in module_map.items():
441
+ if isinstance(module, source_module):
442
+ module_ = target_module(module, **vram_config, vram_limit=vram_limit, name=layer_name, disk_map=disk_map, **kwargs)
443
+ if isinstance(module_, AutoWrappedNonRecurseModule):
444
+ enable_vram_management_recursively(module_, module_map, vram_config, vram_limit=vram_limit, name_prefix=layer_name, disk_map=disk_map, **kwargs)
445
+ setattr(model, name, module_)
446
+ break
447
+ else:
448
+ enable_vram_management_recursively(module, module_map, vram_config, vram_limit=vram_limit, name_prefix=layer_name, disk_map=disk_map, **kwargs)
449
+
450
+
451
+ def fill_vram_config(model, vram_config):
452
+ vram_config_ = vram_config.copy()
453
+ vram_config_["onload_dtype"] = vram_config["computation_dtype"]
454
+ vram_config_["onload_device"] = vram_config["computation_device"]
455
+ vram_config_["preparing_dtype"] = vram_config["computation_dtype"]
456
+ vram_config_["preparing_device"] = vram_config["computation_device"]
457
+ for k in vram_config:
458
+ if vram_config[k] != vram_config_[k]:
459
+ print(f"No fine-grained VRAM configuration is provided for {model.__class__.__name__}. [`onload`, `preparing`, `computation`] will be the same state. `vram_config` is set to {vram_config_}")
460
+ break
461
+ return vram_config_
462
+
463
+
464
+ def enable_vram_management(model: torch.nn.Module, module_map: dict, vram_config: dict, vram_limit=None, disk_map=None, **kwargs):
465
+ for source_module, target_module in module_map.items():
466
+
467
+ if isinstance(model, source_module):
468
+ vram_config = fill_vram_config(model, vram_config)
469
+ model = target_module(model, **vram_config, vram_limit=vram_limit, disk_map=disk_map, **kwargs)
470
+ break
471
+ else:
472
+ enable_vram_management_recursively(model, module_map, vram_config, vram_limit=vram_limit, disk_map=disk_map, **kwargs)
473
+
474
+ model.vram_management_enabled = True
475
+ return model
diffsynth/diffusion/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .flow_match import FlowMatchScheduler
diffsynth/diffusion/base_pipeline.py ADDED
@@ -0,0 +1,439 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from PIL import Image
2
+ import torch
3
+ import numpy as np
4
+ from einops import repeat, reduce
5
+ from typing import Union
6
+ from ..core import AutoTorchModule, AutoWrappedLinear, load_state_dict, ModelConfig
7
+ from ..utils.lora import GeneralLoRALoader
8
+ from ..models.model_loader import ModelPool
9
+ from ..utils.controlnet import ControlNetInput
10
+
11
+
12
+ class PipelineUnit:
13
+ def __init__(
14
+ self,
15
+ seperate_cfg: bool = False,
16
+ take_over: bool = False,
17
+ input_params: tuple[str] = None,
18
+ output_params: tuple[str] = None,
19
+ input_params_posi: dict[str, str] = None,
20
+ input_params_nega: dict[str, str] = None,
21
+ onload_model_names: tuple[str] = None
22
+ ):
23
+ self.seperate_cfg = seperate_cfg
24
+ self.take_over = take_over
25
+ self.input_params = input_params
26
+ self.output_params = output_params
27
+ self.input_params_posi = input_params_posi
28
+ self.input_params_nega = input_params_nega
29
+ self.onload_model_names = onload_model_names
30
+
31
+ def fetch_input_params(self):
32
+ params = []
33
+ if self.input_params is not None:
34
+ for param in self.input_params:
35
+ params.append(param)
36
+ if self.input_params_posi is not None:
37
+ for _, param in self.input_params_posi.items():
38
+ params.append(param)
39
+ if self.input_params_nega is not None:
40
+ for _, param in self.input_params_nega.items():
41
+ params.append(param)
42
+ params = sorted(list(set(params)))
43
+ return params
44
+
45
+ def fetch_output_params(self):
46
+ params = []
47
+ if self.output_params is not None:
48
+ for param in self.output_params:
49
+ params.append(param)
50
+ return params
51
+
52
+ def process(self, pipe, **kwargs) -> dict:
53
+ return {}
54
+
55
+ def post_process(self, pipe, **kwargs) -> dict:
56
+ return {}
57
+
58
+
59
+ class BasePipeline(torch.nn.Module):
60
+
61
+ def __init__(
62
+ self,
63
+ device="cuda", torch_dtype=torch.float16,
64
+ height_division_factor=64, width_division_factor=64,
65
+ time_division_factor=None, time_division_remainder=None,
66
+ ):
67
+ super().__init__()
68
+
69
+ self.device = device
70
+ self.torch_dtype = torch_dtype
71
+
72
+ self.height_division_factor = height_division_factor
73
+ self.width_division_factor = width_division_factor
74
+ self.time_division_factor = time_division_factor
75
+ self.time_division_remainder = time_division_remainder
76
+
77
+ self.vram_management_enabled = False
78
+
79
+ self.unit_runner = PipelineUnitRunner()
80
+
81
+ self.lora_loader = GeneralLoRALoader
82
+
83
+
84
+ def to(self, *args, **kwargs):
85
+ device, dtype, non_blocking, convert_to_format = torch._C._nn._parse_to(*args, **kwargs)
86
+ if device is not None:
87
+ self.device = device
88
+ if dtype is not None:
89
+ self.torch_dtype = dtype
90
+ super().to(*args, **kwargs)
91
+ return self
92
+
93
+
94
+ def check_resize_height_width(self, height, width, num_frames=None):
95
+
96
+ if height % self.height_division_factor != 0:
97
+ height = (height + self.height_division_factor - 1) // self.height_division_factor * self.height_division_factor
98
+ print(f"height % {self.height_division_factor} != 0. We round it up to {height}.")
99
+ if width % self.width_division_factor != 0:
100
+ width = (width + self.width_division_factor - 1) // self.width_division_factor * self.width_division_factor
101
+ print(f"width % {self.width_division_factor} != 0. We round it up to {width}.")
102
+ if num_frames is None:
103
+ return height, width
104
+ else:
105
+ if num_frames % self.time_division_factor != self.time_division_remainder:
106
+ num_frames = (num_frames + self.time_division_factor - 1) // self.time_division_factor * self.time_division_factor + self.time_division_remainder
107
+ print(f"num_frames % {self.time_division_factor} != {self.time_division_remainder}. We round it up to {num_frames}.")
108
+ return height, width, num_frames
109
+
110
+
111
+ def preprocess_image(self, image, torch_dtype=None, device=None, pattern="B C H W", min_value=-1, max_value=1):
112
+
113
+ image = torch.Tensor(np.array(image, dtype=np.float32))
114
+ image = image.to(dtype=torch_dtype or self.torch_dtype, device=device or self.device)
115
+ image = image * ((max_value - min_value) / 255) + min_value
116
+ image = repeat(image, f"H W C -> {pattern}", **({"B": 1} if "B" in pattern else {}))
117
+ return image
118
+
119
+
120
+ def preprocess_video(self, video, torch_dtype=None, device=None, pattern="B C T H W", min_value=-1, max_value=1):
121
+
122
+ video = [self.preprocess_image(image, torch_dtype=torch_dtype, device=device, min_value=min_value, max_value=max_value) for image in video]
123
+ video = torch.stack(video, dim=pattern.index("T") // 2)
124
+ return video
125
+
126
+
127
+ def vae_output_to_image(self, vae_output, pattern="B C H W", min_value=-1, max_value=1):
128
+
129
+ if pattern != "H W C":
130
+ vae_output = reduce(vae_output, f"{pattern} -> H W C", reduction="mean")
131
+ image = ((vae_output - min_value) * (255 / (max_value - min_value))).clip(0, 255)
132
+ image = image.to(device="cpu", dtype=torch.uint8)
133
+ image = Image.fromarray(image.numpy())
134
+ return image
135
+
136
+
137
+ def vae_output_to_video(self, vae_output, pattern="B C T H W", min_value=-1, max_value=1):
138
+
139
+ if pattern != "T H W C":
140
+ vae_output = reduce(vae_output, f"{pattern} -> T H W C", reduction="mean")
141
+ video = [self.vae_output_to_image(image, pattern="H W C", min_value=min_value, max_value=max_value) for image in vae_output]
142
+ return video
143
+
144
+
145
+ def load_models_to_device(self, model_names):
146
+ if self.vram_management_enabled:
147
+
148
+ for name, model in self.named_children():
149
+ if name not in model_names:
150
+ if hasattr(model, "vram_management_enabled") and model.vram_management_enabled:
151
+ if hasattr(model, "offload"):
152
+ model.offload()
153
+ else:
154
+ for module in model.modules():
155
+ if hasattr(module, "offload"):
156
+ module.offload()
157
+ torch.cuda.empty_cache()
158
+
159
+ for name, model in self.named_children():
160
+ if name in model_names:
161
+ if hasattr(model, "vram_management_enabled") and model.vram_management_enabled:
162
+ if hasattr(model, "onload"):
163
+ model.onload()
164
+ else:
165
+ for module in model.modules():
166
+ if hasattr(module, "onload"):
167
+ module.onload()
168
+
169
+
170
+ def generate_noise(self, shape, seed=None, rand_device="cpu", rand_torch_dtype=torch.float32, device=None, torch_dtype=None):
171
+
172
+ generator = None if seed is None else torch.Generator(rand_device).manual_seed(seed)
173
+ noise = torch.randn(shape, generator=generator, device=rand_device, dtype=rand_torch_dtype)
174
+ noise = noise.to(dtype=torch_dtype or self.torch_dtype, device=device or self.device)
175
+ return noise
176
+
177
+
178
+ def get_vram(self):
179
+ return torch.cuda.mem_get_info(self.device)[1] / (1024 ** 3)
180
+
181
+ def get_module(self, model, name):
182
+ if "." in name:
183
+ name, suffix = name[:name.index(".")], name[name.index(".") + 1:]
184
+ if name.isdigit():
185
+ return self.get_module(model[int(name)], suffix)
186
+ else:
187
+ return self.get_module(getattr(model, name), suffix)
188
+ else:
189
+ return getattr(model, name)
190
+
191
+ def freeze_except(self, model_names):
192
+ self.eval()
193
+ self.requires_grad_(False)
194
+ for name in model_names:
195
+ module = self.get_module(self, name)
196
+ if module is None:
197
+ print(f"No {name} models in the pipeline. We cannot enable training on the model. If this occurs during the data processing stage, it is normal.")
198
+ continue
199
+ module.train()
200
+ module.requires_grad_(True)
201
+
202
+
203
+ def blend_with_mask(self, base, addition, mask):
204
+ return base * (1 - mask) + addition * mask
205
+
206
+
207
+ def step(self, scheduler, latents, progress_id, noise_pred, input_latents=None, inpaint_mask=None, **kwargs):
208
+ timestep = scheduler.timesteps[progress_id]
209
+ if inpaint_mask is not None:
210
+ noise_pred_expected = scheduler.return_to_timestep(scheduler.timesteps[progress_id], latents, input_latents)
211
+ noise_pred = self.blend_with_mask(noise_pred_expected, noise_pred, inpaint_mask)
212
+ latents_next = scheduler.step(noise_pred, timestep, latents)
213
+ return latents_next
214
+
215
+
216
+ def split_pipeline_units(self, model_names: list[str]):
217
+ return PipelineUnitGraph().split_pipeline_units(self.units, model_names)
218
+
219
+
220
+ def flush_vram_management_device(self, device):
221
+ for module in self.modules():
222
+ if isinstance(module, AutoTorchModule):
223
+ module.offload_device = device
224
+ module.onload_device = device
225
+ module.preparing_device = device
226
+ module.computation_device = device
227
+
228
+
229
+ def load_lora(
230
+ self,
231
+ module: torch.nn.Module,
232
+ lora_config: Union[ModelConfig, str] = None,
233
+ alpha=1,
234
+ hotload=None,
235
+ state_dict=None,
236
+ ):
237
+ if state_dict is None:
238
+ if isinstance(lora_config, str):
239
+ lora = load_state_dict(lora_config, torch_dtype=self.torch_dtype, device=self.device)
240
+ else:
241
+ lora_config.download_if_necessary()
242
+ lora = load_state_dict(lora_config.path, torch_dtype=self.torch_dtype, device=self.device)
243
+ else:
244
+ lora = state_dict
245
+ lora_loader = self.lora_loader(torch_dtype=self.torch_dtype, device=self.device)
246
+ lora = lora_loader.convert_state_dict(lora)
247
+ if hotload is None:
248
+ hotload = hasattr(module, "vram_management_enabled") and getattr(module, "vram_management_enabled")
249
+ if hotload:
250
+ if not (hasattr(module, "vram_management_enabled") and getattr(module, "vram_management_enabled")):
251
+ raise ValueError("VRAM Management is not enabled. LoRA hotloading is not supported.")
252
+ updated_num = 0
253
+ for _, module in module.named_modules():
254
+ if isinstance(module, AutoWrappedLinear):
255
+ name = module.name
256
+ lora_a_name = f'{name}.lora_A.weight'
257
+ lora_b_name = f'{name}.lora_B.weight'
258
+ if lora_a_name in lora and lora_b_name in lora:
259
+ updated_num += 1
260
+ module.lora_A_weights.append(lora[lora_a_name] * alpha)
261
+ module.lora_B_weights.append(lora[lora_b_name])
262
+ print(f"{updated_num} tensors are patched by LoRA. You can use `pipe.clear_lora()` to clear all LoRA layers.")
263
+ else:
264
+ lora_loader.fuse_lora_to_base_model(module, lora, alpha=alpha)
265
+
266
+
267
+ def clear_lora(self):
268
+ cleared_num = 0
269
+ for name, module in self.named_modules():
270
+ if isinstance(module, AutoWrappedLinear):
271
+ if hasattr(module, "lora_A_weights"):
272
+ if len(module.lora_A_weights) > 0:
273
+ cleared_num += 1
274
+ module.lora_A_weights.clear()
275
+ if hasattr(module, "lora_B_weights"):
276
+ module.lora_B_weights.clear()
277
+ print(f"{cleared_num} LoRA layers are cleared.")
278
+
279
+
280
+ def download_and_load_models(self, model_configs: list[ModelConfig] = [], vram_limit: float = None):
281
+ model_pool = ModelPool()
282
+ for model_config in model_configs:
283
+ model_config.download_if_necessary()
284
+ vram_config = model_config.vram_config()
285
+ vram_config["computation_dtype"] = vram_config["computation_dtype"] or self.torch_dtype
286
+ vram_config["computation_device"] = vram_config["computation_device"] or self.device
287
+ model_pool.auto_load_model(
288
+ model_config.path,
289
+ vram_config=vram_config,
290
+ vram_limit=vram_limit,
291
+ clear_parameters=model_config.clear_parameters,
292
+ )
293
+ return model_pool
294
+
295
+
296
+ def check_vram_management_state(self):
297
+ vram_management_enabled = False
298
+ for module in self.children():
299
+ if hasattr(module, "vram_management_enabled") and getattr(module, "vram_management_enabled"):
300
+ vram_management_enabled = True
301
+ return vram_management_enabled
302
+
303
+
304
+ def cfg_guided_model_fn(self, model_fn, cfg_scale, inputs_shared, inputs_posi, inputs_nega, **inputs_others):
305
+ noise_pred_posi = model_fn(**inputs_posi, **inputs_shared, **inputs_others)
306
+ if cfg_scale != 1.0:
307
+ noise_pred_nega = model_fn(**inputs_nega, **inputs_shared, **inputs_others)
308
+ noise_pred = noise_pred_nega + cfg_scale * (noise_pred_posi - noise_pred_nega)
309
+ else:
310
+ noise_pred = noise_pred_posi
311
+ return noise_pred
312
+
313
+
314
+ class PipelineUnitGraph:
315
+ def __init__(self):
316
+ pass
317
+
318
+ def build_edges(self, units: list[PipelineUnit]):
319
+
320
+
321
+ last_compute_unit_id = {}
322
+ edges = []
323
+ for unit_id, unit in enumerate(units):
324
+ for input_param in unit.fetch_input_params():
325
+ if input_param in last_compute_unit_id:
326
+ edges.append((last_compute_unit_id[input_param], unit_id))
327
+ for output_param in unit.fetch_output_params():
328
+ last_compute_unit_id[output_param] = unit_id
329
+ return edges
330
+
331
+ def build_chains(self, units: list[PipelineUnit]):
332
+
333
+
334
+ params = sum([unit.fetch_input_params() + unit.fetch_output_params() for unit in units], [])
335
+ params = sorted(list(set(params)))
336
+ chains = {param: [] for param in params}
337
+ for unit_id, unit in enumerate(units):
338
+ for param in unit.fetch_output_params():
339
+ chains[param].append(unit_id)
340
+ return chains
341
+
342
+ def search_direct_unit_ids(self, units: list[PipelineUnit], model_names: list[str]):
343
+
344
+ related_unit_ids = []
345
+ for unit_id, unit in enumerate(units):
346
+ for model_name in model_names:
347
+ if unit.onload_model_names is not None and model_name in unit.onload_model_names:
348
+ related_unit_ids.append(unit_id)
349
+ break
350
+ return related_unit_ids
351
+
352
+ def search_related_unit_ids(self, edges, start_unit_ids, direction="target"):
353
+
354
+ related_unit_ids = [unit_id for unit_id in start_unit_ids]
355
+ while True:
356
+ neighbors = []
357
+ for source, target in edges:
358
+ if direction == "target" and source in related_unit_ids and target not in related_unit_ids:
359
+ neighbors.append(target)
360
+ elif direction == "source" and source not in related_unit_ids and target in related_unit_ids:
361
+ neighbors.append(source)
362
+ neighbors = sorted(list(set(neighbors)))
363
+ if len(neighbors) == 0:
364
+ break
365
+ else:
366
+ related_unit_ids.extend(neighbors)
367
+ related_unit_ids = sorted(list(set(related_unit_ids)))
368
+ return related_unit_ids
369
+
370
+ def search_updating_unit_ids(self, units: list[PipelineUnit], chains, related_unit_ids):
371
+
372
+
373
+ first_compute_unit_id = {}
374
+ for unit_id in related_unit_ids:
375
+ for param in units[unit_id].fetch_input_params():
376
+ if param not in first_compute_unit_id:
377
+ first_compute_unit_id[param] = unit_id
378
+ updating_unit_ids = []
379
+ for param in first_compute_unit_id:
380
+ unit_id = first_compute_unit_id[param]
381
+ chain = chains[param]
382
+ if unit_id in chain and chain.index(unit_id) != len(chain) - 1:
383
+ for unit_id_ in chain[chain.index(unit_id) + 1:]:
384
+ if unit_id_ not in related_unit_ids:
385
+ updating_unit_ids.append(unit_id_)
386
+ related_unit_ids.extend(updating_unit_ids)
387
+ related_unit_ids = sorted(list(set(related_unit_ids)))
388
+ return related_unit_ids
389
+
390
+ def split_pipeline_units(self, units: list[PipelineUnit], model_names: list[str]):
391
+
392
+
393
+ related_unit_ids = self.search_direct_unit_ids(units, model_names)
394
+ edges = self.build_edges(units)
395
+ chains = self.build_chains(units)
396
+ while True:
397
+ num_related_unit_ids = len(related_unit_ids)
398
+ related_unit_ids = self.search_related_unit_ids(edges, related_unit_ids, "target")
399
+ related_unit_ids = self.search_updating_unit_ids(units, chains, related_unit_ids)
400
+ if len(related_unit_ids) == num_related_unit_ids:
401
+ break
402
+ else:
403
+ num_related_unit_ids = len(related_unit_ids)
404
+ related_units = [units[i] for i in related_unit_ids]
405
+ unrelated_units = [units[i] for i in range(len(units)) if i not in related_unit_ids]
406
+ return related_units, unrelated_units
407
+
408
+
409
+ class PipelineUnitRunner:
410
+ def __init__(self):
411
+ pass
412
+
413
+ def __call__(self, unit: PipelineUnit, pipe: BasePipeline, inputs_shared: dict, inputs_posi: dict, inputs_nega: dict) -> tuple[dict, dict]:
414
+ if unit.take_over:
415
+
416
+ inputs_shared, inputs_posi, inputs_nega = unit.process(pipe, inputs_shared=inputs_shared, inputs_posi=inputs_posi, inputs_nega=inputs_nega)
417
+ elif unit.seperate_cfg:
418
+
419
+ processor_inputs = {name: inputs_posi.get(name_) for name, name_ in unit.input_params_posi.items()}
420
+ if unit.input_params is not None:
421
+ for name in unit.input_params:
422
+ processor_inputs[name] = inputs_shared.get(name)
423
+ processor_outputs = unit.process(pipe, **processor_inputs)
424
+ inputs_posi.update(processor_outputs)
425
+
426
+ if inputs_shared["cfg_scale"] != 1:
427
+ processor_inputs = {name: inputs_nega.get(name_) for name, name_ in unit.input_params_nega.items()}
428
+ if unit.input_params is not None:
429
+ for name in unit.input_params:
430
+ processor_inputs[name] = inputs_shared.get(name)
431
+ processor_outputs = unit.process(pipe, **processor_inputs)
432
+ inputs_nega.update(processor_outputs)
433
+ else:
434
+ inputs_nega.update(processor_outputs)
435
+ else:
436
+ processor_inputs = {name: inputs_shared.get(name) for name in unit.input_params}
437
+ processor_outputs = unit.process(pipe, **processor_inputs)
438
+ inputs_shared.update(processor_outputs)
439
+ return inputs_shared, inputs_posi, inputs_nega
diffsynth/diffusion/flow_match.py ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch, math
2
+ from typing_extensions import Literal
3
+
4
+
5
+ class FlowMatchScheduler():
6
+
7
+ def __init__(self, template: Literal["FLUX.1", "Wan", "Qwen-Image", "FLUX.2", "Z-Image"] = "FLUX.1"):
8
+ self.set_timesteps_fn = {
9
+ "FLUX.1": FlowMatchScheduler.set_timesteps_flux,
10
+ "Wan": FlowMatchScheduler.set_timesteps_wan,
11
+ "Qwen-Image": FlowMatchScheduler.set_timesteps_qwen_image,
12
+ "FLUX.2": FlowMatchScheduler.set_timesteps_flux2,
13
+ "Z-Image": FlowMatchScheduler.set_timesteps_z_image,
14
+ }.get(template, FlowMatchScheduler.set_timesteps_flux)
15
+ self.num_train_timesteps = 1000
16
+
17
+ @staticmethod
18
+ def set_timesteps_flux(num_inference_steps=100, denoising_strength=1.0, shift=None):
19
+ sigma_min = 0.003/1.002
20
+ sigma_max = 1.0
21
+ shift = 3 if shift is None else shift
22
+ num_train_timesteps = 1000
23
+ sigma_start = sigma_min + (sigma_max - sigma_min) * denoising_strength
24
+ sigmas = torch.linspace(sigma_start, sigma_min, num_inference_steps)
25
+ sigmas = shift * sigmas / (1 + (shift - 1) * sigmas)
26
+ timesteps = sigmas * num_train_timesteps
27
+ return sigmas, timesteps
28
+
29
+ @staticmethod
30
+ def set_timesteps_wan(num_inference_steps=100, denoising_strength=1.0, shift=None):
31
+ sigma_min = 0.0
32
+ sigma_max = 1.0
33
+ shift = 5 if shift is None else shift
34
+ num_train_timesteps = 1000
35
+ sigma_start = sigma_min + (sigma_max - sigma_min) * denoising_strength
36
+ sigmas = torch.linspace(sigma_start, sigma_min, num_inference_steps + 1)[:-1]
37
+ sigmas = shift * sigmas / (1 + (shift - 1) * sigmas)
38
+ timesteps = sigmas * num_train_timesteps
39
+ return sigmas, timesteps
40
+
41
+ @staticmethod
42
+ def _calculate_shift_qwen_image(image_seq_len, base_seq_len=256, max_seq_len=8192, base_shift=0.5, max_shift=0.9):
43
+ m = (max_shift - base_shift) / (max_seq_len - base_seq_len)
44
+ b = base_shift - m * base_seq_len
45
+ mu = image_seq_len * m + b
46
+ return mu
47
+
48
+ @staticmethod
49
+ def set_timesteps_qwen_image(num_inference_steps=100, denoising_strength=1.0, exponential_shift_mu=None, dynamic_shift_len=None):
50
+ sigma_min = 0.0
51
+ sigma_max = 1.0
52
+ num_train_timesteps = 1000
53
+ shift_terminal = 0.02
54
+
55
+ sigma_start = sigma_min + (sigma_max - sigma_min) * denoising_strength
56
+ sigmas = torch.linspace(sigma_start, sigma_min, num_inference_steps + 1)[:-1]
57
+
58
+ if exponential_shift_mu is not None:
59
+ mu = exponential_shift_mu
60
+ elif dynamic_shift_len is not None:
61
+ mu = FlowMatchScheduler._calculate_shift_qwen_image(dynamic_shift_len)
62
+ else:
63
+ mu = 0.8
64
+ sigmas = math.exp(mu) / (math.exp(mu) + (1 / sigmas - 1))
65
+
66
+ one_minus_z = 1 - sigmas
67
+ scale_factor = one_minus_z[-1] / (1 - shift_terminal)
68
+ sigmas = 1 - (one_minus_z / scale_factor)
69
+
70
+ timesteps = sigmas * num_train_timesteps
71
+ return sigmas, timesteps
72
+
73
+ @staticmethod
74
+ def compute_empirical_mu(image_seq_len, num_steps):
75
+ a1, b1 = 8.73809524e-05, 1.89833333
76
+ a2, b2 = 0.00016927, 0.45666666
77
+
78
+ if image_seq_len > 4300:
79
+ mu = a2 * image_seq_len + b2
80
+ return float(mu)
81
+
82
+ m_200 = a2 * image_seq_len + b2
83
+ m_10 = a1 * image_seq_len + b1
84
+
85
+ a = (m_200 - m_10) / 190.0
86
+ b = m_200 - 200.0 * a
87
+ mu = a * num_steps + b
88
+
89
+ return float(mu)
90
+
91
+ @staticmethod
92
+ def set_timesteps_flux2(num_inference_steps=100, denoising_strength=1.0, dynamic_shift_len=1024//16*1024//16):
93
+ sigma_min = 1 / num_inference_steps
94
+ sigma_max = 1.0
95
+ num_train_timesteps = 1000
96
+ sigma_start = sigma_min + (sigma_max - sigma_min) * denoising_strength
97
+ sigmas = torch.linspace(sigma_start, sigma_min, num_inference_steps)
98
+ mu = FlowMatchScheduler.compute_empirical_mu(dynamic_shift_len, num_inference_steps)
99
+ sigmas = math.exp(mu) / (math.exp(mu) + (1 / sigmas - 1))
100
+ timesteps = sigmas * num_train_timesteps
101
+ return sigmas, timesteps
102
+
103
+ @staticmethod
104
+ def set_timesteps_z_image(num_inference_steps=100, denoising_strength=1.0, shift=None, target_timesteps=None):
105
+ sigma_min = 0.0
106
+ sigma_max = 1.0
107
+ shift = 3 if shift is None else shift
108
+ num_train_timesteps = 1000
109
+ sigma_start = sigma_min + (sigma_max - sigma_min) * denoising_strength
110
+ sigmas = torch.linspace(sigma_start, sigma_min, num_inference_steps + 1)[:-1]
111
+ sigmas = shift * sigmas / (1 + (shift - 1) * sigmas)
112
+ timesteps = sigmas * num_train_timesteps
113
+ if target_timesteps is not None:
114
+ target_timesteps = target_timesteps.to(dtype=timesteps.dtype, device=timesteps.device)
115
+ for timestep in target_timesteps:
116
+ timestep_id = torch.argmin((timesteps - timestep).abs())
117
+ timesteps[timestep_id] = timestep
118
+ return sigmas, timesteps
119
+
120
+ def set_training_weight(self):
121
+ steps = 1000
122
+ x = self.timesteps
123
+ y = torch.exp(-2 * ((x - steps / 2) / steps) ** 2)
124
+ y_shifted = y - y.min()
125
+ bsmntw_weighing = y_shifted * (steps / y_shifted.sum())
126
+ if len(self.timesteps) != 1000:
127
+
128
+ bsmntw_weighing = bsmntw_weighing * (len(self.timesteps) / steps)
129
+ bsmntw_weighing = bsmntw_weighing + bsmntw_weighing[1]
130
+ self.linear_timesteps_weights = bsmntw_weighing
131
+
132
+ def set_timesteps(self, num_inference_steps=100, denoising_strength=1.0, training=False, **kwargs):
133
+ self.sigmas, self.timesteps = self.set_timesteps_fn(
134
+ num_inference_steps=num_inference_steps,
135
+ denoising_strength=denoising_strength,
136
+ **kwargs,
137
+ )
138
+ if training:
139
+ self.set_training_weight()
140
+ self.training = True
141
+ else:
142
+ self.training = False
143
+
144
+ def step(self, model_output, timestep, sample, to_final=False, **kwargs):
145
+ if isinstance(timestep, torch.Tensor):
146
+ timestep = timestep.cpu()
147
+ timestep_id = torch.argmin((self.timesteps - timestep).abs())
148
+ sigma = self.sigmas[timestep_id]
149
+ if to_final or timestep_id + 1 >= len(self.timesteps):
150
+ sigma_ = 0
151
+ else:
152
+ sigma_ = self.sigmas[timestep_id + 1]
153
+ prev_sample = sample + model_output * (sigma_ - sigma)
154
+ return prev_sample
155
+
156
+ def return_to_timestep(self, timestep, sample, sample_stablized):
157
+ if isinstance(timestep, torch.Tensor):
158
+ timestep = timestep.cpu()
159
+ timestep_id = torch.argmin((self.timesteps - timestep).abs())
160
+ sigma = self.sigmas[timestep_id]
161
+ model_output = (sample - sample_stablized) / sigma
162
+ return model_output
163
+
164
+ def add_noise(self, original_samples, noise, timestep):
165
+ if isinstance(timestep, torch.Tensor):
166
+ timestep = timestep.cpu()
167
+ timestep_id = torch.argmin((self.timesteps - timestep).abs())
168
+ sigma = self.sigmas[timestep_id]
169
+ sample = (1 - sigma) * original_samples + sigma * noise
170
+ return sample
171
+
172
+ def training_target(self, sample, noise, timestep):
173
+ target = noise - sample
174
+ return target
175
+
176
+ def training_weight(self, timestep):
177
+ timestep_id = torch.argmin((self.timesteps - timestep.to(self.timesteps.device)).abs())
178
+ weights = self.linear_timesteps_weights[timestep_id]
179
+ return weights
diffsynth/models/__init__.py ADDED
File without changes
diffsynth/models/general_modules.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch, math
2
+
3
+
4
+ def get_timestep_embedding(
5
+ timesteps: torch.Tensor,
6
+ embedding_dim: int,
7
+ flip_sin_to_cos: bool = False,
8
+ downscale_freq_shift: float = 1,
9
+ scale: float = 1,
10
+ max_period: int = 10000,
11
+ computation_device = None,
12
+ align_dtype_to_timestep = False,
13
+ ):
14
+ assert len(timesteps.shape) == 1, "Timesteps should be a 1d-array"
15
+
16
+ half_dim = embedding_dim // 2
17
+ exponent = -math.log(max_period) * torch.arange(
18
+ start=0, end=half_dim, dtype=torch.float32, device=timesteps.device if computation_device is None else computation_device
19
+ )
20
+ exponent = exponent / (half_dim - downscale_freq_shift)
21
+
22
+ emb = torch.exp(exponent).to(timesteps.device)
23
+ if align_dtype_to_timestep:
24
+ emb = emb.to(timesteps.dtype)
25
+ emb = timesteps[:, None].float() * emb[None, :]
26
+
27
+
28
+ emb = scale * emb
29
+
30
+
31
+ emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=-1)
32
+
33
+
34
+ if flip_sin_to_cos:
35
+ emb = torch.cat([emb[:, half_dim:], emb[:, :half_dim]], dim=-1)
36
+
37
+
38
+ if embedding_dim % 2 == 1:
39
+ emb = torch.nn.functional.pad(emb, (0, 1, 0, 0))
40
+ return emb
41
+
42
+
43
+ class TemporalTimesteps(torch.nn.Module):
44
+ def __init__(self, num_channels: int, flip_sin_to_cos: bool, downscale_freq_shift: float, computation_device = None, scale=1, align_dtype_to_timestep=False):
45
+ super().__init__()
46
+ self.num_channels = num_channels
47
+ self.flip_sin_to_cos = flip_sin_to_cos
48
+ self.downscale_freq_shift = downscale_freq_shift
49
+ self.computation_device = computation_device
50
+ self.scale = scale
51
+ self.align_dtype_to_timestep = align_dtype_to_timestep
52
+
53
+ def forward(self, timesteps):
54
+ t_emb = get_timestep_embedding(
55
+ timesteps,
56
+ self.num_channels,
57
+ flip_sin_to_cos=self.flip_sin_to_cos,
58
+ downscale_freq_shift=self.downscale_freq_shift,
59
+ computation_device=self.computation_device,
60
+ scale=self.scale,
61
+ align_dtype_to_timestep=self.align_dtype_to_timestep,
62
+ )
63
+ return t_emb
64
+
65
+
66
+ class DiffusersCompatibleTimestepProj(torch.nn.Module):
67
+ def __init__(self, dim_in, dim_out):
68
+ super().__init__()
69
+ self.linear_1 = torch.nn.Linear(dim_in, dim_out)
70
+ self.act = torch.nn.SiLU()
71
+ self.linear_2 = torch.nn.Linear(dim_out, dim_out)
72
+
73
+ def forward(self, x):
74
+ x = self.linear_1(x)
75
+ x = self.act(x)
76
+ x = self.linear_2(x)
77
+ return x
78
+
79
+
80
+ class TimestepEmbeddings(torch.nn.Module):
81
+ def __init__(self, dim_in, dim_out, computation_device=None, diffusers_compatible_format=False, scale=1, align_dtype_to_timestep=False):
82
+ super().__init__()
83
+ self.time_proj = TemporalTimesteps(num_channels=dim_in, flip_sin_to_cos=True, downscale_freq_shift=0, computation_device=computation_device, scale=scale, align_dtype_to_timestep=align_dtype_to_timestep)
84
+ if diffusers_compatible_format:
85
+ self.timestep_embedder = DiffusersCompatibleTimestepProj(dim_in, dim_out)
86
+ else:
87
+ self.timestep_embedder = torch.nn.Sequential(
88
+ torch.nn.Linear(dim_in, dim_out), torch.nn.SiLU(), torch.nn.Linear(dim_out, dim_out)
89
+ )
90
+
91
+ def forward(self, timestep, dtype):
92
+ time_emb = self.time_proj(timestep).to(dtype)
93
+ time_emb = self.timestep_embedder(time_emb)
94
+ return time_emb
95
+
96
+
97
+ class RMSNorm(torch.nn.Module):
98
+ def __init__(self, dim, eps, elementwise_affine=True):
99
+ super().__init__()
100
+ self.eps = eps
101
+ if elementwise_affine:
102
+ self.weight = torch.nn.Parameter(torch.ones((dim,)))
103
+ else:
104
+ self.weight = None
105
+
106
+ def forward(self, hidden_states):
107
+ input_dtype = hidden_states.dtype
108
+ variance = hidden_states.to(torch.float32).square().mean(-1, keepdim=True)
109
+ hidden_states = hidden_states * torch.rsqrt(variance + self.eps)
110
+ hidden_states = hidden_states.to(input_dtype)
111
+ if self.weight is not None:
112
+ hidden_states = hidden_states * self.weight
113
+ return hidden_states
114
+
115
+
116
+ class AdaLayerNorm(torch.nn.Module):
117
+ def __init__(self, dim, single=False, dual=False):
118
+ super().__init__()
119
+ self.single = single
120
+ self.dual = dual
121
+ self.linear = torch.nn.Linear(dim, dim * [[6, 2][single], 9][dual])
122
+ self.norm = torch.nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6)
123
+
124
+ def forward(self, x, emb):
125
+ emb = self.linear(torch.nn.functional.silu(emb))
126
+ if self.single:
127
+ scale, shift = emb.unsqueeze(1).chunk(2, dim=2)
128
+ x = self.norm(x) * (1 + scale) + shift
129
+ return x
130
+ elif self.dual:
131
+ shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp, shift_msa2, scale_msa2, gate_msa2 = emb.unsqueeze(1).chunk(9, dim=2)
132
+ norm_x = self.norm(x)
133
+ x = norm_x * (1 + scale_msa) + shift_msa
134
+ norm_x2 = norm_x * (1 + scale_msa2) + shift_msa2
135
+ return x, gate_msa, shift_mlp, scale_mlp, gate_mlp, norm_x2, gate_msa2
136
+ else:
137
+ shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = emb.unsqueeze(1).chunk(6, dim=2)
138
+ x = self.norm(x) * (1 + scale_msa) + shift_msa
139
+ return x, gate_msa, shift_mlp, scale_mlp, gate_mlp
diffsynth/models/model_loader.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ..core.loader import load_model, hash_model_file
2
+ from ..core.vram import AutoWrappedModule
3
+ from ..configs import MODEL_CONFIGS, VRAM_MANAGEMENT_MODULE_MAPS
4
+ import importlib, json, torch
5
+
6
+
7
+ class ModelPool:
8
+ def __init__(self):
9
+ self.model = []
10
+ self.model_name = []
11
+ self.model_path = []
12
+
13
+ def import_model_class(self, model_class):
14
+ split = model_class.rfind(".")
15
+ model_resource, model_class = model_class[:split], model_class[split+1:]
16
+ model_class = importlib.import_module(model_resource).__getattribute__(model_class)
17
+ return model_class
18
+
19
+ def need_to_enable_vram_management(self, vram_config):
20
+ return vram_config["offload_dtype"] is not None and vram_config["offload_device"] is not None
21
+
22
+ def fetch_module_map(self, model_class, vram_config):
23
+ if self.need_to_enable_vram_management(vram_config):
24
+ if model_class in VRAM_MANAGEMENT_MODULE_MAPS:
25
+ module_map = {self.import_model_class(source): self.import_model_class(target) for source, target in VRAM_MANAGEMENT_MODULE_MAPS[model_class].items()}
26
+ else:
27
+ module_map = {self.import_model_class(model_class): AutoWrappedModule}
28
+ else:
29
+ module_map = None
30
+ return module_map
31
+
32
+ def load_model_file(self, config, path, vram_config, vram_limit=None):
33
+ model_class = self.import_model_class(config["model_class"])
34
+ model_config = config.get("extra_kwargs", {})
35
+ if "state_dict_converter" in config:
36
+ state_dict_converter = self.import_model_class(config["state_dict_converter"])
37
+ else:
38
+ state_dict_converter = None
39
+ module_map = self.fetch_module_map(config["model_class"], vram_config)
40
+ model = load_model(
41
+ model_class, path, model_config,
42
+ vram_config["computation_dtype"], vram_config["computation_device"],
43
+ state_dict_converter,
44
+ use_disk_map=True,
45
+ vram_config=vram_config, module_map=module_map, vram_limit=vram_limit,
46
+ )
47
+ return model
48
+
49
+ def default_vram_config(self):
50
+ vram_config = {
51
+ "offload_dtype": None,
52
+ "offload_device": None,
53
+ "onload_dtype": torch.bfloat16,
54
+ "onload_device": "cpu",
55
+ "preparing_dtype": torch.bfloat16,
56
+ "preparing_device": "cpu",
57
+ "computation_dtype": torch.bfloat16,
58
+ "computation_device": "cpu",
59
+ }
60
+ return vram_config
61
+
62
+ def auto_load_model(self, path, vram_config=None, vram_limit=None, clear_parameters=False):
63
+ print(f"Loading models from: {json.dumps(path, indent=4)}")
64
+ if vram_config is None:
65
+ vram_config = self.default_vram_config()
66
+ model_hash = hash_model_file(path)
67
+ loaded = False
68
+ for config in MODEL_CONFIGS:
69
+ if config["model_hash"] == model_hash:
70
+ model = self.load_model_file(config, path, vram_config, vram_limit=vram_limit)
71
+ if clear_parameters: self.clear_parameters(model)
72
+ self.model.append(model)
73
+ model_name = config["model_name"]
74
+ self.model_name.append(model_name)
75
+ self.model_path.append(path)
76
+ model_info = {"model_name": model_name, "model_class": config["model_class"], "extra_kwargs": config.get("extra_kwargs")}
77
+ print(f"Loaded model: {json.dumps(model_info, indent=4)}")
78
+ loaded = True
79
+ if not loaded:
80
+ raise ValueError(f"Cannot detect the model type. File: {path}. Model hash: {model_hash}")
81
+
82
+ def fetch_model(self, model_name, index=None):
83
+ fetched_models = []
84
+ fetched_model_paths = []
85
+ for model, model_path, model_name_ in zip(self.model, self.model_path, self.model_name):
86
+ if model_name == model_name_:
87
+ fetched_models.append(model)
88
+ fetched_model_paths.append(model_path)
89
+ if len(fetched_models) == 0:
90
+ print(f"No {model_name} models available. This is not an error.")
91
+ model = None
92
+ elif len(fetched_models) == 1:
93
+ print(f"Using {model_name} from {json.dumps(fetched_model_paths[0], indent=4)}.")
94
+ model = fetched_models[0]
95
+ else:
96
+ if index is None:
97
+ model = fetched_models[0]
98
+ print(f"More than one {model_name} models are loaded: {fetched_model_paths}. Using {model_name} from {json.dumps(fetched_model_paths[0], indent=4)}.")
99
+ elif isinstance(index, int):
100
+ model = fetched_models[:index]
101
+ print(f"More than one {model_name} models are loaded: {fetched_model_paths}. Using {model_name} from {json.dumps(fetched_model_paths[:index], indent=4)}.")
102
+ else:
103
+ model = fetched_models
104
+ print(f"More than one {model_name} models are loaded: {fetched_model_paths}. Using {model_name} from {json.dumps(fetched_model_paths, indent=4)}.")
105
+ return model
106
+
107
+ def clear_parameters(self, model: torch.nn.Module):
108
+ for name, module in model.named_children():
109
+ self.clear_parameters(module)
110
+ for name, param in model.named_parameters(recurse=False):
111
+ setattr(model, name, None)
diffsynth/models/qwen_image_connector.py ADDED
@@ -0,0 +1,260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ from typing import Optional, Tuple
4
+
5
+ from transformers.models.qwen2.configuration_qwen2 import Qwen2Config
6
+ from transformers.models.qwen2.modeling_qwen2 import (
7
+ Qwen2PreTrainedModel,
8
+ Qwen2Attention,
9
+ Qwen2MLP,
10
+ Qwen2RMSNorm,
11
+ Qwen2RotaryEmbedding,
12
+ apply_rotary_pos_emb,
13
+ )
14
+ from transformers.integrations.sdpa_attention import sdpa_attention_forward
15
+ from torch.nn import functional as F
16
+
17
+
18
+ class MultiHeadRMSNorm(nn.Module):
19
+ def __init__(self, dim, heads=1):
20
+ super().__init__()
21
+ self.scale = dim**0.5
22
+ self.gamma = nn.Parameter(torch.ones(heads, 1, dim))
23
+
24
+ def forward(self, x):
25
+ return F.normalize(x, dim=-1) * self.gamma * self.scale
26
+
27
+
28
+ class Qwen2BidirectionalSdpaAttention(Qwen2Attention):
29
+ """
30
+ An SDPA-based attention that does NOT apply causal masking.
31
+ Inherits from Qwen2Attention, but sets self.is_causal = False.
32
+ """
33
+
34
+ def __init__(self, config: Qwen2Config, layer_idx: Optional[int] = None):
35
+ super().__init__(config, layer_idx)
36
+ self.is_causal = False
37
+ self.qk_norm = config.qk_norm
38
+ if self.qk_norm:
39
+ self.q_norm = MultiHeadRMSNorm(
40
+ config.hidden_size // config.num_attention_heads,
41
+ config.num_attention_heads,
42
+ )
43
+ self.k_norm = MultiHeadRMSNorm(
44
+ config.hidden_size // config.num_attention_heads,
45
+ config.num_key_value_heads,
46
+ )
47
+
48
+ def forward(
49
+ self,
50
+ hidden_states: torch.Tensor,
51
+ position_embeddings: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
52
+ ):
53
+ input_shape = hidden_states.shape[:-1]
54
+ hidden_shape = (*input_shape, -1, self.head_dim)
55
+
56
+ query_states = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
57
+ key_states = self.k_proj(hidden_states).view(hidden_shape).transpose(1, 2)
58
+ value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
59
+
60
+ if position_embeddings is not None:
61
+ cos, sin = position_embeddings
62
+ query_states, key_states = apply_rotary_pos_emb(
63
+ query_states, key_states, cos, sin
64
+ )
65
+
66
+ if self.qk_norm:
67
+ query_states = self.q_norm(query_states)
68
+ key_states = self.k_norm(key_states)
69
+
70
+ attn_output, attn_weights = sdpa_attention_forward(
71
+ self,
72
+ query_states,
73
+ key_states,
74
+ value_states,
75
+ attention_mask=None,
76
+ dropout=0.0 if not self.training else self.attention_dropout,
77
+ scaling=self.scaling,
78
+ is_causal=False,
79
+ )
80
+
81
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
82
+ attn_output = self.o_proj(attn_output)
83
+ return attn_output
84
+
85
+
86
+ class Qwen2EncoderLayer(nn.Module):
87
+ def __init__(self, config: Qwen2Config, layer_idx: int):
88
+ super().__init__()
89
+ self.hidden_size = config.hidden_size
90
+ self.self_attn = Qwen2BidirectionalSdpaAttention(config, layer_idx)
91
+ self.mlp = Qwen2MLP(config)
92
+
93
+ self.input_layernorm = Qwen2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
94
+ self.post_attention_layernorm = Qwen2RMSNorm(
95
+ config.hidden_size, eps=config.rms_norm_eps
96
+ )
97
+
98
+ def forward(
99
+ self,
100
+ hidden_states: torch.Tensor,
101
+ position_embeddings: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
102
+ ):
103
+
104
+ residual = hidden_states
105
+ hidden_states = self.input_layernorm(hidden_states)
106
+
107
+ hidden_states = self.self_attn(
108
+ hidden_states=hidden_states,
109
+ position_embeddings=position_embeddings,
110
+ )
111
+ hidden_states = residual + hidden_states
112
+
113
+
114
+ residual = hidden_states
115
+ hidden_states = self.post_attention_layernorm(hidden_states)
116
+ hidden_states = self.mlp(hidden_states)
117
+ hidden_states = residual + hidden_states
118
+
119
+ return hidden_states
120
+
121
+
122
+ class Qwen2Encoder(Qwen2PreTrainedModel):
123
+ supports_gradient_checkpointing = True
124
+
125
+ def __init__(self, config: Qwen2Config):
126
+ super().__init__(config)
127
+ self.layers = nn.ModuleList(
128
+ [Qwen2EncoderLayer(config, i) for i in range(self.config.num_hidden_layers)]
129
+ )
130
+ if config.rope:
131
+ self.rotary_emb = Qwen2RotaryEmbedding(config=config)
132
+ else:
133
+ self.rotary_emb = None
134
+ if hasattr(config, "norm") and config.norm:
135
+ self.norm = Qwen2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
136
+ else:
137
+ self.norm = None
138
+ self.gradient_checkpointing = True
139
+ self.post_init()
140
+
141
+ def _init_weights(self, module):
142
+ std = self.config.initializer_range
143
+ if isinstance(module, nn.Linear):
144
+ module.weight.data.normal_(mean=0.0, std=std)
145
+ if module.bias is not None:
146
+ module.bias.data.zero_()
147
+
148
+ def forward(self, hidden_states):
149
+ bsz, seq_len, _ = hidden_states.size()
150
+ position_ids = torch.arange(seq_len, device=hidden_states.device).unsqueeze(0)
151
+
152
+
153
+ if self.rotary_emb is not None:
154
+ position_embeddings = self.rotary_emb(hidden_states, position_ids)
155
+ else:
156
+ position_embeddings = None
157
+
158
+ for layer in self.layers:
159
+ if self.gradient_checkpointing and self.training:
160
+ hidden_states = self._gradient_checkpointing_func(
161
+ layer.__call__,
162
+ hidden_states,
163
+ position_embeddings,
164
+ )
165
+ else:
166
+ hidden_states = layer(
167
+ hidden_states,
168
+ position_embeddings=position_embeddings,
169
+ )
170
+ if self.norm:
171
+ hidden_states = self.norm(hidden_states)
172
+ return hidden_states
173
+
174
+
175
+ class RMSNorm(nn.Module):
176
+ def __init__(self, dim, eps: float, elementwise_affine: bool = True):
177
+ super().__init__()
178
+ self.eps = eps
179
+ if elementwise_affine:
180
+ self.weight = nn.Parameter(torch.ones(dim))
181
+ else:
182
+ self.weight = None
183
+
184
+ def forward(self, hidden_states):
185
+ input_dtype = hidden_states.dtype
186
+ variance = hidden_states.to(torch.float32).pow(2).mean(-1, keepdim=True)
187
+ hidden_states = hidden_states * torch.rsqrt(variance + self.eps)
188
+
189
+ if self.weight is not None:
190
+
191
+ if self.weight.dtype in [torch.float16, torch.bfloat16]:
192
+ hidden_states = hidden_states.to(self.weight.dtype)
193
+ hidden_states = hidden_states * self.weight
194
+ else:
195
+ hidden_states = hidden_states.to(input_dtype)
196
+
197
+ return hidden_states
198
+
199
+
200
+ class QwenImageConnector(nn.Module):
201
+ def __init__(
202
+ self,
203
+ input_dim: int=3584,
204
+ output_dim: int=3584,
205
+ num_layers: int=6,
206
+ input_scale: float = 1.0,
207
+ ):
208
+ super().__init__()
209
+ self.encoder = Qwen2Encoder(
210
+ Qwen2Config(
211
+ hidden_size=input_dim,
212
+ intermediate_size=input_dim * 4,
213
+ num_hidden_layers=num_layers,
214
+ num_attention_heads=input_dim // 64,
215
+ num_key_value_heads=input_dim // 64,
216
+ initializer_range=0.014,
217
+ use_cache=False,
218
+ rope=True,
219
+ qk_norm=True,
220
+ ),
221
+ )
222
+ self.proj = nn.Sequential(
223
+ nn.Linear(input_dim, output_dim),
224
+ nn.GELU(approximate="tanh"),
225
+ nn.Linear(output_dim, output_dim),
226
+ RMSNorm(output_dim, eps=1e-5, elementwise_affine=True),
227
+ )
228
+
229
+
230
+ with torch.no_grad():
231
+ self.proj[-1].weight.fill_(input_scale)
232
+
233
+ def forward(self, x):
234
+ x = self.encoder(x)
235
+ x = self.proj(x)
236
+ return x
237
+
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+
246
+
247
+
248
+
249
+
250
+
251
+
252
+
253
+
254
+
255
+
256
+
257
+
258
+
259
+
260
+
diffsynth/models/qwen_image_controlnet.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ from .general_modules import RMSNorm
4
+
5
+
6
+ class BlockWiseControlBlock(torch.nn.Module):
7
+
8
+ def __init__(self, dim: int = 3072):
9
+ super().__init__()
10
+ self.x_rms = RMSNorm(dim, eps=1e-6)
11
+ self.y_rms = RMSNorm(dim, eps=1e-6)
12
+ self.input_proj = nn.Linear(dim, dim)
13
+ self.act = nn.GELU()
14
+ self.output_proj = nn.Linear(dim, dim)
15
+
16
+ def forward(self, x, y):
17
+ x, y = self.x_rms(x), self.y_rms(y)
18
+ x = self.input_proj(x + y)
19
+ x = self.act(x)
20
+ x = self.output_proj(x)
21
+ return x
22
+
23
+ def init_weights(self):
24
+
25
+ nn.init.zeros_(self.output_proj.weight)
26
+ nn.init.zeros_(self.output_proj.bias)
27
+
28
+
29
+ class QwenImageBlockWiseControlNet(torch.nn.Module):
30
+ def __init__(
31
+ self,
32
+ num_layers: int = 60,
33
+ in_dim: int = 64,
34
+ additional_in_dim: int = 0,
35
+ dim: int = 3072,
36
+ ):
37
+ super().__init__()
38
+ self.img_in = nn.Linear(in_dim + additional_in_dim, dim)
39
+ self.controlnet_blocks = nn.ModuleList(
40
+ [
41
+ BlockWiseControlBlock(dim)
42
+ for _ in range(num_layers)
43
+ ]
44
+ )
45
+
46
+ def init_weight(self):
47
+ nn.init.zeros_(self.img_in.weight)
48
+ nn.init.zeros_(self.img_in.bias)
49
+ for block in self.controlnet_blocks:
50
+ block.init_weights()
51
+
52
+ def process_controlnet_conditioning(self, controlnet_conditioning):
53
+ return self.img_in(controlnet_conditioning)
54
+
55
+ def blockwise_forward(self, img, controlnet_conditioning, block_id):
56
+ return self.controlnet_blocks[block_id](img, controlnet_conditioning)
diffsynth/models/qwen_image_dit.py ADDED
@@ -0,0 +1,572 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch, math
2
+ import torch.nn as nn
3
+ from typing import Tuple, Optional, Union, List
4
+ from einops import rearrange
5
+ from .general_modules import TimestepEmbeddings, RMSNorm, AdaLayerNorm
6
+
7
+ try:
8
+ import flash_attn_interface
9
+ FLASH_ATTN_3_AVAILABLE = True
10
+ except ModuleNotFoundError:
11
+ FLASH_ATTN_3_AVAILABLE = False
12
+
13
+
14
+ def qwen_image_flash_attention(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, num_heads: int, attention_mask = None, enable_fp8_attention: bool = False):
15
+ if FLASH_ATTN_3_AVAILABLE and attention_mask is None:
16
+ if not enable_fp8_attention:
17
+
18
+ q = rearrange(q, "b n s d -> b s n d", n=num_heads)
19
+ k = rearrange(k, "b n s d -> b s n d", n=num_heads)
20
+ v = rearrange(v, "b n s d -> b s n d", n=num_heads)
21
+ x = flash_attn_interface.flash_attn_func(q, k, v)
22
+ if isinstance(x, tuple):
23
+ x = x[0]
24
+ x = rearrange(x, "b s n d -> b s (n d)", n=num_heads)
25
+ else:
26
+ origin_dtype = q.dtype
27
+ q_std, k_std, v_std = q.std(), k.std(), v.std()
28
+ q, k, v = (q / q_std).to(torch.float8_e4m3fn), (k / k_std).to(torch.float8_e4m3fn), (v / v_std).to(torch.float8_e4m3fn)
29
+ q = rearrange(q, "b n s d -> b s n d", n=num_heads)
30
+ k = rearrange(k, "b n s d -> b s n d", n=num_heads)
31
+ v = rearrange(v, "b n s d -> b s n d", n=num_heads)
32
+ x = flash_attn_interface.flash_attn_func(q, k, v, softmax_scale=q_std * k_std / math.sqrt(q.size(-1)))
33
+ if isinstance(x, tuple):
34
+ x = x[0]
35
+ x = x.to(origin_dtype) * v_std
36
+ x = rearrange(x, "b s n d -> b s (n d)", n=num_heads)
37
+ else:
38
+ x = torch.nn.functional.scaled_dot_product_attention(q, k, v, attn_mask=attention_mask)
39
+ x = rearrange(x, "b n s d -> b s (n d)", n=num_heads)
40
+ return x
41
+
42
+
43
+ class ApproximateGELU(nn.Module):
44
+ def __init__(self, dim_in: int, dim_out: int, bias: bool = True):
45
+ super().__init__()
46
+ self.proj = nn.Linear(dim_in, dim_out, bias=bias)
47
+
48
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
49
+ x = self.proj(x)
50
+ return x * torch.sigmoid(1.702 * x)
51
+
52
+ def apply_rotary_emb_qwen(
53
+ x: torch.Tensor,
54
+ freqs_cis: Union[torch.Tensor, Tuple[torch.Tensor]]
55
+ ):
56
+ x_rotated = torch.view_as_complex(x.float().reshape(*x.shape[:-1], -1, 2))
57
+ x_out = torch.view_as_real(x_rotated * freqs_cis).flatten(3)
58
+ return x_out.type_as(x)
59
+
60
+
61
+ class QwenEmbedRope(nn.Module):
62
+ def __init__(self, theta: int, axes_dim: list[int], scale_rope=False):
63
+ super().__init__()
64
+ self.theta = theta
65
+ self.axes_dim = axes_dim
66
+ pos_index = torch.arange(4096)
67
+ neg_index = torch.arange(4096).flip(0) * -1 - 1
68
+ self.pos_freqs = torch.cat([
69
+ self.rope_params(pos_index, self.axes_dim[0], self.theta),
70
+ self.rope_params(pos_index, self.axes_dim[1], self.theta),
71
+ self.rope_params(pos_index, self.axes_dim[2], self.theta),
72
+ ], dim=1)
73
+ self.neg_freqs = torch.cat([
74
+ self.rope_params(neg_index, self.axes_dim[0], self.theta),
75
+ self.rope_params(neg_index, self.axes_dim[1], self.theta),
76
+ self.rope_params(neg_index, self.axes_dim[2], self.theta),
77
+ ], dim=1)
78
+ self.rope_cache = {}
79
+ self.scale_rope = scale_rope
80
+
81
+ def rope_params(self, index, dim, theta=10000):
82
+ """
83
+ Args:
84
+ index: [0, 1, 2, 3] 1D Tensor representing the position index of the token
85
+ """
86
+ assert dim % 2 == 0
87
+ freqs = torch.outer(
88
+ index,
89
+ 1.0 / torch.pow(theta, torch.arange(0, dim, 2).to(torch.float32).div(dim))
90
+ )
91
+ freqs = torch.polar(torch.ones_like(freqs), freqs)
92
+ return freqs
93
+
94
+
95
+ def _expand_pos_freqs_if_needed(self, video_fhw, txt_seq_lens):
96
+ if isinstance(video_fhw, list):
97
+ video_fhw = tuple(max([i[j] for i in video_fhw]) for j in range(3))
98
+ _, height, width = video_fhw
99
+ if self.scale_rope:
100
+ max_vid_index = max(height // 2, width // 2)
101
+ else:
102
+ max_vid_index = max(height, width)
103
+ required_len = max_vid_index + max(txt_seq_lens)
104
+ cur_max_len = self.pos_freqs.shape[0]
105
+ if required_len <= cur_max_len:
106
+ return
107
+
108
+ new_max_len = math.ceil(required_len / 512) * 512
109
+ pos_index = torch.arange(new_max_len)
110
+ neg_index = torch.arange(new_max_len).flip(0) * -1 - 1
111
+ self.pos_freqs = torch.cat([
112
+ self.rope_params(pos_index, self.axes_dim[0], self.theta),
113
+ self.rope_params(pos_index, self.axes_dim[1], self.theta),
114
+ self.rope_params(pos_index, self.axes_dim[2], self.theta),
115
+ ], dim=1)
116
+ self.neg_freqs = torch.cat([
117
+ self.rope_params(neg_index, self.axes_dim[0], self.theta),
118
+ self.rope_params(neg_index, self.axes_dim[1], self.theta),
119
+ self.rope_params(neg_index, self.axes_dim[2], self.theta),
120
+ ], dim=1)
121
+ return
122
+
123
+
124
+ def forward(self, video_fhw, txt_seq_lens, device):
125
+ self._expand_pos_freqs_if_needed(video_fhw, txt_seq_lens)
126
+ if self.pos_freqs.device != device:
127
+ self.pos_freqs = self.pos_freqs.to(device)
128
+ self.neg_freqs = self.neg_freqs.to(device)
129
+
130
+ vid_freqs = []
131
+ max_vid_index = 0
132
+ for idx, fhw in enumerate(video_fhw):
133
+ frame, height, width = fhw
134
+ rope_key = f"{idx}_{height}_{width}"
135
+
136
+ if rope_key not in self.rope_cache:
137
+ seq_lens = frame * height * width
138
+ freqs_pos = self.pos_freqs.split([x // 2 for x in self.axes_dim], dim=1)
139
+ freqs_neg = self.neg_freqs.split([x // 2 for x in self.axes_dim], dim=1)
140
+ freqs_frame = freqs_pos[0][idx : idx + frame].view(frame, 1, 1, -1).expand(frame, height, width, -1)
141
+ if self.scale_rope:
142
+ freqs_height = torch.cat(
143
+ [freqs_neg[1][-(height - height // 2) :], freqs_pos[1][: height // 2]], dim=0
144
+ )
145
+ freqs_height = freqs_height.view(1, height, 1, -1).expand(frame, height, width, -1)
146
+ freqs_width = torch.cat([freqs_neg[2][-(width - width // 2) :], freqs_pos[2][: width // 2]], dim=0)
147
+ freqs_width = freqs_width.view(1, 1, width, -1).expand(frame, height, width, -1)
148
+
149
+ else:
150
+ freqs_height = freqs_pos[1][:height].view(1, height, 1, -1).expand(frame, height, width, -1)
151
+ freqs_width = freqs_pos[2][:width].view(1, 1, width, -1).expand(frame, height, width, -1)
152
+
153
+ freqs = torch.cat([freqs_frame, freqs_height, freqs_width], dim=-1).reshape(seq_lens, -1)
154
+ self.rope_cache[rope_key] = freqs.clone().contiguous()
155
+ vid_freqs.append(self.rope_cache[rope_key])
156
+
157
+ if self.scale_rope:
158
+ max_vid_index = max(height // 2, width // 2, max_vid_index)
159
+ else:
160
+ max_vid_index = max(height, width, max_vid_index)
161
+
162
+ max_len = max(txt_seq_lens)
163
+ txt_freqs = self.pos_freqs[max_vid_index : max_vid_index + max_len, ...]
164
+ vid_freqs = torch.cat(vid_freqs, dim=0)
165
+
166
+ return vid_freqs, txt_freqs
167
+
168
+
169
+ def forward_sampling(self, video_fhw, txt_seq_lens, device):
170
+ self._expand_pos_freqs_if_needed(video_fhw, txt_seq_lens)
171
+ if self.pos_freqs.device != device:
172
+ self.pos_freqs = self.pos_freqs.to(device)
173
+ self.neg_freqs = self.neg_freqs.to(device)
174
+
175
+ vid_freqs = []
176
+ max_vid_index = 0
177
+ for idx, fhw in enumerate(video_fhw):
178
+ frame, height, width = fhw
179
+ rope_key = f"{idx}_{height}_{width}"
180
+ if idx > 0 and f"{0}_{height}_{width}" not in self.rope_cache:
181
+ frame_0, height_0, width_0 = video_fhw[0]
182
+
183
+ rope_key_0 = f"0_{height_0}_{width_0}"
184
+ spatial_freqs_0 = self.rope_cache[rope_key_0].reshape(frame_0, height_0, width_0, -1)
185
+ h_indices = torch.linspace(0, height_0 - 1, height).long()
186
+ w_indices = torch.linspace(0, width_0 - 1, width).long()
187
+ h_grid, w_grid = torch.meshgrid(h_indices, w_indices, indexing='ij')
188
+ sampled_rope = spatial_freqs_0[:, h_grid, w_grid, :]
189
+
190
+ freqs_pos = self.pos_freqs.split([x // 2 for x in self.axes_dim], dim=1)
191
+ freqs_frame = freqs_pos[0][idx : idx + frame].view(frame, 1, 1, -1).expand(frame, height, width, -1)
192
+ sampled_rope[:, :, :, :freqs_frame.shape[-1]] = freqs_frame
193
+
194
+ seq_lens = frame * height * width
195
+ self.rope_cache[rope_key] = sampled_rope.reshape(seq_lens, -1).clone()
196
+ if rope_key not in self.rope_cache:
197
+ seq_lens = frame * height * width
198
+ freqs_pos = self.pos_freqs.split([x // 2 for x in self.axes_dim], dim=1)
199
+ freqs_neg = self.neg_freqs.split([x // 2 for x in self.axes_dim], dim=1)
200
+ freqs_frame = freqs_pos[0][idx : idx + frame].view(frame, 1, 1, -1).expand(frame, height, width, -1)
201
+ if self.scale_rope:
202
+ freqs_height = torch.cat(
203
+ [freqs_neg[1][-(height - height // 2) :], freqs_pos[1][: height // 2]], dim=0
204
+ )
205
+ freqs_height = freqs_height.view(1, height, 1, -1).expand(frame, height, width, -1)
206
+ freqs_width = torch.cat([freqs_neg[2][-(width - width // 2) :], freqs_pos[2][: width // 2]], dim=0)
207
+ freqs_width = freqs_width.view(1, 1, width, -1).expand(frame, height, width, -1)
208
+
209
+ else:
210
+ freqs_height = freqs_pos[1][:height].view(1, height, 1, -1).expand(frame, height, width, -1)
211
+ freqs_width = freqs_pos[2][:width].view(1, 1, width, -1).expand(frame, height, width, -1)
212
+
213
+ freqs = torch.cat([freqs_frame, freqs_height, freqs_width], dim=-1).reshape(seq_lens, -1)
214
+ self.rope_cache[rope_key] = freqs.clone()
215
+ vid_freqs.append(self.rope_cache[rope_key].contiguous())
216
+
217
+ if self.scale_rope:
218
+ max_vid_index = max(height // 2, width // 2, max_vid_index)
219
+ else:
220
+ max_vid_index = max(height, width, max_vid_index)
221
+
222
+ max_len = max(txt_seq_lens)
223
+ txt_freqs = self.pos_freqs[max_vid_index : max_vid_index + max_len, ...]
224
+ vid_freqs = torch.cat(vid_freqs, dim=0)
225
+
226
+ return vid_freqs, txt_freqs
227
+
228
+
229
+ class QwenFeedForward(nn.Module):
230
+ def __init__(
231
+ self,
232
+ dim: int,
233
+ dim_out: Optional[int] = None,
234
+ dropout: float = 0.0,
235
+ ):
236
+ super().__init__()
237
+ inner_dim = int(dim * 4)
238
+ self.net = nn.ModuleList([])
239
+ self.net.append(ApproximateGELU(dim, inner_dim))
240
+ self.net.append(nn.Dropout(dropout))
241
+ self.net.append(nn.Linear(inner_dim, dim_out))
242
+
243
+ def forward(self, hidden_states: torch.Tensor, *args, **kwargs) -> torch.Tensor:
244
+ for module in self.net:
245
+ hidden_states = module(hidden_states)
246
+ return hidden_states
247
+
248
+ class QwenDoubleStreamAttention(nn.Module):
249
+ def __init__(
250
+ self,
251
+ dim_a,
252
+ dim_b,
253
+ num_heads,
254
+ head_dim,
255
+ ):
256
+ super().__init__()
257
+ self.num_heads = num_heads
258
+ self.head_dim = head_dim
259
+
260
+ self.to_q = nn.Linear(dim_a, dim_a)
261
+ self.to_k = nn.Linear(dim_a, dim_a)
262
+ self.to_v = nn.Linear(dim_a, dim_a)
263
+ self.norm_q = RMSNorm(head_dim, eps=1e-6)
264
+ self.norm_k = RMSNorm(head_dim, eps=1e-6)
265
+
266
+ self.add_q_proj = nn.Linear(dim_b, dim_b)
267
+ self.add_k_proj = nn.Linear(dim_b, dim_b)
268
+ self.add_v_proj = nn.Linear(dim_b, dim_b)
269
+ self.norm_added_q = RMSNorm(head_dim, eps=1e-6)
270
+ self.norm_added_k = RMSNorm(head_dim, eps=1e-6)
271
+
272
+ self.to_out = torch.nn.Sequential(nn.Linear(dim_a, dim_a))
273
+ self.to_add_out = nn.Linear(dim_b, dim_b)
274
+
275
+ def forward(
276
+ self,
277
+ image: torch.FloatTensor,
278
+ text: torch.FloatTensor,
279
+ image_rotary_emb: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
280
+ attention_mask: Optional[torch.FloatTensor] = None,
281
+ enable_fp8_attention: bool = False,
282
+ ) -> Tuple[torch.FloatTensor, torch.FloatTensor]:
283
+ img_q, img_k, img_v = self.to_q(image), self.to_k(image), self.to_v(image)
284
+ txt_q, txt_k, txt_v = self.add_q_proj(text), self.add_k_proj(text), self.add_v_proj(text)
285
+ seq_txt = txt_q.shape[1]
286
+
287
+ img_q = rearrange(img_q, 'b s (h d) -> b h s d', h=self.num_heads)
288
+ img_k = rearrange(img_k, 'b s (h d) -> b h s d', h=self.num_heads)
289
+ img_v = rearrange(img_v, 'b s (h d) -> b h s d', h=self.num_heads)
290
+
291
+ txt_q = rearrange(txt_q, 'b s (h d) -> b h s d', h=self.num_heads)
292
+ txt_k = rearrange(txt_k, 'b s (h d) -> b h s d', h=self.num_heads)
293
+ txt_v = rearrange(txt_v, 'b s (h d) -> b h s d', h=self.num_heads)
294
+
295
+ img_q, img_k = self.norm_q(img_q), self.norm_k(img_k)
296
+ txt_q, txt_k = self.norm_added_q(txt_q), self.norm_added_k(txt_k)
297
+
298
+ if image_rotary_emb is not None:
299
+ img_freqs, txt_freqs = image_rotary_emb
300
+ img_q = apply_rotary_emb_qwen(img_q, img_freqs)
301
+ img_k = apply_rotary_emb_qwen(img_k, img_freqs)
302
+ txt_q = apply_rotary_emb_qwen(txt_q, txt_freqs)
303
+ txt_k = apply_rotary_emb_qwen(txt_k, txt_freqs)
304
+
305
+ joint_q = torch.cat([txt_q, img_q], dim=2)
306
+ joint_k = torch.cat([txt_k, img_k], dim=2)
307
+ joint_v = torch.cat([txt_v, img_v], dim=2)
308
+
309
+ joint_attn_out = qwen_image_flash_attention(joint_q, joint_k, joint_v, num_heads=joint_q.shape[1], attention_mask=attention_mask, enable_fp8_attention=enable_fp8_attention).to(joint_q.dtype)
310
+
311
+ txt_attn_output = joint_attn_out[:, :seq_txt, :]
312
+ img_attn_output = joint_attn_out[:, seq_txt:, :]
313
+
314
+ img_attn_output = self.to_out(img_attn_output)
315
+ txt_attn_output = self.to_add_out(txt_attn_output)
316
+
317
+ return img_attn_output, txt_attn_output
318
+
319
+
320
+ class QwenImageTransformerBlock(nn.Module):
321
+ def __init__(
322
+ self,
323
+ dim: int,
324
+ num_attention_heads: int,
325
+ attention_head_dim: int,
326
+ eps: float = 1e-6,
327
+ ):
328
+ super().__init__()
329
+
330
+ self.dim = dim
331
+ self.num_attention_heads = num_attention_heads
332
+ self.attention_head_dim = attention_head_dim
333
+
334
+ self.img_mod = nn.Sequential(
335
+ nn.SiLU(),
336
+ nn.Linear(dim, 6 * dim),
337
+ )
338
+ self.img_norm1 = nn.LayerNorm(dim, elementwise_affine=False, eps=eps)
339
+ self.attn = QwenDoubleStreamAttention(
340
+ dim_a=dim,
341
+ dim_b=dim,
342
+ num_heads=num_attention_heads,
343
+ head_dim=attention_head_dim,
344
+ )
345
+ self.img_norm2 = nn.LayerNorm(dim, elementwise_affine=False, eps=eps)
346
+ self.img_mlp = QwenFeedForward(dim=dim, dim_out=dim)
347
+
348
+ self.txt_mod = nn.Sequential(
349
+ nn.SiLU(),
350
+ nn.Linear(dim, 6 * dim, bias=True),
351
+ )
352
+ self.txt_norm1 = nn.LayerNorm(dim, elementwise_affine=False, eps=eps)
353
+ self.txt_norm2 = nn.LayerNorm(dim, elementwise_affine=False, eps=eps)
354
+ self.txt_mlp = QwenFeedForward(dim=dim, dim_out=dim)
355
+
356
+
357
+
358
+
359
+
360
+
361
+ def _modulate(self, x, mod_params, index=None):
362
+ shift, scale, gate = mod_params.chunk(3, dim=-1)
363
+ if index is not None:
364
+
365
+
366
+ actual_batch = shift.size(0) // 2
367
+ shift_0, shift_1 = shift[:actual_batch], shift[actual_batch:]
368
+ scale_0, scale_1 = scale[:actual_batch], scale[actual_batch:]
369
+ gate_0, gate_1 = gate[:actual_batch], gate[actual_batch:]
370
+
371
+
372
+
373
+ index_expanded = index.unsqueeze(-1)
374
+
375
+
376
+ shift_0_exp = shift_0.unsqueeze(1)
377
+ shift_1_exp = shift_1.unsqueeze(1)
378
+ scale_0_exp = scale_0.unsqueeze(1)
379
+ scale_1_exp = scale_1.unsqueeze(1)
380
+ gate_0_exp = gate_0.unsqueeze(1)
381
+ gate_1_exp = gate_1.unsqueeze(1)
382
+
383
+
384
+ shift_result = torch.where(index_expanded == 0, shift_0_exp, shift_1_exp)
385
+ scale_result = torch.where(index_expanded == 0, scale_0_exp, scale_1_exp)
386
+ gate_result = torch.where(index_expanded == 0, gate_0_exp, gate_1_exp)
387
+ else:
388
+ shift_result = shift.unsqueeze(1)
389
+ scale_result = scale.unsqueeze(1)
390
+ gate_result = gate.unsqueeze(1)
391
+
392
+ return x * (1 + scale_result) + shift_result, gate_result
393
+
394
+
395
+ def forward(
396
+ self,
397
+ image: torch.Tensor,
398
+ text: torch.Tensor,
399
+ temb: torch.Tensor,
400
+ image_rotary_emb: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
401
+ attention_mask: Optional[torch.Tensor] = None,
402
+ enable_fp8_attention = False,
403
+ modulate_index: Optional[List[int]] = None,
404
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
405
+
406
+ img_mod_attn, img_mod_mlp = self.img_mod(temb).chunk(2, dim=-1)
407
+ if modulate_index is not None:
408
+ temb = torch.chunk(temb, 2, dim=0)[0]
409
+ txt_mod_attn, txt_mod_mlp = self.txt_mod(temb).chunk(2, dim=-1)
410
+
411
+ img_normed = self.img_norm1(image)
412
+ img_modulated, img_gate = self._modulate(img_normed, img_mod_attn, index=modulate_index)
413
+
414
+ txt_normed = self.txt_norm1(text)
415
+ txt_modulated, txt_gate = self._modulate(txt_normed, txt_mod_attn)
416
+
417
+ img_attn_out, txt_attn_out = self.attn(
418
+ image=img_modulated,
419
+ text=txt_modulated,
420
+ image_rotary_emb=image_rotary_emb,
421
+ attention_mask=attention_mask,
422
+ enable_fp8_attention=enable_fp8_attention,
423
+ )
424
+
425
+ image = image + img_gate * img_attn_out
426
+ text = text + txt_gate * txt_attn_out
427
+
428
+ img_normed_2 = self.img_norm2(image)
429
+ img_modulated_2, img_gate_2 = self._modulate(img_normed_2, img_mod_mlp, index=modulate_index)
430
+
431
+ txt_normed_2 = self.txt_norm2(text)
432
+ txt_modulated_2, txt_gate_2 = self._modulate(txt_normed_2, txt_mod_mlp)
433
+
434
+ img_mlp_out = self.img_mlp(img_modulated_2)
435
+ txt_mlp_out = self.txt_mlp(txt_modulated_2)
436
+
437
+ image = image + img_gate_2 * img_mlp_out
438
+ text = text + txt_gate_2 * txt_mlp_out
439
+
440
+ return text, image
441
+
442
+
443
+ class QwenImageDiT(torch.nn.Module):
444
+ def __init__(
445
+ self,
446
+ num_layers: int = 60,
447
+ ):
448
+ super().__init__()
449
+
450
+ self.pos_embed = QwenEmbedRope(theta=10000, axes_dim=[16,56,56], scale_rope=True)
451
+
452
+ self.time_text_embed = TimestepEmbeddings(256, 3072, diffusers_compatible_format=True, scale=1000, align_dtype_to_timestep=True)
453
+ self.txt_norm = RMSNorm(3584, eps=1e-6)
454
+
455
+ self.img_in = nn.Linear(64, 3072)
456
+ self.txt_in = nn.Linear(3584, 3072)
457
+
458
+ self.transformer_blocks = nn.ModuleList(
459
+ [
460
+ QwenImageTransformerBlock(
461
+ dim=3072,
462
+ num_attention_heads=24,
463
+ attention_head_dim=128,
464
+ )
465
+ for _ in range(num_layers)
466
+ ]
467
+ )
468
+ self.norm_out = AdaLayerNorm(3072, single=True)
469
+ self.proj_out = nn.Linear(3072, 64)
470
+
471
+
472
+ def process_entity_masks(self, latents, prompt_emb, prompt_emb_mask, entity_prompt_emb, entity_prompt_emb_mask, entity_masks, height, width, image, img_shapes):
473
+
474
+ all_prompt_emb = entity_prompt_emb + [prompt_emb]
475
+ all_prompt_emb = [self.txt_in(self.txt_norm(local_prompt_emb)) for local_prompt_emb in all_prompt_emb]
476
+ all_prompt_emb = torch.cat(all_prompt_emb, dim=1)
477
+
478
+
479
+ txt_seq_lens = prompt_emb_mask.sum(dim=1).tolist()
480
+ image_rotary_emb = self.pos_embed(img_shapes, txt_seq_lens, device=latents.device)
481
+ entity_seq_lens = [emb_mask.sum(dim=1).tolist() for emb_mask in entity_prompt_emb_mask]
482
+ entity_rotary_emb = [self.pos_embed(img_shapes, entity_seq_len, device=latents.device)[1] for entity_seq_len in entity_seq_lens]
483
+ txt_rotary_emb = torch.cat(entity_rotary_emb + [image_rotary_emb[1]], dim=0)
484
+ image_rotary_emb = (image_rotary_emb[0], txt_rotary_emb)
485
+
486
+
487
+ repeat_dim = latents.shape[1]
488
+ max_masks = entity_masks.shape[1]
489
+ entity_masks = entity_masks.repeat(1, 1, repeat_dim, 1, 1)
490
+ entity_masks = [entity_masks[:, i, None].squeeze(1) for i in range(max_masks)]
491
+ global_mask = torch.ones_like(entity_masks[0]).to(device=latents.device, dtype=latents.dtype)
492
+ entity_masks = entity_masks + [global_mask]
493
+
494
+ N = len(entity_masks)
495
+ batch_size = entity_masks[0].shape[0]
496
+ seq_lens = [mask_.sum(dim=1).item() for mask_ in entity_prompt_emb_mask] + [prompt_emb_mask.sum(dim=1).item()]
497
+ total_seq_len = sum(seq_lens) + image.shape[1]
498
+ patched_masks = []
499
+ for i in range(N):
500
+ patched_mask = rearrange(entity_masks[i], "B C (H P) (W Q) -> B (H W) (C P Q)", H=height//16, W=width//16, P=2, Q=2)
501
+ patched_masks.append(patched_mask)
502
+ attention_mask = torch.ones((batch_size, total_seq_len, total_seq_len), dtype=torch.bool).to(device=entity_masks[0].device)
503
+
504
+
505
+ image_start = sum(seq_lens)
506
+ image_end = total_seq_len
507
+ cumsum = [0]
508
+ single_image_seq = image_end - image_start
509
+ for length in seq_lens:
510
+ cumsum.append(cumsum[-1] + length)
511
+ for i in range(N):
512
+ prompt_start = cumsum[i]
513
+ prompt_end = cumsum[i+1]
514
+ image_mask = torch.sum(patched_masks[i], dim=-1) > 0
515
+ image_mask = image_mask.unsqueeze(1).repeat(1, seq_lens[i], 1)
516
+
517
+ repeat_time = single_image_seq // image_mask.shape[-1]
518
+ image_mask = image_mask.repeat(1, 1, repeat_time)
519
+
520
+ attention_mask[:, prompt_start:prompt_end, image_start:image_end] = image_mask
521
+
522
+ attention_mask[:, image_start:image_end, prompt_start:prompt_end] = image_mask.transpose(1, 2)
523
+
524
+ for i in range(N):
525
+ for j in range(N):
526
+ if i == j:
527
+ continue
528
+ start_i, end_i = cumsum[i], cumsum[i+1]
529
+ start_j, end_j = cumsum[j], cumsum[j+1]
530
+ attention_mask[:, start_i:end_i, start_j:end_j] = False
531
+
532
+ attention_mask = attention_mask.float()
533
+ attention_mask[attention_mask == 0] = float('-inf')
534
+ attention_mask[attention_mask == 1] = 0
535
+ attention_mask = attention_mask.to(device=latents.device, dtype=latents.dtype).unsqueeze(1)
536
+
537
+ return all_prompt_emb, image_rotary_emb, attention_mask
538
+
539
+
540
+ def forward(
541
+ self,
542
+ latents=None,
543
+ timestep=None,
544
+ prompt_emb=None,
545
+ prompt_emb_mask=None,
546
+ height=None,
547
+ width=None,
548
+ ):
549
+ img_shapes = [(latents.shape[0], latents.shape[2]//2, latents.shape[3]//2)]
550
+ txt_seq_lens = prompt_emb_mask.sum(dim=1).tolist()
551
+
552
+ image = rearrange(latents, "B C (H P) (W Q) -> B (H W) (C P Q)", H=height//16, W=width//16, P=2, Q=2)
553
+ image = self.img_in(image)
554
+ text = self.txt_in(self.txt_norm(prompt_emb))
555
+
556
+ conditioning = self.time_text_embed(timestep, image.dtype)
557
+
558
+ image_rotary_emb = self.pos_embed(img_shapes, txt_seq_lens, device=latents.device)
559
+
560
+ for block in self.transformer_blocks:
561
+ text, image = block(
562
+ image=image,
563
+ text=text,
564
+ temb=conditioning,
565
+ image_rotary_emb=image_rotary_emb,
566
+ )
567
+
568
+ image = self.norm_out(image, conditioning)
569
+ image = self.proj_out(image)
570
+
571
+ latents = rearrange(image, "B (H W) (C P Q) -> B C (H P) (W Q)", H=height//16, W=width//16, P=2, Q=2)
572
+ return image
diffsynth/models/qwen_image_extractor.py ADDED
@@ -0,0 +1,415 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ from einops import rearrange
4
+ from timm.models.vision_transformer import PatchEmbed
5
+
6
+
7
+
8
+ def rotate_half(x):
9
+ """Rotates half the hidden dims of the input."""
10
+ x1 = x[..., : x.shape[-1] // 2]
11
+ x2 = x[..., x.shape[-1] // 2 :]
12
+ return torch.cat((-x2, x1), dim=-1)
13
+
14
+ def apply_multimodal_rotary_pos_emb(
15
+ q: torch.Tensor,
16
+ k: torch.Tensor,
17
+ cos: torch.Tensor,
18
+ sin: torch.Tensor,
19
+ mrope_section: list[int],
20
+ unsqueeze_dim: int = 2
21
+ ) -> tuple[torch.Tensor, torch.Tensor]:
22
+ """
23
+ [重写] 严格按照 Qwen2.5-VL 的实现。
24
+
25
+ 将 3D RoPE (T, H, W) 应用于 query 和 key。
26
+
27
+ Args:
28
+ q (`torch.Tensor`): query (B, N_img, H, Hc)
29
+ k (`torch.Tensor`): key (B, N_img, H, Hc)
30
+ cos (`torch.Tensor`): cosine (3, B, N_img, Hc)
31
+ sin (`torch.Tensor`): sine (3, B, N_img, Hc)
32
+ mrope_section (`List[int]`):
33
+ T, H, W 的通道维度列表, e.g., [16, 24, 24].
34
+ 注意: 2 * sum(mrope_section) 必须等于 Hc.
35
+ unsqueeze_dim (`int`, *optional*, defaults to 2):
36
+ 为 (cos, sin) 增加的广播维度。
37
+ 在我们的 Attention 模块中, q/k 形状为 (B, N_img, H, Hc),
38
+ 因此我们使用 unsqueeze_dim=2 使 cos/sin 形状变为 (B, N_img, 1, Hc)
39
+ 以便在 H (头数) 维度上广播。
40
+ (Qwen-VL 原始代码默认为 1, 因为它在 (B, H, N_img, Hc) 上操作)
41
+ """
42
+
43
+ split_sections = mrope_section * 2
44
+
45
+
46
+ cos_chunks = cos.split(split_sections, dim=-1)
47
+ sin_chunks = sin.split(split_sections, dim=-1)
48
+
49
+
50
+
51
+ cos_emb = torch.cat(
52
+ [m[i % 3] for i, m in enumerate(cos_chunks)],
53
+ dim=-1
54
+ ).unsqueeze(unsqueeze_dim)
55
+
56
+
57
+
58
+ sin_emb = torch.cat(
59
+ [m[i % 3] for i, m in enumerate(sin_chunks)],
60
+ dim=-1
61
+ ).unsqueeze(unsqueeze_dim)
62
+
63
+
64
+
65
+ q_embed = (q * cos_emb) + (rotate_half(q) * sin_emb)
66
+ k_embed = (k * cos_emb) + (rotate_half(k) * sin_emb)
67
+
68
+ return q_embed, k_embed
69
+
70
+
71
+
72
+ class Multimodal3DRotaryEmbedding(nn.Module):
73
+ """
74
+ Qwen2.5-VL 文本模型使用的 3D RoPE 实现。
75
+ 它接收 3D 坐标 (T, H, W) 并为每个坐标计算独立的 RoPE。
76
+ """
77
+ inv_freq: torch.Tensor
78
+
79
+ def __init__(self, head_dim: int, theta: float = 10000.0, device=None):
80
+ """
81
+ dim: 应该是 head_dim (Hc)
82
+ """
83
+ super().__init__()
84
+ self.head_dim = head_dim
85
+ self.theta = theta
86
+
87
+
88
+
89
+ inv_freq = 1.0 / (
90
+ self.theta ** (torch.arange(0, self.head_dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / self.head_dim)
91
+ )
92
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
93
+ self.attention_scaling = 1.0
94
+
95
+ def forward(self, x: torch.Tensor, position_ids: torch.Tensor):
96
+ """
97
+ x: 任意张量,仅用于获取 device 和 dtype
98
+ position_ids: [3, B, N_img] (T, H, W 坐标)
99
+ """
100
+
101
+
102
+ inv_freq_expanded = self.inv_freq[None, None, :, None].float().expand(3, position_ids.shape[1], -1, 1)
103
+
104
+
105
+ position_ids_expanded = position_ids.float().unsqueeze(2)
106
+
107
+
108
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
109
+ with torch.autocast(device_type=device_type, enabled=False):
110
+
111
+
112
+ freqs = (inv_freq_expanded @ position_ids_expanded).transpose(2, 3)
113
+
114
+
115
+ emb = torch.cat((freqs, freqs), dim=-1)
116
+
117
+ cos = emb.cos() * self.attention_scaling
118
+ sin = emb.sin() * self.attention_scaling
119
+
120
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
121
+
122
+
123
+
124
+ class RMSNorm(nn.Module):
125
+ def __init__(self, hidden_size, eps=1e-6):
126
+ super().__init__()
127
+ self.weight = nn.Parameter(torch.ones(hidden_size))
128
+ self.variance_epsilon = eps
129
+
130
+ def forward(self, hidden_states):
131
+ input_dtype = hidden_states.dtype
132
+ hidden_states = hidden_states.to(torch.float32)
133
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
134
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
135
+ return self.weight * hidden_states.to(input_dtype)
136
+
137
+ class FeedForward(nn.Module):
138
+ def __init__(
139
+ self,
140
+ dim: int,
141
+ hidden_dim: int,
142
+ ):
143
+ super().__init__()
144
+ self.fc1 = nn.Linear(dim, hidden_dim)
145
+ self.act = nn.GELU()
146
+ self.fc2 = nn.Linear(hidden_dim, dim)
147
+
148
+ def forward(self, x):
149
+ x = self.fc1(x)
150
+ x = self.act(x)
151
+ x = self.fc2(x)
152
+ return x
153
+
154
+
155
+
156
+ class Attention(nn.Module):
157
+ def __init__(
158
+ self,
159
+ dim: int,
160
+ num_heads: int = 8,
161
+ qkv_bias: bool = False,
162
+ qk_norm: bool = True,
163
+ attn_drop: float = 0.,
164
+ proj_drop: float = 0.,
165
+ norm_layer: nn.Module = RMSNorm,
166
+ ) -> None:
167
+ super().__init__()
168
+ assert dim % num_heads == 0, "dim should be divisible by num_heads"
169
+
170
+ self.dim = dim
171
+ self.num_heads = num_heads
172
+ self.head_dim = dim // num_heads
173
+ self.scale = self.head_dim ** -0.5
174
+
175
+ self.mrope_section = [8,12,12]
176
+
177
+
178
+ self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
179
+ self.q_norm = norm_layer(self.head_dim) if qk_norm else nn.Identity()
180
+ self.k_norm = norm_layer(self.head_dim) if qk_norm else nn.Identity()
181
+ self.attn_drop = attn_drop
182
+ self.proj = nn.Linear(dim, dim)
183
+ self.proj_drop = nn.Dropout(proj_drop)
184
+
185
+ def forward(self, x: torch.Tensor, pos: tuple[torch.Tensor, torch.Tensor], mask=None) -> torch.Tensor:
186
+ """
187
+ x: [B, N_total, C] (N_total = N_images + N_query)
188
+ pos: (cos, sin)
189
+ cos/sin 形状为 [3, B, N_total, Hc]
190
+ """
191
+ B, N_total, C = x.shape
192
+ qkv = self.qkv(x).reshape(B, N_total, 3, self.num_heads, C // self.num_heads).permute(2, 0, 1, 3, 4)
193
+ q, k, v = qkv[0], qkv[1], qkv[2]
194
+
195
+ q = self.q_norm(q)
196
+ k = self.k_norm(k)
197
+
198
+
199
+ cos, sin = pos
200
+
201
+
202
+
203
+ q, k = apply_multimodal_rotary_pos_emb(
204
+ q,
205
+ k,
206
+ cos=cos,
207
+ sin=sin,
208
+ mrope_section=self.mrope_section,
209
+ unsqueeze_dim=2
210
+ )
211
+
212
+
213
+
214
+
215
+
216
+
217
+ q = q.transpose(1, 2)
218
+ k = k.transpose(1, 2)
219
+ v = v.transpose(1, 2)
220
+
221
+
222
+
223
+
224
+
225
+
226
+
227
+ x = torch.nn.functional.scaled_dot_product_attention(q, k, v, attn_mask=mask, dropout_p=self.attn_drop)
228
+
229
+ x = x.transpose(1, 2).reshape(B, N_total, C)
230
+ x = self.proj(x)
231
+ x = self.proj_drop(x)
232
+ return x
233
+
234
+ class Block(nn.Module):
235
+ def __init__(self, hidden_size, num_heads, mlp_ratio=4.0):
236
+ super().__init__()
237
+ self.norm1 = RMSNorm(hidden_size, eps=1e-6)
238
+
239
+ self.attn = Attention(hidden_size, num_heads=num_heads, qkv_bias=False)
240
+ self.norm2 = RMSNorm(hidden_size, eps=1e-6)
241
+ self.mlp = FeedForward(hidden_size, int(hidden_size * mlp_ratio))
242
+
243
+ def forward(self, x, pos, mask=None):
244
+
245
+ residual = x
246
+ x = self.norm1(x)
247
+ x = self.attn(x, pos, mask=mask)
248
+ x = residual + x
249
+
250
+ residual = x
251
+ x = self.norm2(x)
252
+ x = self.mlp(x)
253
+ x = residual + x
254
+ return x
255
+
256
+ class QwenImageExtractor(nn.Module):
257
+ def __init__(self, hidden_size=1024,num_layers=16,num_heads=16,query_length=256,patch_size=2,in_chans=16,output_dim=3584):
258
+ super().__init__()
259
+ self.hidden_size = hidden_size
260
+ self.num_layers = num_layers
261
+ self.num_heads = num_heads
262
+ self.head_dim = hidden_size // num_heads
263
+ self.output_dim=output_dim
264
+
265
+ self.patch_embed = PatchEmbed(
266
+ img_size = None,
267
+ patch_size = patch_size,
268
+ in_chans = in_chans,
269
+ embed_dim = hidden_size,
270
+ strict_img_size=False
271
+ )
272
+ self.query_length=query_length
273
+ self.query = nn.Parameter(torch.randn(1, self.query_length, self.hidden_size))
274
+
275
+
276
+
277
+ self.blocks = nn.ModuleList([Block(hidden_size, num_heads) for _ in range(self.num_layers)])
278
+ self.norm2 = nn.LayerNorm(hidden_size)
279
+ self.output_proj = nn.Linear(hidden_size, output_dim)
280
+
281
+
282
+
283
+
284
+ self.rotary_pos_emb = Multimodal3DRotaryEmbedding(self.head_dim)
285
+
286
+
287
+
288
+ self.cached_pos_ids_shape = None
289
+ self.cached_pos_ids = None
290
+
291
+ def _compute_dynamic_pos_ids(self, H: int, W: int, device: torch.device) -> torch.Tensor:
292
+ """
293
+ ⭐️ (修改点 4)
294
+ 根据输入的 H 和 W 动态计算 3D RoPE 的 Position IDs
295
+ 返回: [3, L_total] (L_images + L_query)
296
+ """
297
+ patch_size_h, patch_size_w = self.patch_embed.patch_size
298
+ grid_h = H // patch_size_h
299
+ grid_w = W // patch_size_w
300
+
301
+ current_shape = (grid_h, grid_w)
302
+
303
+
304
+ if current_shape == self.cached_pos_ids_shape:
305
+ if self.cached_pos_ids.device == device:
306
+ return self.cached_pos_ids
307
+ else:
308
+
309
+ self.cached_pos_ids = self.cached_pos_ids.to(device)
310
+ return self.cached_pos_ids
311
+
312
+
313
+
314
+ num_patches_per_image = grid_h * grid_w
315
+
316
+
317
+ hpos_ids = torch.arange(grid_h, device=device).unsqueeze(1).expand(-1, grid_w).flatten()
318
+ wpos_ids = torch.arange(grid_w, device=device).unsqueeze(0).expand(grid_h, -1).flatten()
319
+
320
+
321
+
322
+ tpos_ids_0 = torch.zeros(num_patches_per_image, device=device, dtype=torch.long)
323
+
324
+ pos_ids_0 = torch.stack([tpos_ids_0, hpos_ids, wpos_ids], dim=0)
325
+
326
+
327
+
328
+ tpos_ids_1 = torch.ones(num_patches_per_image, device=device, dtype=torch.long)
329
+
330
+ pos_ids_1 = torch.stack([tpos_ids_1, hpos_ids, wpos_ids], dim=0)
331
+
332
+
333
+
334
+ img_pos_ids = torch.cat([pos_ids_0, pos_ids_1], dim=1)
335
+
336
+
337
+
338
+ start_idx = max(grid_h, grid_w)
339
+ query_indices = torch.arange(start_idx, start_idx + self.query_length, device=device, dtype=torch.long)
340
+
341
+ query_pos_ids = query_indices.unsqueeze(0).expand(3, -1)
342
+
343
+ all_pos_ids = torch.cat([img_pos_ids, query_pos_ids], dim=1)
344
+
345
+
346
+ self.cached_pos_ids_shape = current_shape
347
+ self.cached_pos_ids = all_pos_ids
348
+
349
+ return all_pos_ids
350
+
351
+ def forward(self, x):
352
+ """
353
+ x: [B, 2, C, H, W]
354
+ """
355
+ B, N, C, H, W = x.shape
356
+ assert N == 2, "This model is hardcoded for N=2"
357
+
358
+
359
+
360
+
361
+ pos_ids = self._compute_dynamic_pos_ids(H, W, device=x.device)
362
+
363
+
364
+
365
+ pos_ids_batch = pos_ids.unsqueeze(1).expand(-1, B, -1)
366
+
367
+
368
+
369
+ pos_tuple = self.rotary_pos_emb(x, pos_ids_batch)
370
+
371
+
372
+ x = rearrange(x, "B N C H W -> (B N) C H W")
373
+ x = self.patch_embed(x)
374
+ x = rearrange(x, "(B N) l d -> B (N l) d", B=B, N=N)
375
+
376
+
377
+
378
+
379
+
380
+
381
+
382
+ x = torch.cat([x, self.query.repeat(B, 1, 1)], dim=1)
383
+
384
+
385
+ if x.shape[1] != pos_ids.shape[1]:
386
+ raise ValueError(
387
+ f"Total sequence length mismatch. "
388
+ f"Input tensor 'x' length is {x.shape[1]}, "
389
+ f"but calculated 3D RoPE length is {pos_ids.shape[1]}. "
390
+ f"Check H/W ({H}/{W}) vs patch_size ({self.patch_embed.patch_size})."
391
+ )
392
+
393
+ for block in self.blocks:
394
+
395
+ x = block(x, pos=pos_tuple)
396
+ x=x[:, -self.query_length:, :]
397
+
398
+ x = self.norm2(x)
399
+ x = self.output_proj(x)
400
+
401
+ return x
402
+
403
+
404
+
405
+
406
+
407
+
408
+
409
+
410
+
411
+
412
+
413
+
414
+
415
+
diffsynth/models/qwen_image_learnable_query.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+
4
+ class QwenImageLearnableQuery(nn.Module):
5
+ def __init__(self, query_length: int = 64, hidden_state_dim: int = 3584, initializer_range: float = 0.02):
6
+ super().__init__()
7
+ self.query_length = query_length
8
+ self.hidden_state_dim = hidden_state_dim
9
+ self.learnable_query = nn.Parameter(torch.randn(query_length, hidden_state_dim) * initializer_range)
10
+
11
+ def forward(self, batch_size: int):
12
+ return self.learnable_query.unsqueeze(0).expand(batch_size, -1, -1)
13
+
14
+
15
+
16
+
17
+
18
+
19
+
diffsynth/models/qwen_image_mae.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch import nn
3
+
4
+ from typing import Optional
5
+
6
+ class QwenImageMAE(nn.Module):
7
+ def __init__(
8
+ self,
9
+ ):
10
+ super().__init__()
11
+ from transformers import ViTMAEConfig, ViTMAEModel
12
+
13
+ config=ViTMAEConfig(**{
14
+ "architectures": [
15
+ "ViTMAEForPreTraining"
16
+ ],
17
+ "attention_probs_dropout_prob": 0.0,
18
+ "decoder_hidden_size": 512,
19
+ "decoder_intermediate_size": 2048,
20
+ "decoder_num_attention_heads": 16,
21
+ "decoder_num_hidden_layers": 8,
22
+ "hidden_act": "gelu",
23
+ "hidden_dropout_prob": 0.0,
24
+ "hidden_size": 1024,
25
+ "image_size": 224,
26
+ "initializer_range": 0.02,
27
+ "intermediate_size": 4096,
28
+ "layer_norm_eps": 1e-12,
29
+ "mask_ratio": 0.0,
30
+ "model_type": "vit_mae",
31
+ "norm_pix_loss": False,
32
+ "num_attention_heads": 16,
33
+ "num_channels": 3,
34
+ "num_hidden_layers": 24,
35
+ "patch_size": 16,
36
+ "qkv_bias": True,
37
+ "torch_dtype": "bfloat16",
38
+ "attn_implementation": "sdpa"
39
+ }
40
+ )
41
+
42
+ self.model = ViTMAEModel(config)
43
+ self.config=config
44
+
45
+
46
+ def forward(self, pixel_values):
47
+ outputs=self.model(pixel_values,interpolate_pos_encoding=True)
48
+ return outputs.last_hidden_state
49
+
50
+ def new_forward(self, pixel_values,sample1_shapes):
51
+ outputs=self.model.new_forward(pixel_values,sample1_shapes,interpolate_pos_encoding=True)
52
+ return outputs.last_hidden_state
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+
104
+
105
+
106
+
diffsynth/models/qwen_image_rformer.py ADDED
@@ -0,0 +1,512 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers.models.vit.modeling_vit import (
2
+ ViTConfig,
3
+ ViTPreTrainedModel,
4
+ ViTEncoder
5
+ )
6
+ from torch import nn
7
+ import torch
8
+ from typing import Optional, Dict, List, Tuple, Union
9
+ from transformers.modeling_outputs import BaseModelOutputWithPooling
10
+
11
+ class RFormerEmbeddings(nn.Module):
12
+ def __init__(self, config: ViTConfig) -> None:
13
+ super().__init__()
14
+
15
+ query_num = config.query_num
16
+ self.query_num = query_num
17
+ self.latent_motion_token = nn.Parameter(torch.zeros(1, query_num, config.hidden_size))
18
+ self.sep_token = nn.Parameter(torch.zeros(1, 1, config.hidden_size))
19
+
20
+
21
+ self.projection = nn.Linear(config.input_hidden_size, config.hidden_size, bias=True)
22
+
23
+
24
+ self.position_embeddings = nn.Parameter(torch.randn(1, config.num_patches*2 + 1 + query_num, config.hidden_size))
25
+ self.token_type_embeddings = nn.Parameter(torch.randn(2, config.hidden_size))
26
+ self.dropout = nn.Dropout(config.hidden_dropout_prob)
27
+ self.config = config
28
+
29
+ if hasattr(config, "legacy"):
30
+ self.legacy = config.legacy
31
+ else:
32
+ self.legacy = True
33
+
34
+ def forward(
35
+ self,
36
+ cond_hidden_states: torch.Tensor,
37
+ target_hidden_states: torch.Tensor,
38
+ ) -> torch.Tensor:
39
+ batch_size, per_seq_length = cond_hidden_states.shape[:2]
40
+
41
+ cond_embeddings = self.projection(cond_hidden_states)
42
+
43
+ latent_motion_tokens = self.latent_motion_token.expand(batch_size, -1, -1)
44
+ sep_tokens = self.sep_token.expand(batch_size, -1, -1)
45
+ cond_embeddings = torch.cat((latent_motion_tokens, cond_embeddings, sep_tokens), dim=1)
46
+
47
+ target_embeddings = self.projection(target_hidden_states)
48
+ embeddings = torch.cat((cond_embeddings, target_embeddings), dim=1)
49
+
50
+
51
+ embeddings = embeddings + self.position_embeddings
52
+
53
+
54
+ cond_token_type_embeddings = self.token_type_embeddings[0].expand(batch_size, per_seq_length + self.query_num + 1, -1)
55
+ if self.legacy:
56
+ target_token_type_embeddings = self.token_type_embeddings[0].expand(batch_size, per_seq_length, -1)
57
+ else:
58
+ target_token_type_embeddings = self.token_type_embeddings[1].expand(batch_size, per_seq_length, -1)
59
+ token_type_embeddings = torch.cat((cond_token_type_embeddings, target_token_type_embeddings), dim=1)
60
+ embeddings = embeddings + token_type_embeddings
61
+
62
+ embeddings = self.dropout(embeddings)
63
+
64
+ return embeddings
65
+
66
+
67
+ class RFormer2DEmbeddings(nn.Module):
68
+ def __init__(self, config: ViTConfig) -> None:
69
+ super().__init__()
70
+ query_num = config.query_num
71
+ self.query_num = query_num
72
+ self.latent_motion_token = nn.Parameter(torch.zeros(1, query_num, config.hidden_size))
73
+ self.sep_token = nn.Parameter(torch.zeros(1, 1, config.hidden_size))
74
+ self.projection = nn.Linear(config.input_hidden_size, config.hidden_size, bias=True)
75
+
76
+
77
+ self.patch_size = 16
78
+ self.max_side_len = 280
79
+ self.d_half = config.hidden_size // 2
80
+
81
+
82
+ self.pos_emb_x_cond = nn.Parameter(torch.randn(1, self.max_side_len, self.d_half))
83
+ self.pos_emb_y_cond = nn.Parameter(torch.randn(1, self.max_side_len, self.d_half))
84
+
85
+ self.cls_pos_emb_cond = nn.Parameter(torch.randn(1, 1, config.hidden_size))
86
+
87
+
88
+ self.pos_emb_x_target = nn.Parameter(torch.randn(1, self.max_side_len, self.d_half))
89
+ self.pos_emb_y_target = nn.Parameter(torch.randn(1, self.max_side_len, self.d_half))
90
+
91
+ self.cls_pos_emb_target = nn.Parameter(torch.randn(1, 1, config.hidden_size))
92
+
93
+
94
+ self.query_pos_embedding = nn.Parameter(torch.randn(1, query_num, config.hidden_size))
95
+ self.sep_pos_embedding = nn.Parameter(torch.randn(1, 1, config.hidden_size))
96
+
97
+
98
+ self.token_type_embeddings = nn.Parameter(torch.randn(2, config.hidden_size))
99
+ self.dropout = nn.Dropout(config.hidden_dropout_prob)
100
+ self.config = config
101
+ if hasattr(config, "legacy"):
102
+ self.legacy = config.legacy
103
+ else:
104
+ self.legacy = True
105
+
106
+ def _get_grid_embedding(self, pos_emb_x, pos_emb_y, h, w):
107
+ """辅助函数:生成纯粹的 2D 网格位置编码 (不含 CLS)"""
108
+
109
+ y_emb = pos_emb_y[:, :h, :]
110
+ x_emb = pos_emb_x[:, :w, :]
111
+
112
+
113
+ y_grid = y_emb.unsqueeze(2).expand(-1, -1, w, -1)
114
+ x_grid = x_emb.unsqueeze(1).expand(-1, h, -1, -1)
115
+
116
+
117
+ grid_emb_2d = torch.cat([y_grid, x_grid], dim=-1)
118
+ return grid_emb_2d[0].flatten(0, 1)
119
+
120
+ def forward(
121
+ self,
122
+ cond_hidden_states: torch.Tensor,
123
+ target_hidden_states: torch.Tensor,
124
+ sample1_shapes: List[torch.Tensor] = None
125
+ ) -> torch.Tensor:
126
+ batch_size = cond_hidden_states.shape[0]
127
+
128
+
129
+ cond_embeddings = self.projection(cond_hidden_states)
130
+ target_embeddings = self.projection(target_hidden_states)
131
+
132
+
133
+
134
+ latent_motion_tokens = self.latent_motion_token.expand(batch_size, -1, -1)
135
+ sep_tokens = self.sep_token.expand(batch_size, -1, -1)
136
+
137
+
138
+ cond_part = torch.cat((latent_motion_tokens, cond_embeddings, sep_tokens), dim=1)
139
+ embeddings = torch.cat((cond_part, target_embeddings), dim=1)
140
+
141
+
142
+
143
+ batch_pixel_h = sample1_shapes[0]
144
+ batch_pixel_w = sample1_shapes[1]
145
+
146
+ batch_pos_embeddings = []
147
+
148
+ for b in range(batch_size):
149
+
150
+ pixel_h = int(batch_pixel_h[b].item())
151
+ pixel_w = int(batch_pixel_w[b].item())
152
+ h = pixel_h // self.patch_size
153
+ w = pixel_w // self.patch_size
154
+ num_patches = h * w
155
+
156
+
157
+
158
+
159
+ len_cond = cond_hidden_states.shape[1]
160
+ has_cls_cond = (len_cond == num_patches + 1)
161
+
162
+
163
+ grid_cond = self._get_grid_embedding(self.pos_emb_x_cond, self.pos_emb_y_cond, h, w)
164
+
165
+
166
+ if has_cls_cond:
167
+
168
+
169
+ pos_emb_cond = torch.cat([self.cls_pos_emb_cond[0], grid_cond], dim=0)
170
+ else:
171
+ pos_emb_cond = grid_cond
172
+
173
+
174
+
175
+ len_target = target_hidden_states.shape[1]
176
+ has_cls_target = (len_target == num_patches + 1)
177
+
178
+ grid_target = self._get_grid_embedding(self.pos_emb_x_target, self.pos_emb_y_target, h, w)
179
+
180
+ if has_cls_target:
181
+
182
+
183
+ pos_emb_target = torch.cat([self.cls_pos_emb_target[0], grid_target], dim=0)
184
+ else:
185
+ pos_emb_target = grid_target
186
+
187
+
188
+
189
+
190
+ full_pos_emb = torch.cat([
191
+ self.query_pos_embedding[0],
192
+ pos_emb_cond,
193
+ self.sep_pos_embedding[0],
194
+ pos_emb_target
195
+ ], dim=0)
196
+
197
+ batch_pos_embeddings.append(full_pos_emb)
198
+
199
+
200
+ pos_embeddings = torch.stack(batch_pos_embeddings).to(embeddings.device)
201
+ embeddings = embeddings + pos_embeddings
202
+
203
+
204
+
205
+ len_cond_total = latent_motion_tokens.shape[1] + cond_embeddings.shape[1] + sep_tokens.shape[1]
206
+ len_target_total = target_embeddings.shape[1]
207
+
208
+
209
+ cond_token_type_embeddings = self.token_type_embeddings[0].expand(batch_size, len_cond_total, -1)
210
+
211
+
212
+ if self.legacy:
213
+ target_token_type_embeddings = self.token_type_embeddings[0].expand(batch_size, len_target_total, -1)
214
+ else:
215
+ target_token_type_embeddings = self.token_type_embeddings[1].expand(batch_size, len_target_total, -1)
216
+
217
+ token_type_embeddings = torch.cat((cond_token_type_embeddings, target_token_type_embeddings), dim=1)
218
+ embeddings = embeddings + token_type_embeddings
219
+
220
+ embeddings = self.dropout(embeddings)
221
+
222
+ return embeddings
223
+
224
+
225
+ class ViTPooler(nn.Module):
226
+ def __init__(self, config: ViTConfig):
227
+ super().__init__()
228
+ self.dense = nn.Linear(config.hidden_size, config.hidden_size)
229
+ self.activation = nn.Tanh()
230
+
231
+ def forward(self, hidden_states):
232
+
233
+
234
+ first_token_tensor = hidden_states[:, 0]
235
+ pooled_output = self.dense(first_token_tensor)
236
+ pooled_output = self.activation(pooled_output)
237
+ return pooled_output
238
+
239
+ class RFormer(ViTPreTrainedModel):
240
+ def __init__(self, add_pooling_layer: bool = False):
241
+
242
+
243
+ config = ViTConfig(
244
+ hidden_size=768,
245
+ num_hidden_layers=4,
246
+ num_attention_heads=12,
247
+ intermediate_size=3072,
248
+ hidden_act="gelu",
249
+ hidden_dropout_prob=0.0,
250
+ attention_probs_dropout_prob=0.0,
251
+ initializer_range=0.02,
252
+ layer_norm_eps=1e-12,
253
+ qkv_bias=True,
254
+ model_type="vit",
255
+
256
+ query_num=8,
257
+ input_hidden_size=1024,
258
+ num_patches=4097,
259
+ legacy=True
260
+ )
261
+
262
+
263
+ super().__init__(config)
264
+ self.config = config
265
+
266
+
267
+ self.query_num = config.query_num
268
+ self.embeddings = RFormerEmbeddings(config)
269
+ self.encoder = ViTEncoder(config)
270
+
271
+ self.layernorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
272
+ self.pooler = ViTPooler(config) if add_pooling_layer else None
273
+
274
+
275
+ self.post_init()
276
+
277
+ def _init_weights(self, module: Union[nn.Linear, nn.Conv2d, nn.LayerNorm]) -> None:
278
+ """Initialize the weights"""
279
+ if isinstance(module, (nn.Linear, nn.Conv2d)):
280
+ module.weight.data = nn.init.trunc_normal_(
281
+ module.weight.data.to(torch.float32), mean=0.0, std=self.config.initializer_range
282
+ ).to(module.weight.dtype)
283
+ if module.bias is not None:
284
+ module.bias.data.zero_()
285
+ elif isinstance(module, nn.LayerNorm):
286
+ module.bias.data.zero_()
287
+ module.weight.data.fill_(1.0)
288
+ elif isinstance(module, RFormerEmbeddings):
289
+ module.position_embeddings.data = nn.init.trunc_normal_(
290
+ module.position_embeddings.data.to(torch.float32),
291
+ mean=0.0,
292
+ std=self.config.initializer_range,
293
+ ).to(module.position_embeddings.dtype)
294
+
295
+ module.token_type_embeddings.data = nn.init.trunc_normal_(
296
+ module.token_type_embeddings.data.to(torch.float32),
297
+ mean=0.0,
298
+ std=self.config.initializer_range,
299
+ ).to(module.token_type_embeddings.dtype)
300
+
301
+ module.latent_motion_token.data = nn.init.trunc_normal_(
302
+ module.latent_motion_token.data.to(torch.float32),
303
+ mean=0.0,
304
+ std=self.config.initializer_range,
305
+ ).to(module.latent_motion_token.dtype)
306
+
307
+ module.sep_token.data = nn.init.trunc_normal_(
308
+ module.sep_token.data.to(torch.float32),
309
+ mean=0.0,
310
+ std=self.config.initializer_range,
311
+ ).to(module.sep_token.dtype)
312
+
313
+ def _prune_heads(self, heads_to_prune: Dict[int, List[int]]) -> None:
314
+ for layer, heads in heads_to_prune.items():
315
+ self.encoder.layer[layer].attention.prune_heads(heads)
316
+
317
+ def forward(
318
+ self,
319
+ cond_hidden_states: torch.Tensor,
320
+ target_hidden_states: torch.Tensor,
321
+ head_mask: Optional[torch.Tensor] = None,
322
+ output_attentions: Optional[bool] = None,
323
+ output_hidden_states: Optional[bool] = None,
324
+ return_dict: Optional[bool] = None,
325
+ ) -> Union[Tuple, BaseModelOutputWithPooling]:
326
+
327
+ output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
328
+ output_hidden_states = (
329
+ output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
330
+ )
331
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
332
+
333
+ head_mask = self.get_head_mask(head_mask, self.config.num_hidden_layers)
334
+
335
+ embedding_output = self.embeddings(
336
+ cond_hidden_states=cond_hidden_states,
337
+ target_hidden_states=target_hidden_states
338
+ )
339
+
340
+
341
+
342
+
343
+
344
+
345
+
346
+
347
+
348
+ encoder_outputs = self.encoder(
349
+ embedding_output,
350
+ head_mask=head_mask
351
+ )
352
+ sequence_output = encoder_outputs[0]
353
+ sequence_output = self.layernorm(sequence_output)
354
+ pooled_output = self.pooler(sequence_output) if self.pooler is not None else None
355
+
356
+ if not return_dict:
357
+ head_outputs = (sequence_output, pooled_output) if pooled_output is not None else (sequence_output,)
358
+ return head_outputs + encoder_outputs[1:]
359
+
360
+ return BaseModelOutputWithPooling(
361
+ last_hidden_state=sequence_output,
362
+ pooler_output=pooled_output,
363
+ hidden_states=encoder_outputs.hidden_states,
364
+ attentions=encoder_outputs.attentions,
365
+ )
366
+
367
+ class RFormer2D(ViTPreTrainedModel):
368
+ def __init__(self, add_pooling_layer: bool = False):
369
+
370
+ config = ViTConfig(
371
+ attn_implementation="sdpa",
372
+ dtype="bfloat16",
373
+ output_attentions=False,
374
+ output_hidden_states=False,
375
+ return_dict=True,
376
+ hidden_size=768,
377
+ num_hidden_layers=4,
378
+ num_attention_heads=12,
379
+ intermediate_size=3072,
380
+ hidden_act="gelu",
381
+ hidden_dropout_prob=0.0,
382
+ attention_probs_dropout_prob=0.0,
383
+ initializer_range=0.02,
384
+ layer_norm_eps=1e-12,
385
+ qkv_bias=True,
386
+ model_type="vit",
387
+
388
+ query_num=8,
389
+ input_hidden_size=1024,
390
+ num_patches=16801,
391
+ legacy=True
392
+ )
393
+
394
+ super().__init__(config)
395
+ self.config = config
396
+ self.query_num = config.query_num
397
+ self.embeddings = RFormer2DEmbeddings(config)
398
+ self.encoder = ViTEncoder(config)
399
+
400
+ self.layernorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
401
+ self.pooler = ViTPooler(config) if add_pooling_layer else None
402
+
403
+ self.post_init()
404
+
405
+ def _init_weights(self, module: Union[nn.Linear, nn.Conv2d, nn.LayerNorm]) -> None:
406
+ """Initialize the weights"""
407
+ if isinstance(module, (nn.Linear, nn.Conv2d)):
408
+ module.weight.data = nn.init.trunc_normal_(
409
+ module.weight.data.to(torch.float32), mean=0.0, std=self.config.initializer_range
410
+ ).to(module.weight.dtype)
411
+ if module.bias is not None:
412
+ module.bias.data.zero_()
413
+ elif isinstance(module, nn.LayerNorm):
414
+ module.bias.data.zero_()
415
+ module.weight.data.fill_(1.0)
416
+ elif isinstance(module, RFormer2DEmbeddings):
417
+
418
+
419
+
420
+ def init_param(param):
421
+ param.data = nn.init.trunc_normal_(
422
+ param.data.to(torch.float32),
423
+ mean=0.0,
424
+ std=self.config.initializer_range,
425
+ ).to(param.dtype)
426
+
427
+
428
+ init_param(module.pos_emb_x_cond)
429
+ init_param(module.pos_emb_y_cond)
430
+
431
+
432
+ init_param(module.pos_emb_x_target)
433
+ init_param(module.pos_emb_y_target)
434
+
435
+
436
+ init_param(module.query_pos_embedding)
437
+ init_param(module.sep_pos_embedding)
438
+
439
+
440
+ init_param(module.token_type_embeddings)
441
+ init_param(module.latent_motion_token)
442
+ init_param(module.sep_token)
443
+
444
+ init_param( module.cls_pos_emb_cond)
445
+ init_param( module.cls_pos_emb_target)
446
+
447
+
448
+ def _prune_heads(self, heads_to_prune: Dict[int, List[int]]) -> None:
449
+ for layer, heads in heads_to_prune.items():
450
+ self.encoder.layer[layer].attention.prune_heads(heads)
451
+
452
+
453
+ def forward(
454
+ self,
455
+ cond_hidden_states: torch.Tensor,
456
+ target_hidden_states: torch.Tensor,
457
+ sample1_shapes: List[torch.Tensor],
458
+ head_mask: Optional[torch.Tensor] = None,
459
+ output_attentions: Optional[bool] = None,
460
+ output_hidden_states: Optional[bool] = None,
461
+ return_dict: Optional[bool] = None,
462
+ ) -> Union[Tuple, BaseModelOutputWithPooling]:
463
+
464
+
465
+
466
+
467
+
468
+ head_mask = self.get_head_mask(head_mask, self.config.num_hidden_layers)
469
+
470
+ embedding_output = self.embeddings(
471
+ cond_hidden_states=cond_hidden_states,
472
+ target_hidden_states=target_hidden_states,
473
+ sample1_shapes=sample1_shapes
474
+ )
475
+
476
+ encoder_outputs = self.encoder(
477
+ embedding_output,
478
+ head_mask=head_mask
479
+ )
480
+ sequence_output = encoder_outputs.last_hidden_state
481
+ sequence_output = self.layernorm(sequence_output)
482
+ pooled_output = self.pooler(sequence_output) if self.pooler is not None else None
483
+
484
+
485
+
486
+
487
+
488
+ return BaseModelOutputWithPooling(
489
+ last_hidden_state=sequence_output,
490
+ pooler_output=pooled_output,
491
+ hidden_states=encoder_outputs.hidden_states,
492
+ attentions=encoder_outputs.attentions,
493
+ )
494
+
495
+
496
+
497
+
498
+
499
+
500
+
501
+
502
+
503
+
504
+
505
+
506
+
507
+
508
+
509
+
510
+
511
+
512
+
diffsynth/models/qwen_image_text_encoder.py ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from typing import Optional, Union
3
+
4
+
5
+ class QwenImageTextEncoder(torch.nn.Module):
6
+ def __init__(self):
7
+ super().__init__()
8
+ from transformers import Qwen2_5_VLConfig, Qwen2_5_VLModel
9
+ config = Qwen2_5_VLConfig(**{
10
+ "architectures": [
11
+ "Qwen2_5_VLForConditionalGeneration"
12
+ ],
13
+ "attention_dropout": 0.0,
14
+ "bos_token_id": 151643,
15
+ "eos_token_id": 151645,
16
+ "hidden_act": "silu",
17
+ "hidden_size": 3584,
18
+ "image_token_id": 151655,
19
+ "initializer_range": 0.02,
20
+ "intermediate_size": 18944,
21
+ "max_position_embeddings": 128000,
22
+ "max_window_layers": 28,
23
+ "model_type": "qwen2_5_vl",
24
+ "num_attention_heads": 28,
25
+ "num_hidden_layers": 28,
26
+ "num_key_value_heads": 4,
27
+ "rms_norm_eps": 1e-06,
28
+ "rope_scaling": {
29
+ "mrope_section": [
30
+ 16,
31
+ 24,
32
+ 24
33
+ ],
34
+ "rope_type": "default",
35
+ "type": "default"
36
+ },
37
+ "rope_theta": 1000000.0,
38
+ "sliding_window": 32768,
39
+ "text_config": {
40
+ "architectures": [
41
+ "Qwen2_5_VLForConditionalGeneration"
42
+ ],
43
+ "attention_dropout": 0.0,
44
+ "bos_token_id": 151643,
45
+ "eos_token_id": 151645,
46
+ "hidden_act": "silu",
47
+ "hidden_size": 3584,
48
+ "image_token_id": None,
49
+ "initializer_range": 0.02,
50
+ "intermediate_size": 18944,
51
+ "layer_types": [
52
+ "full_attention",
53
+ "full_attention",
54
+ "full_attention",
55
+ "full_attention",
56
+ "full_attention",
57
+ "full_attention",
58
+ "full_attention",
59
+ "full_attention",
60
+ "full_attention",
61
+ "full_attention",
62
+ "full_attention",
63
+ "full_attention",
64
+ "full_attention",
65
+ "full_attention",
66
+ "full_attention",
67
+ "full_attention",
68
+ "full_attention",
69
+ "full_attention",
70
+ "full_attention",
71
+ "full_attention",
72
+ "full_attention",
73
+ "full_attention",
74
+ "full_attention",
75
+ "full_attention",
76
+ "full_attention",
77
+ "full_attention",
78
+ "full_attention",
79
+ "full_attention"
80
+ ],
81
+ "max_position_embeddings": 128000,
82
+ "max_window_layers": 28,
83
+ "model_type": "qwen2_5_vl_text",
84
+ "num_attention_heads": 28,
85
+ "num_hidden_layers": 28,
86
+ "num_key_value_heads": 4,
87
+ "rms_norm_eps": 1e-06,
88
+ "rope_scaling": {
89
+ "mrope_section": [
90
+ 16,
91
+ 24,
92
+ 24
93
+ ],
94
+ "rope_type": "default",
95
+ "type": "default"
96
+ },
97
+ "rope_theta": 1000000.0,
98
+ "sliding_window": None,
99
+ "attn_implementation": "sdpa",
100
+ "torch_dtype": "bfloat16",
101
+ "use_cache": True,
102
+ "use_sliding_window": False,
103
+ "video_token_id": None,
104
+ "vision_end_token_id": 151653,
105
+ "vision_start_token_id": 151652,
106
+ "vision_token_id": 151654,
107
+ "vocab_size": 152064
108
+ },
109
+ "tie_word_embeddings": False,
110
+ "attn_implementation": "sdpa",
111
+ "torch_dtype": "bfloat16",
112
+ "use_cache": True,
113
+ "use_sliding_window": False,
114
+ "video_token_id": 151656,
115
+ "vision_config": {
116
+ "depth": 32,
117
+ "fullatt_block_indexes": [
118
+ 7,
119
+ 15,
120
+ 23,
121
+ 31
122
+ ],
123
+ "hidden_act": "silu",
124
+ "hidden_size": 1280,
125
+ "in_channels": 3,
126
+ "in_chans": 3,
127
+ "initializer_range": 0.02,
128
+ "intermediate_size": 3420,
129
+ "model_type": "qwen2_5_vl",
130
+ "num_heads": 16,
131
+ "out_hidden_size": 3584,
132
+ "patch_size": 14,
133
+ "spatial_merge_size": 2,
134
+ "spatial_patch_size": 14,
135
+ "temporal_patch_size": 2,
136
+ "tokens_per_second": 2,
137
+ "torch_dtype": "bfloat16",
138
+ "attn_implementation": "sdpa",
139
+ "window_size": 112
140
+ },
141
+ "vision_end_token_id": 151653,
142
+ "vision_start_token_id": 151652,
143
+ "vision_token_id": 151654,
144
+ "vocab_size": 152064
145
+ })
146
+ self.model = Qwen2_5_VLModel(config)
147
+ self.lm_head = torch.nn.Linear(config.text_config.hidden_size, config.text_config.vocab_size, bias=False)
148
+ self.config = config
149
+
150
+ def forward(
151
+ self,
152
+ input_ids: torch.LongTensor = None,
153
+ attention_mask: Optional[torch.Tensor] = None,
154
+ position_ids: Optional[torch.LongTensor] = None,
155
+ past_key_values = None,
156
+ inputs_embeds: Optional[torch.FloatTensor] = None,
157
+ labels: Optional[torch.LongTensor] = None,
158
+ use_cache: Optional[bool] = None,
159
+ output_attentions: Optional[bool] = None,
160
+ output_hidden_states: Optional[bool] = None,
161
+ pixel_values: Optional[torch.Tensor] = None,
162
+ pixel_values_videos: Optional[torch.FloatTensor] = None,
163
+ image_grid_thw: Optional[torch.LongTensor] = None,
164
+ video_grid_thw: Optional[torch.LongTensor] = None,
165
+ rope_deltas: Optional[torch.LongTensor] = None,
166
+ cache_position: Optional[torch.LongTensor] = None,
167
+ second_per_grid_ts: Optional[torch.Tensor] = None,
168
+ logits_to_keep: Union[int, torch.Tensor] = 0,
169
+ **kwargs,
170
+ ):
171
+ output_attentions = False
172
+ output_hidden_states = True
173
+
174
+ outputs = self.model(
175
+ input_ids=input_ids,
176
+ pixel_values=pixel_values,
177
+ pixel_values_videos=pixel_values_videos,
178
+ image_grid_thw=image_grid_thw,
179
+ video_grid_thw=video_grid_thw,
180
+ second_per_grid_ts=second_per_grid_ts,
181
+ position_ids=position_ids,
182
+ attention_mask=attention_mask,
183
+ past_key_values=past_key_values,
184
+ inputs_embeds=inputs_embeds,
185
+ use_cache=use_cache,
186
+ output_attentions=output_attentions,
187
+ output_hidden_states=output_hidden_states,
188
+ return_dict=True,
189
+ cache_position=cache_position,
190
+ **kwargs,
191
+ )
192
+ return outputs.hidden_states
193
+
194
+
195
+ def vision_forward(
196
+ self,
197
+ input_ids: torch.LongTensor = None,
198
+ attention_mask: Optional[torch.Tensor] = None,
199
+ position_ids: Optional[torch.LongTensor] = None,
200
+ past_key_values = None,
201
+ inputs_embeds: Optional[torch.FloatTensor] = None,
202
+ pixel_values: Optional[torch.Tensor] = None,
203
+ pixel_values_videos: Optional[torch.FloatTensor] = None,
204
+ image_grid_thw: Optional[torch.LongTensor] = None,
205
+ video_grid_thw: Optional[torch.LongTensor] = None,
206
+ cache_position: Optional[torch.LongTensor] = None,
207
+ second_per_grid_ts: Optional[torch.Tensor] = None,
208
+ **kwargs,
209
+ ):
210
+ return self.model.vision_forward(
211
+ input_ids=input_ids,
212
+ inputs_embeds=inputs_embeds,
213
+ pixel_values=pixel_values,
214
+ pixel_values_videos=pixel_values_videos,
215
+ image_grid_thw=image_grid_thw,
216
+ video_grid_thw=video_grid_thw,
217
+ attention_mask=attention_mask,
218
+ position_ids=position_ids,
219
+ past_key_values=past_key_values,
220
+ cache_position=cache_position,
221
+ second_per_grid_ts=second_per_grid_ts,
222
+ )
223
+
224
+ def llm_forward(
225
+ self,
226
+ inputs_embeds: torch.FloatTensor,
227
+ position_ids: Optional[torch.LongTensor] = None,
228
+ attention_mask: Optional[torch.Tensor] = None,
229
+ past_key_values = None,
230
+ use_cache: Optional[bool] = None,
231
+ output_attentions: Optional[bool] = None,
232
+ output_hidden_states: Optional[bool] = None,
233
+ cache_position: Optional[torch.LongTensor] = None,
234
+ **kwargs,
235
+ ):
236
+ output_attentions = False
237
+ output_hidden_states = True
238
+
239
+ outputs = self.model.llm_forward(
240
+ inputs_embeds=inputs_embeds,
241
+ position_ids=position_ids,
242
+ attention_mask=attention_mask,
243
+ past_key_values=past_key_values,
244
+ use_cache=use_cache,
245
+ output_attentions=output_attentions,
246
+ output_hidden_states=output_hidden_states,
247
+ return_dict=True,
248
+ cache_position=cache_position,
249
+ **kwargs,
250
+ )
251
+ return outputs.hidden_states
diffsynth/models/qwen_image_vae.py ADDED
@@ -0,0 +1,876 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from typing import List, Optional, Tuple, Union
3
+ from torch import nn
4
+
5
+
6
+ CACHE_T = 2
7
+
8
+ class QwenImageCausalConv3d(torch.nn.Conv3d):
9
+ r"""
10
+ A custom 3D causal convolution layer with feature caching support.
11
+
12
+ This layer extends the standard Conv3D layer by ensuring causality in the time dimension and handling feature
13
+ caching for efficient inference.
14
+
15
+ Args:
16
+ in_channels (int): Number of channels in the input image
17
+ out_channels (int): Number of channels produced by the convolution
18
+ kernel_size (int or tuple): Size of the convolving kernel
19
+ stride (int or tuple, optional): Stride of the convolution. Default: 1
20
+ padding (int or tuple, optional): Zero-padding added to all three sides of the input. Default: 0
21
+ """
22
+
23
+ def __init__(
24
+ self,
25
+ in_channels: int,
26
+ out_channels: int,
27
+ kernel_size: Union[int, Tuple[int, int, int]],
28
+ stride: Union[int, Tuple[int, int, int]] = 1,
29
+ padding: Union[int, Tuple[int, int, int]] = 0,
30
+ ) -> None:
31
+ super().__init__(
32
+ in_channels=in_channels,
33
+ out_channels=out_channels,
34
+ kernel_size=kernel_size,
35
+ stride=stride,
36
+ padding=padding,
37
+ )
38
+
39
+
40
+ self._padding = (self.padding[2], self.padding[2], self.padding[1], self.padding[1], 2 * self.padding[0], 0)
41
+ self.padding = (0, 0, 0)
42
+
43
+ def forward(self, x, cache_x=None):
44
+ padding = list(self._padding)
45
+ if cache_x is not None and self._padding[4] > 0:
46
+ cache_x = cache_x.to(x.device)
47
+ x = torch.cat([cache_x, x], dim=2)
48
+ padding[4] -= cache_x.shape[2]
49
+ x = torch.nn.functional.pad(x, padding)
50
+ return super().forward(x)
51
+
52
+
53
+
54
+ class QwenImageRMS_norm(nn.Module):
55
+ r"""
56
+ A custom RMS normalization layer.
57
+
58
+ Args:
59
+ dim (int): The number of dimensions to normalize over.
60
+ channel_first (bool, optional): Whether the input tensor has channels as the first dimension.
61
+ Default is True.
62
+ images (bool, optional): Whether the input represents image data. Default is True.
63
+ bias (bool, optional): Whether to include a learnable bias term. Default is False.
64
+ """
65
+
66
+ def __init__(self, dim: int, channel_first: bool = True, images: bool = True, bias: bool = False) -> None:
67
+ super().__init__()
68
+ broadcastable_dims = (1, 1, 1) if not images else (1, 1)
69
+ shape = (dim, *broadcastable_dims) if channel_first else (dim,)
70
+
71
+ self.channel_first = channel_first
72
+ self.scale = dim**0.5
73
+ self.gamma = nn.Parameter(torch.ones(shape))
74
+ self.bias = nn.Parameter(torch.zeros(shape)) if bias else 0.0
75
+
76
+ def forward(self, x):
77
+ return torch.nn.functional.normalize(x, dim=(1 if self.channel_first else -1)) * self.scale * self.gamma + self.bias
78
+
79
+
80
+
81
+ class QwenImageResidualBlock(nn.Module):
82
+ r"""
83
+ A custom residual block module.
84
+
85
+ Args:
86
+ in_dim (int): Number of input channels.
87
+ out_dim (int): Number of output channels.
88
+ dropout (float, optional): Dropout rate for the dropout layer. Default is 0.0.
89
+ non_linearity (str, optional): Type of non-linearity to use. Default is "silu".
90
+ """
91
+
92
+ def __init__(
93
+ self,
94
+ in_dim: int,
95
+ out_dim: int,
96
+ dropout: float = 0.0,
97
+ non_linearity: str = "silu",
98
+ ) -> None:
99
+ super().__init__()
100
+ self.in_dim = in_dim
101
+ self.out_dim = out_dim
102
+ self.nonlinearity = torch.nn.SiLU()
103
+
104
+
105
+ self.norm1 = QwenImageRMS_norm(in_dim, images=False)
106
+ self.conv1 = QwenImageCausalConv3d(in_dim, out_dim, 3, padding=1)
107
+ self.norm2 = QwenImageRMS_norm(out_dim, images=False)
108
+ self.dropout = nn.Dropout(dropout)
109
+ self.conv2 = QwenImageCausalConv3d(out_dim, out_dim, 3, padding=1)
110
+ self.conv_shortcut = QwenImageCausalConv3d(in_dim, out_dim, 1) if in_dim != out_dim else nn.Identity()
111
+
112
+ def forward(self, x, feat_cache=None, feat_idx=[0]):
113
+
114
+ h = self.conv_shortcut(x)
115
+
116
+
117
+ x = self.norm1(x)
118
+ x = self.nonlinearity(x)
119
+
120
+ if feat_cache is not None:
121
+ idx = feat_idx[0]
122
+ cache_x = x[:, :, -CACHE_T:, :, :].clone()
123
+ if cache_x.shape[2] < 2 and feat_cache[idx] is not None:
124
+ cache_x = torch.cat([feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to(cache_x.device), cache_x], dim=2)
125
+
126
+ x = self.conv1(x, feat_cache[idx])
127
+ feat_cache[idx] = cache_x
128
+ feat_idx[0] += 1
129
+ else:
130
+ x = self.conv1(x)
131
+
132
+
133
+ x = self.norm2(x)
134
+ x = self.nonlinearity(x)
135
+
136
+
137
+ x = self.dropout(x)
138
+
139
+ if feat_cache is not None:
140
+ idx = feat_idx[0]
141
+ cache_x = x[:, :, -CACHE_T:, :, :].clone()
142
+ if cache_x.shape[2] < 2 and feat_cache[idx] is not None:
143
+ cache_x = torch.cat([feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to(cache_x.device), cache_x], dim=2)
144
+
145
+ x = self.conv2(x, feat_cache[idx])
146
+ feat_cache[idx] = cache_x
147
+ feat_idx[0] += 1
148
+ else:
149
+ x = self.conv2(x)
150
+
151
+
152
+ return x + h
153
+
154
+
155
+
156
+ class QwenImageAttentionBlock(nn.Module):
157
+ r"""
158
+ Causal self-attention with a single head.
159
+
160
+ Args:
161
+ dim (int): The number of channels in the input tensor.
162
+ """
163
+
164
+ def __init__(self, dim):
165
+ super().__init__()
166
+ self.dim = dim
167
+
168
+
169
+ self.norm = QwenImageRMS_norm(dim)
170
+ self.to_qkv = nn.Conv2d(dim, dim * 3, 1)
171
+ self.proj = nn.Conv2d(dim, dim, 1)
172
+
173
+ def forward(self, x):
174
+ identity = x
175
+ batch_size, channels, time, height, width = x.size()
176
+
177
+ x = x.permute(0, 2, 1, 3, 4).reshape(batch_size * time, channels, height, width)
178
+ x = self.norm(x)
179
+
180
+
181
+ qkv = self.to_qkv(x)
182
+ qkv = qkv.reshape(batch_size * time, 1, channels * 3, -1)
183
+ qkv = qkv.permute(0, 1, 3, 2).contiguous()
184
+ q, k, v = qkv.chunk(3, dim=-1)
185
+
186
+
187
+ x = torch.nn.functional.scaled_dot_product_attention(q, k, v)
188
+
189
+ x = x.squeeze(1).permute(0, 2, 1).reshape(batch_size * time, channels, height, width)
190
+
191
+
192
+ x = self.proj(x)
193
+
194
+
195
+ x = x.view(batch_size, time, channels, height, width)
196
+ x = x.permute(0, 2, 1, 3, 4)
197
+
198
+ return x + identity
199
+
200
+
201
+
202
+ class QwenImageUpsample(nn.Upsample):
203
+ r"""
204
+ Perform upsampling while ensuring the output tensor has the same data type as the input.
205
+
206
+ Args:
207
+ x (torch.Tensor): Input tensor to be upsampled.
208
+
209
+ Returns:
210
+ torch.Tensor: Upsampled tensor with the same data type as the input.
211
+ """
212
+
213
+ def forward(self, x):
214
+ return super().forward(x.float()).type_as(x)
215
+
216
+
217
+
218
+ class QwenImageResample(nn.Module):
219
+ r"""
220
+ A custom resampling module for 2D and 3D data.
221
+
222
+ Args:
223
+ dim (int): The number of input/output channels.
224
+ mode (str): The resampling mode. Must be one of:
225
+ - 'none': No resampling (identity operation).
226
+ - 'upsample2d': 2D upsampling with nearest-exact interpolation and convolution.
227
+ - 'upsample3d': 3D upsampling with nearest-exact interpolation, convolution, and causal 3D convolution.
228
+ - 'downsample2d': 2D downsampling with zero-padding and convolution.
229
+ - 'downsample3d': 3D downsampling with zero-padding, convolution, and causal 3D convolution.
230
+ """
231
+
232
+ def __init__(self, dim: int, mode: str) -> None:
233
+ super().__init__()
234
+ self.dim = dim
235
+ self.mode = mode
236
+
237
+
238
+ if mode == "upsample2d":
239
+ self.resample = nn.Sequential(
240
+ QwenImageUpsample(scale_factor=(2.0, 2.0), mode="nearest-exact"), nn.Conv2d(dim, dim // 2, 3, padding=1)
241
+ )
242
+ elif mode == "upsample3d":
243
+ self.resample = nn.Sequential(
244
+ QwenImageUpsample(scale_factor=(2.0, 2.0), mode="nearest-exact"), nn.Conv2d(dim, dim // 2, 3, padding=1)
245
+ )
246
+ self.time_conv = QwenImageCausalConv3d(dim, dim * 2, (3, 1, 1), padding=(1, 0, 0))
247
+
248
+ elif mode == "downsample2d":
249
+ self.resample = nn.Sequential(nn.ZeroPad2d((0, 1, 0, 1)), nn.Conv2d(dim, dim, 3, stride=(2, 2)))
250
+ elif mode == "downsample3d":
251
+ self.resample = nn.Sequential(nn.ZeroPad2d((0, 1, 0, 1)), nn.Conv2d(dim, dim, 3, stride=(2, 2)))
252
+ self.time_conv = QwenImageCausalConv3d(dim, dim, (3, 1, 1), stride=(2, 1, 1), padding=(0, 0, 0))
253
+
254
+ else:
255
+ self.resample = nn.Identity()
256
+
257
+ def forward(self, x, feat_cache=None, feat_idx=[0]):
258
+ b, c, t, h, w = x.size()
259
+ if self.mode == "upsample3d":
260
+ if feat_cache is not None:
261
+ idx = feat_idx[0]
262
+ if feat_cache[idx] is None:
263
+ feat_cache[idx] = "Rep"
264
+ feat_idx[0] += 1
265
+ else:
266
+ cache_x = x[:, :, -CACHE_T:, :, :].clone()
267
+ if cache_x.shape[2] < 2 and feat_cache[idx] is not None and feat_cache[idx] != "Rep":
268
+
269
+ cache_x = torch.cat(
270
+ [feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to(cache_x.device), cache_x], dim=2
271
+ )
272
+ if cache_x.shape[2] < 2 and feat_cache[idx] is not None and feat_cache[idx] == "Rep":
273
+ cache_x = torch.cat([torch.zeros_like(cache_x).to(cache_x.device), cache_x], dim=2)
274
+ if feat_cache[idx] == "Rep":
275
+ x = self.time_conv(x)
276
+ else:
277
+ x = self.time_conv(x, feat_cache[idx])
278
+ feat_cache[idx] = cache_x
279
+ feat_idx[0] += 1
280
+
281
+ x = x.reshape(b, 2, c, t, h, w)
282
+ x = torch.stack((x[:, 0, :, :, :, :], x[:, 1, :, :, :, :]), 3)
283
+ x = x.reshape(b, c, t * 2, h, w)
284
+ t = x.shape[2]
285
+ x = x.permute(0, 2, 1, 3, 4).reshape(b * t, c, h, w)
286
+ x = self.resample(x)
287
+ x = x.view(b, t, x.size(1), x.size(2), x.size(3)).permute(0, 2, 1, 3, 4)
288
+
289
+ if self.mode == "downsample3d":
290
+ if feat_cache is not None:
291
+ idx = feat_idx[0]
292
+ if feat_cache[idx] is None:
293
+ feat_cache[idx] = x.clone()
294
+ feat_idx[0] += 1
295
+ else:
296
+ cache_x = x[:, :, -1:, :, :].clone()
297
+ x = self.time_conv(torch.cat([feat_cache[idx][:, :, -1:, :, :], x], 2))
298
+ feat_cache[idx] = cache_x
299
+ feat_idx[0] += 1
300
+ return x
301
+
302
+
303
+
304
+ class QwenImageMidBlock(nn.Module):
305
+ """
306
+ Middle block for WanVAE encoder and decoder.
307
+
308
+ Args:
309
+ dim (int): Number of input/output channels.
310
+ dropout (float): Dropout rate.
311
+ non_linearity (str): Type of non-linearity to use.
312
+ """
313
+
314
+ def __init__(self, dim: int, dropout: float = 0.0, non_linearity: str = "silu", num_layers: int = 1):
315
+ super().__init__()
316
+ self.dim = dim
317
+
318
+
319
+ resnets = [QwenImageResidualBlock(dim, dim, dropout, non_linearity)]
320
+ attentions = []
321
+ for _ in range(num_layers):
322
+ attentions.append(QwenImageAttentionBlock(dim))
323
+ resnets.append(QwenImageResidualBlock(dim, dim, dropout, non_linearity))
324
+ self.attentions = nn.ModuleList(attentions)
325
+ self.resnets = nn.ModuleList(resnets)
326
+
327
+ self.gradient_checkpointing = False
328
+
329
+ def forward(self, x, feat_cache=None, feat_idx=[0]):
330
+
331
+ x = self.resnets[0](x, feat_cache, feat_idx)
332
+
333
+
334
+ for attn, resnet in zip(self.attentions, self.resnets[1:]):
335
+ if attn is not None:
336
+ x = attn(x)
337
+
338
+ x = resnet(x, feat_cache, feat_idx)
339
+
340
+ return x
341
+
342
+
343
+
344
+ class QwenImageEncoder3d(nn.Module):
345
+ r"""
346
+ A 3D encoder module.
347
+
348
+ Args:
349
+ dim (int): The base number of channels in the first layer.
350
+ z_dim (int): The dimensionality of the latent space.
351
+ dim_mult (list of int): Multipliers for the number of channels in each block.
352
+ num_res_blocks (int): Number of residual blocks in each block.
353
+ attn_scales (list of float): Scales at which to apply attention mechanisms.
354
+ temperal_downsample (list of bool): Whether to downsample temporally in each block.
355
+ dropout (float): Dropout rate for the dropout layers.
356
+ non_linearity (str): Type of non-linearity to use.
357
+ """
358
+
359
+ def __init__(
360
+ self,
361
+ dim=128,
362
+ z_dim=4,
363
+ dim_mult=[1, 2, 4, 4],
364
+ num_res_blocks=2,
365
+ attn_scales=[],
366
+ temperal_downsample=[True, True, False],
367
+ dropout=0.0,
368
+ non_linearity: str = "silu",
369
+ ):
370
+ super().__init__()
371
+ self.dim = dim
372
+ self.z_dim = z_dim
373
+ self.dim_mult = dim_mult
374
+ self.num_res_blocks = num_res_blocks
375
+ self.attn_scales = attn_scales
376
+ self.temperal_downsample = temperal_downsample
377
+ self.nonlinearity = torch.nn.SiLU()
378
+
379
+
380
+ dims = [dim * u for u in [1] + dim_mult]
381
+ scale = 1.0
382
+
383
+
384
+ self.conv_in = QwenImageCausalConv3d(3, dims[0], 3, padding=1)
385
+
386
+
387
+ self.down_blocks = torch.nn.ModuleList([])
388
+ for i, (in_dim, out_dim) in enumerate(zip(dims[:-1], dims[1:])):
389
+
390
+ for _ in range(num_res_blocks):
391
+ self.down_blocks.append(QwenImageResidualBlock(in_dim, out_dim, dropout))
392
+ if scale in attn_scales:
393
+ self.down_blocks.append(QwenImageAttentionBlock(out_dim))
394
+ in_dim = out_dim
395
+
396
+
397
+ if i != len(dim_mult) - 1:
398
+ mode = "downsample3d" if temperal_downsample[i] else "downsample2d"
399
+ self.down_blocks.append(QwenImageResample(out_dim, mode=mode))
400
+ scale /= 2.0
401
+
402
+
403
+ self.mid_block = QwenImageMidBlock(out_dim, dropout, non_linearity, num_layers=1)
404
+
405
+
406
+ self.norm_out = QwenImageRMS_norm(out_dim, images=False)
407
+ self.conv_out = QwenImageCausalConv3d(out_dim, z_dim, 3, padding=1)
408
+
409
+ self.gradient_checkpointing = False
410
+
411
+ def forward(self, x, feat_cache=None, feat_idx=[0]):
412
+ if feat_cache is not None:
413
+ idx = feat_idx[0]
414
+ cache_x = x[:, :, -CACHE_T:, :, :].clone()
415
+ if cache_x.shape[2] < 2 and feat_cache[idx] is not None:
416
+
417
+ cache_x = torch.cat([feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to(cache_x.device), cache_x], dim=2)
418
+ x = self.conv_in(x, feat_cache[idx])
419
+ feat_cache[idx] = cache_x
420
+ feat_idx[0] += 1
421
+ else:
422
+ x = self.conv_in(x)
423
+
424
+
425
+ for layer in self.down_blocks:
426
+ if feat_cache is not None:
427
+ x = layer(x, feat_cache, feat_idx)
428
+ else:
429
+ x = layer(x)
430
+
431
+
432
+ x = self.mid_block(x, feat_cache, feat_idx)
433
+
434
+
435
+ x = self.norm_out(x)
436
+ x = self.nonlinearity(x)
437
+ if feat_cache is not None:
438
+ idx = feat_idx[0]
439
+ cache_x = x[:, :, -CACHE_T:, :, :].clone()
440
+ if cache_x.shape[2] < 2 and feat_cache[idx] is not None:
441
+
442
+ cache_x = torch.cat([feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to(cache_x.device), cache_x], dim=2)
443
+ x = self.conv_out(x, feat_cache[idx])
444
+ feat_cache[idx] = cache_x
445
+ feat_idx[0] += 1
446
+ else:
447
+ x = self.conv_out(x)
448
+ return x
449
+
450
+
451
+
452
+ class QwenImageUpBlock(nn.Module):
453
+ """
454
+ A block that handles upsampling for the WanVAE decoder.
455
+
456
+ Args:
457
+ in_dim (int): Input dimension
458
+ out_dim (int): Output dimension
459
+ num_res_blocks (int): Number of residual blocks
460
+ dropout (float): Dropout rate
461
+ upsample_mode (str, optional): Mode for upsampling ('upsample2d' or 'upsample3d')
462
+ non_linearity (str): Type of non-linearity to use
463
+ """
464
+
465
+ def __init__(
466
+ self,
467
+ in_dim: int,
468
+ out_dim: int,
469
+ num_res_blocks: int,
470
+ dropout: float = 0.0,
471
+ upsample_mode: Optional[str] = None,
472
+ non_linearity: str = "silu",
473
+ ):
474
+ super().__init__()
475
+ self.in_dim = in_dim
476
+ self.out_dim = out_dim
477
+
478
+
479
+ resnets = []
480
+
481
+ current_dim = in_dim
482
+ for _ in range(num_res_blocks + 1):
483
+ resnets.append(QwenImageResidualBlock(current_dim, out_dim, dropout, non_linearity))
484
+ current_dim = out_dim
485
+
486
+ self.resnets = nn.ModuleList(resnets)
487
+
488
+
489
+ self.upsamplers = None
490
+ if upsample_mode is not None:
491
+ self.upsamplers = nn.ModuleList([QwenImageResample(out_dim, mode=upsample_mode)])
492
+
493
+ self.gradient_checkpointing = False
494
+
495
+ def forward(self, x, feat_cache=None, feat_idx=[0]):
496
+ """
497
+ Forward pass through the upsampling block.
498
+
499
+ Args:
500
+ x (torch.Tensor): Input tensor
501
+ feat_cache (list, optional): Feature cache for causal convolutions
502
+ feat_idx (list, optional): Feature index for cache management
503
+
504
+ Returns:
505
+ torch.Tensor: Output tensor
506
+ """
507
+ for resnet in self.resnets:
508
+ if feat_cache is not None:
509
+ x = resnet(x, feat_cache, feat_idx)
510
+ else:
511
+ x = resnet(x)
512
+
513
+ if self.upsamplers is not None:
514
+ if feat_cache is not None:
515
+ x = self.upsamplers[0](x, feat_cache, feat_idx)
516
+ else:
517
+ x = self.upsamplers[0](x)
518
+ return x
519
+
520
+
521
+
522
+ class QwenImageDecoder3d(nn.Module):
523
+ r"""
524
+ A 3D decoder module.
525
+
526
+ Args:
527
+ dim (int): The base number of channels in the first layer.
528
+ z_dim (int): The dimensionality of the latent space.
529
+ dim_mult (list of int): Multipliers for the number of channels in each block.
530
+ num_res_blocks (int): Number of residual blocks in each block.
531
+ attn_scales (list of float): Scales at which to apply attention mechanisms.
532
+ temperal_upsample (list of bool): Whether to upsample temporally in each block.
533
+ dropout (float): Dropout rate for the dropout layers.
534
+ non_linearity (str): Type of non-linearity to use.
535
+ """
536
+
537
+ def __init__(
538
+ self,
539
+ dim=128,
540
+ z_dim=4,
541
+ dim_mult=[1, 2, 4, 4],
542
+ num_res_blocks=2,
543
+ attn_scales=[],
544
+ temperal_upsample=[False, True, True],
545
+ dropout=0.0,
546
+ non_linearity: str = "silu",
547
+ ):
548
+ super().__init__()
549
+ self.dim = dim
550
+ self.z_dim = z_dim
551
+ self.dim_mult = dim_mult
552
+ self.num_res_blocks = num_res_blocks
553
+ self.attn_scales = attn_scales
554
+ self.temperal_upsample = temperal_upsample
555
+
556
+ self.nonlinearity = torch.nn.SiLU()
557
+
558
+
559
+ dims = [dim * u for u in [dim_mult[-1]] + dim_mult[::-1]]
560
+ scale = 1.0 / 2 ** (len(dim_mult) - 2)
561
+
562
+
563
+ self.conv_in = QwenImageCausalConv3d(z_dim, dims[0], 3, padding=1)
564
+
565
+
566
+ self.mid_block = QwenImageMidBlock(dims[0], dropout, non_linearity, num_layers=1)
567
+
568
+
569
+ self.up_blocks = nn.ModuleList([])
570
+ for i, (in_dim, out_dim) in enumerate(zip(dims[:-1], dims[1:])):
571
+
572
+ if i > 0:
573
+ in_dim = in_dim // 2
574
+
575
+
576
+ upsample_mode = None
577
+ if i != len(dim_mult) - 1:
578
+ upsample_mode = "upsample3d" if temperal_upsample[i] else "upsample2d"
579
+
580
+
581
+ up_block = QwenImageUpBlock(
582
+ in_dim=in_dim,
583
+ out_dim=out_dim,
584
+ num_res_blocks=num_res_blocks,
585
+ dropout=dropout,
586
+ upsample_mode=upsample_mode,
587
+ non_linearity=non_linearity,
588
+ )
589
+ self.up_blocks.append(up_block)
590
+
591
+
592
+ if upsample_mode is not None:
593
+ scale *= 2.0
594
+
595
+
596
+ self.norm_out = QwenImageRMS_norm(out_dim, images=False)
597
+ self.conv_out = QwenImageCausalConv3d(out_dim, 3, 3, padding=1)
598
+
599
+ self.gradient_checkpointing = False
600
+
601
+ def forward(self, x, feat_cache=None, feat_idx=[0]):
602
+
603
+ if feat_cache is not None:
604
+ idx = feat_idx[0]
605
+ cache_x = x[:, :, -CACHE_T:, :, :].clone()
606
+ if cache_x.shape[2] < 2 and feat_cache[idx] is not None:
607
+
608
+ cache_x = torch.cat([feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to(cache_x.device), cache_x], dim=2)
609
+ x = self.conv_in(x, feat_cache[idx])
610
+ feat_cache[idx] = cache_x
611
+ feat_idx[0] += 1
612
+ else:
613
+ x = self.conv_in(x)
614
+
615
+
616
+ x = self.mid_block(x, feat_cache, feat_idx)
617
+
618
+
619
+ for up_block in self.up_blocks:
620
+ x = up_block(x, feat_cache, feat_idx)
621
+
622
+
623
+ x = self.norm_out(x)
624
+ x = self.nonlinearity(x)
625
+ if feat_cache is not None:
626
+ idx = feat_idx[0]
627
+ cache_x = x[:, :, -CACHE_T:, :, :].clone()
628
+ if cache_x.shape[2] < 2 and feat_cache[idx] is not None:
629
+
630
+ cache_x = torch.cat([feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to(cache_x.device), cache_x], dim=2)
631
+ x = self.conv_out(x, feat_cache[idx])
632
+ feat_cache[idx] = cache_x
633
+ feat_idx[0] += 1
634
+ else:
635
+ x = self.conv_out(x)
636
+ return x
637
+
638
+
639
+
640
+ class QwenImageVAE(torch.nn.Module):
641
+ def __init__(
642
+ self,
643
+ base_dim: int = 96,
644
+ z_dim: int = 16,
645
+ dim_mult: Tuple[int] = [1, 2, 4, 4],
646
+ num_res_blocks: int = 2,
647
+ attn_scales: List[float] = [],
648
+ temperal_downsample: List[bool] = [False, True, True],
649
+ dropout: float = 0.0,
650
+ ) -> None:
651
+ super().__init__()
652
+
653
+ self.z_dim = z_dim
654
+ self.temperal_downsample = temperal_downsample
655
+ self.temperal_upsample = temperal_downsample[::-1]
656
+
657
+ self.encoder = QwenImageEncoder3d(
658
+ base_dim, z_dim * 2, dim_mult, num_res_blocks, attn_scales, self.temperal_downsample, dropout
659
+ )
660
+ self.quant_conv = QwenImageCausalConv3d(z_dim * 2, z_dim * 2, 1)
661
+ self.post_quant_conv = QwenImageCausalConv3d(z_dim, z_dim, 1)
662
+
663
+ self.decoder = QwenImageDecoder3d(
664
+ base_dim, z_dim, dim_mult, num_res_blocks, attn_scales, self.temperal_upsample, dropout
665
+ )
666
+
667
+ mean = [
668
+ -0.7571,
669
+ -0.7089,
670
+ -0.9113,
671
+ 0.1075,
672
+ -0.1745,
673
+ 0.9653,
674
+ -0.1517,
675
+ 1.5508,
676
+ 0.4134,
677
+ -0.0715,
678
+ 0.5517,
679
+ -0.3632,
680
+ -0.1922,
681
+ -0.9497,
682
+ 0.2503,
683
+ -0.2921,
684
+ ]
685
+ std = [
686
+ 2.8184,
687
+ 1.4541,
688
+ 2.3275,
689
+ 2.6558,
690
+ 1.2196,
691
+ 1.7708,
692
+ 2.6052,
693
+ 2.0743,
694
+ 3.2687,
695
+ 2.1526,
696
+ 2.8652,
697
+ 1.5579,
698
+ 1.6382,
699
+ 1.1253,
700
+ 2.8251,
701
+ 1.9160,
702
+ ]
703
+ self.mean = torch.tensor(mean).view(1, 16, 1, 1, 1)
704
+ self.std = 1 / torch.tensor(std).view(1, 16, 1, 1, 1)
705
+
706
+ def encode(self, x, **kwargs):
707
+ x = x.unsqueeze(2)
708
+ x = self.encoder(x)
709
+ x = self.quant_conv(x)
710
+ x = x[:, :16]
711
+ mean, std = self.mean.to(dtype=x.dtype, device=x.device), self.std.to(dtype=x.dtype, device=x.device)
712
+ x = (x - mean) * std
713
+ x = x.squeeze(2)
714
+ return x
715
+
716
+ def decode(self, x, **kwargs):
717
+ x = x.unsqueeze(2)
718
+ mean, std = self.mean.to(dtype=x.dtype, device=x.device), self.std.to(dtype=x.dtype, device=x.device)
719
+ x = x / std + mean
720
+ x = self.post_quant_conv(x)
721
+ x = self.decoder(x)
722
+ x = x.squeeze(2)
723
+ return x
724
+
725
+
726
+
727
+
728
+
729
+
730
+
731
+
732
+
733
+
734
+
735
+
736
+
737
+
738
+
739
+
740
+
741
+
742
+
743
+
744
+
745
+
746
+
747
+
748
+
749
+
750
+
751
+
752
+
753
+
754
+
755
+
756
+
757
+
758
+
759
+
760
+
761
+
762
+
763
+
764
+
765
+
766
+
767
+
768
+
769
+
770
+
771
+
772
+
773
+
774
+
775
+
776
+
777
+
778
+
779
+
780
+
781
+
782
+
783
+
784
+
785
+
786
+
787
+
788
+
789
+
790
+
791
+
792
+
793
+
794
+
795
+
796
+
797
+
798
+
799
+
800
+
801
+
802
+
803
+
804
+
805
+
806
+
807
+
808
+
809
+
810
+
811
+
812
+
813
+
814
+
815
+
816
+
817
+
818
+
819
+
820
+
821
+
822
+
823
+
824
+
825
+
826
+
827
+
828
+
829
+
830
+
831
+
832
+
833
+
834
+
835
+
836
+
837
+
838
+
839
+
840
+
841
+
842
+
843
+
844
+
845
+
846
+
847
+
848
+
849
+
850
+
851
+
852
+
853
+
854
+
855
+
856
+
857
+
858
+
859
+
860
+
861
+
862
+
863
+
864
+
865
+
866
+
867
+
868
+
869
+
870
+
871
+
872
+
873
+
874
+
875
+
876
+
diffsynth/pipelines/__init__.py ADDED
File without changes
diffsynth/pipelines/qwen_image.py ADDED
@@ -0,0 +1,1374 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch, math
2
+ from PIL import Image
3
+ from typing import Union
4
+ from tqdm import tqdm
5
+ from einops import rearrange
6
+ import numpy as np
7
+ from math import prod
8
+ import cv2
9
+ from torchvision.transforms import v2, InterpolationMode
10
+
11
+ from ..diffusion import FlowMatchScheduler
12
+ from ..core import ModelConfig, gradient_checkpoint_forward
13
+ from ..diffusion.base_pipeline import BasePipeline, PipelineUnit, ControlNetInput
14
+
15
+ from ..models.qwen_image_dit import QwenImageDiT
16
+ from ..models.qwen_image_text_encoder import QwenImageTextEncoder
17
+ from ..models.qwen_image_vae import QwenImageVAE
18
+ from ..models.qwen_image_controlnet import QwenImageBlockWiseControlNet
19
+ from ..models.qwen_image_connector import QwenImageConnector
20
+ from ..models.qwen_image_learnable_query import QwenImageLearnableQuery
21
+ from ..models.qwen_image_extractor import QwenImageExtractor
22
+ from ..models.qwen_image_rformer import RFormer,RFormer2D
23
+ from ..models.qwen_image_mae import QwenImageMAE
24
+
25
+ import torch
26
+ import random
27
+ torch.set_printoptions(threshold=10_000_000)
28
+
29
+
30
+ def extract_polygons(item):
31
+ """
32
+ 递归解析 edit_region,提取所有的多边形轮廓。
33
+ 目标是找到所有的 list of points [[x,y], [x,y], ...]
34
+ """
35
+ if not isinstance(item, list):
36
+ return []
37
+
38
+
39
+ if len(item) == 2 and all(isinstance(x, (int, float)) for x in item):
40
+ return None
41
+
42
+
43
+
44
+ first_elem = item[0] if len(item) > 0 else None
45
+ if isinstance(first_elem, list) and len(first_elem) == 2 and all(isinstance(x, (int, float)) for x in first_elem):
46
+
47
+
48
+ return [np.array(item, dtype=np.float32)]
49
+
50
+
51
+ polys = []
52
+ for sub in item:
53
+ res = extract_polygons(sub)
54
+ if res:
55
+ polys.extend(res)
56
+ return polys
57
+
58
+ class QwenImagePipeline(BasePipeline):
59
+
60
+ def __init__(self, device="cuda", torch_dtype=torch.bfloat16):
61
+ super().__init__(
62
+ device=device, torch_dtype=torch_dtype,
63
+ height_division_factor=16, width_division_factor=16,
64
+ )
65
+ from transformers import Qwen2Tokenizer, Qwen2VLProcessor
66
+
67
+
68
+ self.scheduler = FlowMatchScheduler("Qwen-Image")
69
+ self.text_encoder: QwenImageTextEncoder = None
70
+ self.connector: QwenImageConnector=None
71
+ self.learnable_query: QwenImageLearnableQuery=None
72
+ self.extractor: QwenImageExtractor=None
73
+ self.rformer: RFormer=None
74
+ self.rformer2d: RFormer2D=None
75
+ self.mae: QwenImageMAE=None
76
+ self.dit: QwenImageDiT = None
77
+ self.vae: QwenImageVAE = None
78
+ self.blockwise_controlnet: QwenImageBlockwiseMultiControlNet = None
79
+ self.tokenizer: Qwen2Tokenizer = None
80
+ self.processor: Qwen2VLProcessor = None
81
+ self.in_iteration_models = ("dit", "blockwise_controlnet")
82
+ self.units = [
83
+ QwenImageUnit_ShapeChecker(),
84
+ QwenImageUnit_NoiseInitializer(),
85
+ QwenImageUnit_InputImageEmbedder(),
86
+ QwenImageUnit_Inpaint(),
87
+ QwenImageUnit_PromptEmbedderWithExtractor(),
88
+ QwenImageUnit_MirrorPPROperationEncoder(),
89
+ QwenImageUnit_MirrorPPROperationEncoder2D(),
90
+ QwenImageUnit_EditImageEmbedder(),
91
+ QwenImageUnit_ContextImageEmbedder(),
92
+ QwenImageUnit_PromptEmbedder(),
93
+ QwenImageUnit_EntityControl(),
94
+ QwenImageUnit_BlockwiseControlNet(),
95
+ ]
96
+ self.model_fn = model_fn_qwen_image
97
+
98
+
99
+ @staticmethod
100
+ def from_pretrained(
101
+ torch_dtype: torch.dtype = torch.bfloat16,
102
+ device: Union[str, torch.device] = "cuda",
103
+ model_configs: list[ModelConfig] = [],
104
+ tokenizer_config: ModelConfig = ModelConfig(model_id="Qwen/Qwen-Image", origin_file_pattern="tokenizer/"),
105
+ processor_config: ModelConfig = None,
106
+ vram_limit: float = None,
107
+ ):
108
+
109
+ pipe = QwenImagePipeline(device=device, torch_dtype=torch_dtype)
110
+ model_pool = pipe.download_and_load_models(model_configs, vram_limit)
111
+
112
+
113
+ pipe.text_encoder = model_pool.fetch_model("qwen_image_text_encoder")
114
+ pipe.dit = model_pool.fetch_model("qwen_image_dit")
115
+ pipe.vae = model_pool.fetch_model("qwen_image_vae")
116
+ pipe.blockwise_controlnet = QwenImageBlockwiseMultiControlNet(model_pool.fetch_model("qwen_image_blockwise_controlnet", index="all"))
117
+ pipe.connector=model_pool.fetch_model("qwen_image_connector")
118
+ pipe.learnable_query=model_pool.fetch_model("qwen_image_learnable_query")
119
+ pipe.extractor=model_pool.fetch_model("qwen_image_extractor")
120
+ pipe.rformer=model_pool.fetch_model("qwen_image_rformer")
121
+ pipe.rformer2d=model_pool.fetch_model("qwen_image_rformer2D")
122
+ pipe.mae=model_pool.fetch_model("qwen_image_mae")
123
+
124
+ if tokenizer_config is not None:
125
+ tokenizer_config.download_if_necessary()
126
+ from transformers import Qwen2Tokenizer
127
+ pipe.tokenizer = Qwen2Tokenizer.from_pretrained(tokenizer_config.path)
128
+ if processor_config is not None:
129
+ processor_config.download_if_necessary()
130
+ from transformers import Qwen2VLProcessor
131
+ pipe.processor = Qwen2VLProcessor.from_pretrained(processor_config.path)
132
+
133
+
134
+ pipe.vram_management_enabled = pipe.check_vram_management_state()
135
+ if pipe.vram_management_enabled:
136
+ print("enable vram management")
137
+ else:
138
+ print("disable vram management")
139
+ return pipe
140
+
141
+
142
+ @torch.no_grad()
143
+ def __call__(
144
+ self,
145
+
146
+ prompt: str,
147
+ negative_prompt: str = "",
148
+ cfg_scale: float = 4.0,
149
+
150
+ input_image: Image.Image = None,
151
+ denoising_strength: float = 1.0,
152
+
153
+ inpaint_mask: Image.Image = None,
154
+ inpaint_blur_size: int = None,
155
+ inpaint_blur_sigma: float = None,
156
+
157
+ height: int = 1328,
158
+ width: int = 1328,
159
+
160
+ seed: int = None,
161
+ rand_device: str = "cpu",
162
+
163
+ num_inference_steps: int = 30,
164
+ exponential_shift_mu: float = None,
165
+
166
+ blockwise_controlnet_inputs: list[ControlNetInput] = None,
167
+
168
+ eligen_entity_prompts: list[str] = None,
169
+ eligen_entity_masks: list[Image.Image] = None,
170
+ eligen_enable_on_negative: bool = False,
171
+
172
+ edit_image: Image.Image = None,
173
+ edit_image_auto_resize: bool = True,
174
+ edit_rope_interpolation: bool = False,
175
+ zero_cond_t: bool = False,
176
+
177
+ example_origin: Image.Image = None,
178
+ example_target: Image.Image = None,
179
+ shape_1=None,
180
+ no_qwenvl=False,
181
+
182
+ context_image: Image.Image = None,
183
+
184
+ tiled: bool = False,
185
+ tile_size: int = 128,
186
+ tile_stride: int = 64,
187
+
188
+ progress_bar_cmd = tqdm,
189
+ ):
190
+
191
+
192
+ self.scheduler.set_timesteps(num_inference_steps, denoising_strength=denoising_strength, dynamic_shift_len=(height // 16) * (width // 16), exponential_shift_mu=exponential_shift_mu)
193
+
194
+
195
+ inputs_posi = {
196
+ "prompt": prompt,
197
+ }
198
+ inputs_nega = {
199
+ "negative_prompt": negative_prompt,
200
+ }
201
+ inputs_shared = {
202
+ "cfg_scale": cfg_scale,
203
+ "input_image": input_image, "denoising_strength": denoising_strength,
204
+ "inpaint_mask": inpaint_mask, "inpaint_blur_size": inpaint_blur_size, "inpaint_blur_sigma": inpaint_blur_sigma,
205
+ "height": height, "width": width,
206
+ "seed": seed, "rand_device": rand_device,
207
+ "num_inference_steps": num_inference_steps,
208
+ "blockwise_controlnet_inputs": blockwise_controlnet_inputs,
209
+ "tiled": tiled, "tile_size": tile_size, "tile_stride": tile_stride,
210
+ "eligen_entity_prompts": eligen_entity_prompts, "eligen_entity_masks": eligen_entity_masks, "eligen_enable_on_negative": eligen_enable_on_negative,
211
+ "edit_image": edit_image, "edit_image_auto_resize": edit_image_auto_resize, "edit_rope_interpolation": edit_rope_interpolation,
212
+ "context_image": context_image,
213
+ "zero_cond_t": zero_cond_t,
214
+ "example_origin": example_origin, "example_target": example_target,"shape_1":shape_1,"no_qwenvl":no_qwenvl
215
+ }
216
+ for unit in self.units:
217
+ inputs_shared, inputs_posi, inputs_nega = self.unit_runner(unit, self, inputs_shared, inputs_posi, inputs_nega)
218
+
219
+
220
+ self.load_models_to_device(self.in_iteration_models)
221
+ models = {name: getattr(self, name) for name in self.in_iteration_models}
222
+ for progress_id, timestep in enumerate(progress_bar_cmd(self.scheduler.timesteps)):
223
+ timestep = timestep.unsqueeze(0).to(dtype=self.torch_dtype, device=self.device)
224
+ noise_pred = self.cfg_guided_model_fn(
225
+ self.model_fn, cfg_scale,
226
+ inputs_shared, inputs_posi, inputs_nega,
227
+ **models, timestep=timestep, progress_id=progress_id
228
+ )
229
+ inputs_shared["latents"] = self.step(self.scheduler, progress_id=progress_id, noise_pred=noise_pred, **inputs_shared)
230
+
231
+
232
+ self.load_models_to_device(['vae'])
233
+ image = self.vae.decode(inputs_shared["latents"], device=self.device, tiled=tiled, tile_size=tile_size, tile_stride=tile_stride)
234
+ image = self.vae_output_to_image(image)
235
+ self.load_models_to_device([])
236
+
237
+ return image
238
+
239
+
240
+
241
+ class QwenImageBlockwiseMultiControlNet(torch.nn.Module):
242
+ def __init__(self, models: list[QwenImageBlockWiseControlNet]):
243
+ super().__init__()
244
+ if not isinstance(models, list):
245
+ models = [models]
246
+ self.models = torch.nn.ModuleList(models)
247
+ for model in models:
248
+ if hasattr(model, "vram_management_enabled") and getattr(model, "vram_management_enabled"):
249
+ self.vram_management_enabled = True
250
+
251
+ def preprocess(self, controlnet_inputs: list[ControlNetInput], conditionings: list[torch.Tensor], **kwargs):
252
+ processed_conditionings = []
253
+ for controlnet_input, conditioning in zip(controlnet_inputs, conditionings):
254
+ conditioning = rearrange(conditioning, "B C (H P) (W Q) -> B (H W) (C P Q)", P=2, Q=2)
255
+ model_output = self.models[controlnet_input.controlnet_id].process_controlnet_conditioning(conditioning)
256
+ processed_conditionings.append(model_output)
257
+ return processed_conditionings
258
+
259
+ def blockwise_forward(self, image, conditionings: list[torch.Tensor], controlnet_inputs: list[ControlNetInput], progress_id, num_inference_steps, block_id, **kwargs):
260
+ res = 0
261
+ for controlnet_input, conditioning in zip(controlnet_inputs, conditionings):
262
+ progress = (num_inference_steps - 1 - progress_id) / max(num_inference_steps - 1, 1)
263
+ if progress > controlnet_input.start + (1e-4) or progress < controlnet_input.end - (1e-4):
264
+ continue
265
+ model_output = self.models[controlnet_input.controlnet_id].blockwise_forward(image, conditioning, block_id)
266
+ res = res + model_output * controlnet_input.scale
267
+ return res
268
+
269
+
270
+ class QwenImageUnit_Inpaint(PipelineUnit):
271
+ def __init__(self):
272
+ super().__init__(
273
+ input_params=("inpaint_mask", "height", "width", "inpaint_blur_size", "inpaint_blur_sigma"),
274
+ output_params=("inpaint_mask",),
275
+ )
276
+
277
+ def process(self, pipe: QwenImagePipeline, inpaint_mask, height, width, inpaint_blur_size, inpaint_blur_sigma):
278
+ if inpaint_mask is None:
279
+ return {}
280
+ inpaint_mask = pipe.preprocess_image(inpaint_mask.convert("RGB").resize((width // 8, height // 8)), min_value=0, max_value=1)
281
+ inpaint_mask = inpaint_mask.mean(dim=1, keepdim=True)
282
+ if inpaint_blur_size is not None and inpaint_blur_sigma is not None:
283
+ from torchvision.transforms import GaussianBlur
284
+ blur = GaussianBlur(kernel_size=inpaint_blur_size * 2 + 1, sigma=inpaint_blur_sigma)
285
+ inpaint_mask = blur(inpaint_mask)
286
+ return {"inpaint_mask": inpaint_mask}
287
+
288
+
289
+ class QwenImageUnit_EntityControl(PipelineUnit):
290
+ def __init__(self):
291
+ super().__init__(
292
+ take_over=True,
293
+ input_params=("eligen_entity_prompts", "width", "height", "eligen_enable_on_negative", "cfg_scale"),
294
+ output_params=("entity_prompt_emb", "entity_masks", "entity_prompt_emb_mask"),
295
+ onload_model_names=("text_encoder",)
296
+ )
297
+
298
+ def extract_masked_hidden(self, hidden_states: torch.Tensor, mask: torch.Tensor):
299
+ bool_mask = mask.bool()
300
+ valid_lengths = bool_mask.sum(dim=1)
301
+ selected = hidden_states[bool_mask]
302
+ split_result = torch.split(selected, valid_lengths.tolist(), dim=0)
303
+ return split_result
304
+
305
+ def get_prompt_emb(self, pipe: QwenImagePipeline, prompt) -> dict:
306
+ if pipe.text_encoder is not None:
307
+ prompt = [prompt]
308
+ template = "<|im_start|>system\nDescribe the image by detailing the color, shape, size, texture, quantity, text, spatial relationships of the objects and background:<|im_end|>\n<|im_start|>user\n{}<|im_end|>\n<|im_start|>assistant\n"
309
+ drop_idx = 34
310
+ txt = [template.format(e) for e in prompt]
311
+ txt_tokens = pipe.tokenizer(txt, max_length=1024+drop_idx, padding=True, truncation=True, return_tensors="pt").to(pipe.device)
312
+ hidden_states = pipe.text_encoder(input_ids=txt_tokens.input_ids, attention_mask=txt_tokens.attention_mask, output_hidden_states=True,)[-1]
313
+
314
+ split_hidden_states = self.extract_masked_hidden(hidden_states, txt_tokens.attention_mask)
315
+ split_hidden_states = [e[drop_idx:] for e in split_hidden_states]
316
+ attn_mask_list = [torch.ones(e.size(0), dtype=torch.long, device=e.device) for e in split_hidden_states]
317
+ max_seq_len = max([e.size(0) for e in split_hidden_states])
318
+ prompt_embeds = torch.stack([torch.cat([u, u.new_zeros(max_seq_len - u.size(0), u.size(1))]) for u in split_hidden_states])
319
+ encoder_attention_mask = torch.stack([torch.cat([u, u.new_zeros(max_seq_len - u.size(0))]) for u in attn_mask_list])
320
+ prompt_embeds = prompt_embeds.to(dtype=pipe.torch_dtype, device=pipe.device)
321
+ return {"prompt_emb": prompt_embeds, "prompt_emb_mask": encoder_attention_mask}
322
+ else:
323
+ return {}
324
+
325
+ def preprocess_masks(self, pipe, masks, height, width, dim):
326
+ out_masks = []
327
+ for mask in masks:
328
+ mask = pipe.preprocess_image(mask.resize((width, height), resample=Image.NEAREST)).mean(dim=1, keepdim=True) > 0
329
+ mask = mask.repeat(1, dim, 1, 1).to(device=pipe.device, dtype=pipe.torch_dtype)
330
+ out_masks.append(mask)
331
+ return out_masks
332
+
333
+ def prepare_entity_inputs(self, pipe, entity_prompts, entity_masks, width, height):
334
+ entity_masks = self.preprocess_masks(pipe, entity_masks, height//8, width//8, 1)
335
+ entity_masks = torch.cat(entity_masks, dim=0).unsqueeze(0)
336
+ prompt_embs, prompt_emb_masks = [], []
337
+ for entity_prompt in entity_prompts:
338
+ prompt_emb_dict = self.get_prompt_emb(pipe, entity_prompt)
339
+ prompt_embs.append(prompt_emb_dict['prompt_emb'])
340
+ prompt_emb_masks.append(prompt_emb_dict['prompt_emb_mask'])
341
+ return prompt_embs, prompt_emb_masks, entity_masks
342
+
343
+ def prepare_eligen(self, pipe, prompt_emb_nega, eligen_entity_prompts, eligen_entity_masks, width, height, enable_eligen_on_negative, cfg_scale):
344
+ entity_prompt_emb_posi, entity_prompt_emb_posi_mask, entity_masks_posi = self.prepare_entity_inputs(pipe, eligen_entity_prompts, eligen_entity_masks, width, height)
345
+ if enable_eligen_on_negative and cfg_scale != 1.0:
346
+ entity_prompt_emb_nega = [prompt_emb_nega['prompt_emb']] * len(entity_prompt_emb_posi)
347
+ entity_prompt_emb_nega_mask = [prompt_emb_nega['prompt_emb_mask']] * len(entity_prompt_emb_posi)
348
+ entity_masks_nega = entity_masks_posi
349
+ else:
350
+ entity_prompt_emb_nega, entity_prompt_emb_nega_mask, entity_masks_nega = None, None, None
351
+ eligen_kwargs_posi = {"entity_prompt_emb": entity_prompt_emb_posi, "entity_masks": entity_masks_posi, "entity_prompt_emb_mask": entity_prompt_emb_posi_mask}
352
+ eligen_kwargs_nega = {"entity_prompt_emb": entity_prompt_emb_nega, "entity_masks": entity_masks_nega, "entity_prompt_emb_mask": entity_prompt_emb_nega_mask}
353
+ return eligen_kwargs_posi, eligen_kwargs_nega
354
+
355
+ def process(self, pipe: QwenImagePipeline, inputs_shared, inputs_posi, inputs_nega):
356
+ eligen_entity_prompts, eligen_entity_masks = inputs_shared.get("eligen_entity_prompts", None), inputs_shared.get("eligen_entity_masks", None)
357
+ if eligen_entity_prompts is None or eligen_entity_masks is None or len(eligen_entity_prompts) == 0 or len(eligen_entity_masks) == 0:
358
+ return inputs_shared, inputs_posi, inputs_nega
359
+ pipe.load_models_to_device(self.onload_model_names)
360
+ eligen_enable_on_negative = inputs_shared.get("eligen_enable_on_negative", False)
361
+ eligen_kwargs_posi, eligen_kwargs_nega = self.prepare_eligen(pipe, inputs_nega,
362
+ eligen_entity_prompts, eligen_entity_masks, inputs_shared["width"], inputs_shared["height"],
363
+ eligen_enable_on_negative, inputs_shared["cfg_scale"])
364
+ inputs_posi.update(eligen_kwargs_posi)
365
+ if inputs_shared.get("cfg_scale", 1.0) != 1.0:
366
+ inputs_nega.update(eligen_kwargs_nega)
367
+ return inputs_shared, inputs_posi, inputs_nega
368
+
369
+
370
+ class QwenImageUnit_BlockwiseControlNet(PipelineUnit):
371
+ def __init__(self):
372
+ super().__init__(
373
+ input_params=("blockwise_controlnet_inputs", "tiled", "tile_size", "tile_stride"),
374
+ output_params=("blockwise_controlnet_conditioning",),
375
+ onload_model_names=("vae",)
376
+ )
377
+
378
+ def apply_controlnet_mask_on_latents(self, pipe, latents, mask):
379
+ mask = (pipe.preprocess_image(mask) + 1) / 2
380
+ mask = mask.mean(dim=1, keepdim=True)
381
+ mask = 1 - torch.nn.functional.interpolate(mask, size=latents.shape[-2:])
382
+ latents = torch.concat([latents, mask], dim=1)
383
+ return latents
384
+
385
+ def apply_controlnet_mask_on_image(self, pipe, image, mask):
386
+ mask = mask.resize(image.size)
387
+ mask = pipe.preprocess_image(mask).mean(dim=[0, 1]).cpu()
388
+ image = np.array(image)
389
+ image[mask > 0] = 0
390
+ image = Image.fromarray(image)
391
+ return image
392
+
393
+ def process(self, pipe: QwenImagePipeline, blockwise_controlnet_inputs: list[ControlNetInput], tiled, tile_size, tile_stride):
394
+ if blockwise_controlnet_inputs is None:
395
+ return {}
396
+ pipe.load_models_to_device(self.onload_model_names)
397
+ conditionings = []
398
+ for controlnet_input in blockwise_controlnet_inputs:
399
+ image = controlnet_input.image
400
+ if controlnet_input.inpaint_mask is not None:
401
+ image = self.apply_controlnet_mask_on_image(pipe, image, controlnet_input.inpaint_mask)
402
+
403
+ image = pipe.preprocess_image(image).to(device=pipe.device, dtype=pipe.torch_dtype)
404
+ image = pipe.vae.encode(image, tiled=tiled, tile_size=tile_size, tile_stride=tile_stride)
405
+
406
+ if controlnet_input.inpaint_mask is not None:
407
+ image = self.apply_controlnet_mask_on_latents(pipe, image, controlnet_input.inpaint_mask)
408
+ conditionings.append(image)
409
+
410
+ return {"blockwise_controlnet_conditioning": conditionings}
411
+
412
+
413
+
414
+ class QwenImageUnit_ContextImageEmbedder(PipelineUnit):
415
+ def __init__(self):
416
+ super().__init__(
417
+ input_params=("context_image", "height", "width", "tiled", "tile_size", "tile_stride"),
418
+ output_params=("context_latents",),
419
+ onload_model_names=("vae",)
420
+ )
421
+
422
+ def process(self, pipe: QwenImagePipeline, context_image, height, width, tiled, tile_size, tile_stride):
423
+ if context_image is None:
424
+ return {}
425
+ pipe.load_models_to_device(self.onload_model_names)
426
+ context_image = pipe.preprocess_image(context_image.resize((width, height))).to(device=pipe.device, dtype=pipe.torch_dtype)
427
+ context_latents = pipe.vae.encode(context_image, tiled=tiled, tile_size=tile_size, tile_stride=tile_stride)
428
+ return {"context_latents": context_latents}
429
+
430
+
431
+
432
+ class QwenImageUnit_ShapeChecker(PipelineUnit):
433
+ def __init__(self):
434
+ super().__init__(
435
+ input_params=("height", "width"),
436
+ output_params=("height", "width"),
437
+ )
438
+
439
+ def process(self, pipe: QwenImagePipeline, height, width):
440
+ height, width = pipe.check_resize_height_width(height, width)
441
+ return {"height": height, "width": width}
442
+
443
+
444
+
445
+ class QwenImageUnit_NoiseInitializer(PipelineUnit):
446
+ def __init__(self):
447
+ super().__init__(
448
+ input_params=("height", "width", "seed", "rand_device"),
449
+ output_params=("noise",),
450
+ )
451
+
452
+ def process(self, pipe: QwenImagePipeline, height, width, seed, rand_device):
453
+ noise = pipe.generate_noise((1, 16, height//8, width//8), seed=seed, rand_device=rand_device, rand_torch_dtype=pipe.torch_dtype)
454
+ return {"noise": noise}
455
+
456
+
457
+
458
+ class QwenImageUnit_InputImageEmbedder(PipelineUnit):
459
+ def __init__(self):
460
+ super().__init__(
461
+ input_params=("input_image", "noise", "tiled", "tile_size", "tile_stride","edit_region","loss_weight_mask"),
462
+ output_params=("latents", "input_latents","loss_weight_mask"),
463
+ onload_model_names=("vae",)
464
+ )
465
+
466
+
467
+
468
+
469
+
470
+
471
+
472
+
473
+
474
+
475
+
476
+
477
+
478
+
479
+ def process(self, pipe: QwenImagePipeline, input_image, noise, tiled, tile_size, tile_stride, edit_region=None,loss_weight_mask=None):
480
+ if input_image is None:
481
+ return {"latents": noise, "input_latents": None, "loss_weight_mask": None}
482
+
483
+
484
+
485
+
486
+
487
+ pipe.load_models_to_device(['vae'])
488
+
489
+
490
+
491
+ image = pipe.preprocess_image(input_image).to(device=pipe.device, dtype=pipe.torch_dtype)
492
+ input_latents = pipe.vae.encode(image, tiled=tiled, tile_size=tile_size, tile_stride=tile_stride)
493
+
494
+
495
+
496
+
497
+
498
+
499
+
500
+
501
+
502
+
503
+
504
+
505
+
506
+
507
+
508
+
509
+
510
+
511
+
512
+
513
+
514
+
515
+
516
+
517
+
518
+
519
+
520
+
521
+
522
+
523
+
524
+
525
+
526
+
527
+
528
+
529
+
530
+
531
+
532
+
533
+
534
+
535
+
536
+
537
+
538
+
539
+
540
+
541
+
542
+
543
+
544
+
545
+
546
+
547
+
548
+
549
+
550
+
551
+
552
+
553
+
554
+
555
+
556
+
557
+
558
+
559
+
560
+
561
+
562
+
563
+ latent_mask = None
564
+ if edit_region is not None:
565
+
566
+ B, C, H_lat, W_lat = input_latents.shape
567
+
568
+
569
+ latent_mask = torch.ones((B, 1, H_lat, W_lat), device=input_latents.device, dtype=input_latents.dtype)
570
+
571
+
572
+ edit_weight = 100.0
573
+
574
+
575
+
576
+ scale_h = H_lat / image.shape[2]
577
+ scale_w = W_lat / image.shape[3]
578
+
579
+
580
+
581
+ for b in range(B):
582
+
583
+ if B == 1:
584
+
585
+ current_region = edit_region
586
+ else:
587
+ try:
588
+ current_region = edit_region[b]
589
+ except:
590
+ current_region = edit_region
591
+
592
+ if not current_region:
593
+ continue
594
+
595
+
596
+ polygons = extract_polygons(current_region)
597
+
598
+ if len(polygons) > 0:
599
+ mask_np = np.zeros((H_lat, W_lat), dtype=np.float32)
600
+
601
+ scaled_polys = []
602
+ for poly in polygons:
603
+ poly_scaled = poly * np.array([scale_w, scale_h], dtype=np.float32)
604
+ scaled_polys.append(poly_scaled.astype(np.int32))
605
+
606
+ cv2.fillPoly(mask_np, scaled_polys, color=1.0)
607
+
608
+ mask_tensor = torch.from_numpy(mask_np).to(latent_mask.device)
609
+
610
+ latent_mask[b, 0] = torch.where(mask_tensor > 0.5, torch.tensor(edit_weight, device=latent_mask.device), latent_mask[b, 0])
611
+
612
+ if loss_weight_mask is not None:
613
+ latent_mask=loss_weight_mask
614
+
615
+
616
+ if pipe.scheduler.training:
617
+
618
+
619
+
620
+
621
+
622
+
623
+
624
+
625
+
626
+
627
+
628
+
629
+
630
+
631
+
632
+ return {
633
+ "latents": noise,
634
+ "input_latents": input_latents,
635
+ "loss_weight_mask": latent_mask
636
+ }
637
+ else:
638
+ latents = pipe.scheduler.add_noise(input_latents, noise, timestep=pipe.scheduler.timesteps[0])
639
+ return {
640
+ "latents": latents,
641
+ "input_latents": input_latents
642
+ }
643
+
644
+
645
+ class QwenImageUnit_PromptEmbedder(PipelineUnit):
646
+ def __init__(self):
647
+ super().__init__(
648
+ seperate_cfg=True,
649
+ input_params_posi={"prompt": "prompt"},
650
+ input_params_nega={"prompt": "negative_prompt"},
651
+ input_params=("edit_image",),
652
+ output_params=("prompt_emb", "prompt_emb_mask"),
653
+ onload_model_names=("text_encoder",)
654
+ )
655
+
656
+ def extract_masked_hidden(self, hidden_states: torch.Tensor, mask: torch.Tensor):
657
+ bool_mask = mask.bool()
658
+ valid_lengths = bool_mask.sum(dim=1)
659
+ selected = hidden_states[bool_mask]
660
+ split_result = torch.split(selected, valid_lengths.tolist(), dim=0)
661
+ return split_result
662
+
663
+ def calculate_dimensions(self, target_area, ratio):
664
+ width = math.sqrt(target_area * ratio)
665
+ height = width / ratio
666
+ width = round(width / 32) * 32
667
+ height = round(height / 32) * 32
668
+ return width, height
669
+
670
+ def resize_image(self, image, target_area=384*384):
671
+ width, height = self.calculate_dimensions(target_area, image.size[0] / image.size[1])
672
+ return image.resize((width, height))
673
+
674
+ def encode_prompt(self, pipe: QwenImagePipeline, prompt):
675
+ template = "<|im_start|>system\nDescribe the image by detailing the color, shape, size, texture, quantity, text, spatial relationships of the objects and background:<|im_end|>\n<|im_start|>user\n{}<|im_end|>\n<|im_start|>assistant\n"
676
+ drop_idx = 34
677
+ txt = [template.format(e) for e in prompt]
678
+ model_inputs = pipe.tokenizer(txt, max_length=4096+drop_idx, padding=True, truncation=True, return_tensors="pt").to(pipe.device)
679
+ if model_inputs.input_ids.shape[1] >= 1024:
680
+ print(f"Warning!!! QwenImage model was trained on prompts up to 512 tokens. Current prompt requires {model_inputs['input_ids'].shape[1] - drop_idx} tokens, which may lead to unpredictable behavior.")
681
+ hidden_states = pipe.text_encoder(input_ids=model_inputs.input_ids, attention_mask=model_inputs.attention_mask, output_hidden_states=True,)[-1]
682
+ split_hidden_states = self.extract_masked_hidden(hidden_states, model_inputs.attention_mask)
683
+ split_hidden_states = [e[drop_idx:] for e in split_hidden_states]
684
+ return split_hidden_states
685
+
686
+ def encode_prompt_edit(self, pipe: QwenImagePipeline, prompt, edit_image):
687
+ template = "<|im_start|>system\nDescribe the key features of the input image (color, shape, size, texture, objects, background), then explain how the user's text instruction should alter or modify the image. Generate a new image that meets the user's requirements while maintaining consistency with the original input where appropriate.<|im_end|>\n<|im_start|>user\n<|vision_start|><|image_pad|><|vision_end|>{}<|im_end|>\n<|im_start|>assistant\n"
688
+ drop_idx = 64
689
+ txt = [template.format(e) for e in prompt]
690
+
691
+
692
+ model_inputs = pipe.processor(text=txt, images=edit_image, padding=True, return_tensors="pt").to(pipe.device)
693
+ hidden_states = pipe.text_encoder(input_ids=model_inputs.input_ids, attention_mask=model_inputs.attention_mask, pixel_values=model_inputs.pixel_values, image_grid_thw=model_inputs.image_grid_thw, output_hidden_states=True,)[-1]
694
+ split_hidden_states = self.extract_masked_hidden(hidden_states, model_inputs.attention_mask)
695
+ split_hidden_states = [e[drop_idx:] for e in split_hidden_states]
696
+ return split_hidden_states
697
+
698
+ def encode_prompt_edit_multi(self, pipe: QwenImagePipeline, prompt, edit_image):
699
+ template = "<|im_start|>system\nDescribe the key features of the input image (color, shape, size, texture, objects, background), then explain how the user's text instruction should alter or modify the image. Generate a new image that meets the user's requirements while maintaining consistency with the original input where appropriate.<|im_end|>\n<|im_start|>user\n{}<|im_end|>\n<|im_start|>assistant\n"
700
+ drop_idx = 64
701
+ img_prompt_template = "Picture {}: <|vision_start|><|image_pad|><|vision_end|>"
702
+ base_img_prompt = "".join([img_prompt_template.format(i + 1) for i in range(len(edit_image))])
703
+ txt = [template.format(base_img_prompt + e) for e in prompt]
704
+ edit_image = [self.resize_image(image) for image in edit_image]
705
+
706
+ model_inputs = pipe.processor(text=txt, images=edit_image, padding=True, return_tensors="pt").to(pipe.device)
707
+ hidden_states = pipe.text_encoder(input_ids=model_inputs.input_ids, attention_mask=model_inputs.attention_mask, pixel_values=model_inputs.pixel_values, image_grid_thw=model_inputs.image_grid_thw, output_hidden_states=True,)[-1]
708
+ split_hidden_states = self.extract_masked_hidden(hidden_states, model_inputs.attention_mask)
709
+ split_hidden_states = [e[drop_idx:] for e in split_hidden_states]
710
+ return split_hidden_states
711
+
712
+ def process(self, pipe: QwenImagePipeline, prompt, edit_image=None) -> dict:
713
+
714
+ pipe.load_models_to_device(self.onload_model_names)
715
+ if pipe.text_encoder is not None:
716
+ prompt = [prompt]
717
+ if edit_image is None:
718
+ split_hidden_states = self.encode_prompt(pipe, prompt)
719
+ elif isinstance(edit_image, Image.Image):
720
+ split_hidden_states = self.encode_prompt_edit(pipe, prompt, edit_image)
721
+ else:
722
+ split_hidden_states = self.encode_prompt_edit_multi(pipe, prompt, edit_image)
723
+ attn_mask_list = [torch.ones(e.size(0), dtype=torch.long, device=e.device) for e in split_hidden_states]
724
+ max_seq_len = max([e.size(0) for e in split_hidden_states])
725
+ prompt_embeds = torch.stack([torch.cat([u, u.new_zeros(max_seq_len - u.size(0), u.size(1))]) for u in split_hidden_states])
726
+ encoder_attention_mask = torch.stack([torch.cat([u, u.new_zeros(max_seq_len - u.size(0))]) for u in attn_mask_list])
727
+ prompt_embeds = prompt_embeds.to(dtype=pipe.torch_dtype, device=pipe.device)
728
+ return {"prompt_emb": prompt_embeds, "prompt_emb_mask": encoder_attention_mask}
729
+ else:
730
+ return {}
731
+
732
+
733
+
734
+
735
+
736
+
737
+
738
+
739
+
740
+
741
+
742
+
743
+
744
+
745
+
746
+
747
+
748
+
749
+
750
+
751
+
752
+
753
+
754
+
755
+
756
+
757
+
758
+
759
+
760
+
761
+
762
+
763
+
764
+
765
+
766
+
767
+
768
+
769
+
770
+
771
+
772
+
773
+
774
+
775
+
776
+
777
+
778
+
779
+
780
+
781
+
782
+
783
+
784
+
785
+
786
+
787
+
788
+
789
+
790
+
791
+
792
+
793
+
794
+
795
+
796
+
797
+
798
+
799
+
800
+
801
+
802
+
803
+
804
+
805
+
806
+
807
+
808
+
809
+
810
+
811
+
812
+
813
+
814
+
815
+
816
+
817
+
818
+
819
+
820
+
821
+
822
+
823
+
824
+
825
+
826
+
827
+
828
+
829
+
830
+
831
+
832
+
833
+
834
+
835
+
836
+
837
+
838
+
839
+
840
+
841
+
842
+
843
+
844
+
845
+
846
+
847
+
848
+
849
+
850
+
851
+
852
+
853
+
854
+
855
+
856
+
857
+
858
+
859
+
860
+
861
+
862
+
863
+ class QwenImageUnit_EditImageEmbedder(PipelineUnit):
864
+ def __init__(self):
865
+ super().__init__(
866
+ input_params=("edit_image", "tiled", "tile_size", "tile_stride", "edit_image_auto_resize"),
867
+ output_params=("edit_latents", "edit_image"),
868
+ onload_model_names=("vae",)
869
+ )
870
+
871
+
872
+ def calculate_dimensions(self, target_area, ratio):
873
+ import math
874
+ width = math.sqrt(target_area * ratio)
875
+ height = width / ratio
876
+ width = round(width / 32) * 32
877
+ height = round(height / 32) * 32
878
+
879
+
880
+ return width, height
881
+
882
+
883
+ def edit_image_auto_resize(self, edit_image):
884
+ calculated_width, calculated_height = self.calculate_dimensions(1024 * 1024, edit_image.size[0] / edit_image.size[1])
885
+ return edit_image.resize((calculated_width, calculated_height))
886
+
887
+
888
+ def process(self, pipe: QwenImagePipeline, edit_image, tiled, tile_size, tile_stride, edit_image_auto_resize=False):
889
+ if edit_image is None:
890
+ return {}
891
+ if pipe.extractor is not None:
892
+
893
+ return {}
894
+
895
+
896
+
897
+
898
+
899
+
900
+
901
+
902
+
903
+
904
+ pipe.load_models_to_device(self.onload_model_names)
905
+ if isinstance(edit_image, Image.Image):
906
+ resized_edit_image = self.edit_image_auto_resize(edit_image) if edit_image_auto_resize else edit_image
907
+ edit_image = pipe.preprocess_image(resized_edit_image).to(device=pipe.device, dtype=pipe.torch_dtype)
908
+ edit_latents = pipe.vae.encode(edit_image, tiled=tiled, tile_size=tile_size, tile_stride=tile_stride)
909
+ else:
910
+ resized_edit_image, edit_latents = [], []
911
+ for image in edit_image:
912
+ if edit_image_auto_resize:
913
+ image = self.edit_image_auto_resize(image)
914
+ resized_edit_image.append(image)
915
+
916
+ image=resized_edit_image[0]
917
+ image = pipe.preprocess_image(image).to(device=pipe.device, dtype=pipe.torch_dtype)
918
+ latents = pipe.vae.encode(image, tiled=tiled, tile_size=tile_size, tile_stride=tile_stride)
919
+ edit_latents.append(latents)
920
+
921
+
922
+ return {"edit_latents": edit_latents, "edit_image": resized_edit_image}
923
+
924
+
925
+ class QwenImageUnit_MirrorPPROperationEncoder(PipelineUnit):
926
+ def __init__(self):
927
+ super().__init__(
928
+ input_params=("example_origin","example_target"),
929
+ output_params=("operation_emb"),
930
+ onload_model_names=("mae","rformer","connector")
931
+ )
932
+
933
+ def process_image_for_tokenizer(self,image_input, target_size=(1024, 1024)):
934
+ """
935
+ Args:
936
+ image_input: 单个 PIL.Image 对象,或者 PIL.Image 对象的列表。
937
+ target_size: 目标尺寸 (height, width),默认 (1024, 1024)。
938
+ Returns:
939
+ torch.Tensor:
940
+ - 如果输入是单个图片: 返回 (3, H, W)
941
+ - 如果输入是列表: 返回 (B, 3, H, W)
942
+ - 数据类型 float32,已归一化。
943
+ """
944
+ mean = torch.tensor([0.485, 0.456, 0.406]).view(3, 1, 1)
945
+ std = torch.tensor([0.229, 0.224, 0.225]).view(3, 1, 1)
946
+
947
+ transforms = v2.Compose([
948
+ v2.ToImage(),
949
+ v2.ToDtype(torch.float32, scale=True),
950
+ v2.Resize(target_size, interpolation=InterpolationMode.BICUBIC, antialias=True),
951
+ ])
952
+
953
+
954
+ def _process_one_tensor(img):
955
+
956
+ x = transforms(img)
957
+
958
+
959
+ if mean.device != x.device:
960
+ mean_dev = mean.to(x.device)
961
+ std_dev = std.to(x.device)
962
+ else:
963
+ mean_dev, std_dev = mean, std
964
+
965
+
966
+ x = (x - mean_dev) / (std_dev + 1e-6)
967
+ return x
968
+
969
+ if isinstance(image_input, list):
970
+ tensors = [_process_one_tensor(img) for img in image_input]
971
+ return torch.stack(tensors)
972
+ else:
973
+
974
+ return _process_one_tensor(image_input).unsqueeze(0)
975
+
976
+
977
+ def process(self, pipe: QwenImagePipeline,example_origin,example_target):
978
+ if pipe.rformer2d is not None:
979
+ return {}
980
+ if pipe.extractor is not None:
981
+ return {}
982
+
983
+ pipe.load_models_to_device(self.onload_model_names)
984
+
985
+
986
+
987
+
988
+
989
+ example_origin_pixel_values = self.process_image_for_tokenizer(example_origin).to(device=pipe.device, dtype=pipe.torch_dtype)
990
+ example_target_pixel_values = self.process_image_for_tokenizer(example_target).to(device=pipe.device, dtype=pipe.torch_dtype)
991
+ example_origin_hidden_states=pipe.mae(example_origin_pixel_values)
992
+ example_target_hidden_states=pipe.mae(example_target_pixel_values)
993
+ query_num = pipe.rformer.query_num
994
+ latent_motion_tokens = pipe.rformer(
995
+ cond_hidden_states=example_origin_hidden_states,
996
+ target_hidden_states=example_target_hidden_states).last_hidden_state[:, :query_num]
997
+
998
+ for layer in pipe.connector.encoder.layers:
999
+ latent_motion_tokens=gradient_checkpoint_forward(layer,True,False,latent_motion_tokens)
1000
+ operation_emb=pipe.connector.proj(latent_motion_tokens)
1001
+
1002
+
1003
+ return {"operation_emb": operation_emb}
1004
+
1005
+
1006
+
1007
+ class QwenImageUnit_MirrorPPROperationEncoder2D(PipelineUnit):
1008
+ def __init__(self):
1009
+ super().__init__(
1010
+ input_params=("example_origin","example_target","shape_1"),
1011
+ output_params=("operation_emb"),
1012
+ onload_model_names=("mae","rformer2d","connector")
1013
+ )
1014
+
1015
+ def process_image_for_tokenizer(self,image_input):
1016
+ """
1017
+ Args:
1018
+ image_input: 单个 PIL.Image 对象,或者 PIL.Image 对象的列表。
1019
+ Returns:
1020
+ torch.Tensor:
1021
+ - 如果输入是单个图片: 返回 (3, H, W)
1022
+ - 如果输入是列表: 返回 (B, 3, H, W)
1023
+ - 数据类型 float32,已归一化。
1024
+ """
1025
+ mean = torch.tensor([0.485, 0.456, 0.406]).view(3, 1, 1)
1026
+ std = torch.tensor([0.229, 0.224, 0.225]).view(3, 1, 1)
1027
+
1028
+ transforms = v2.Compose([
1029
+ v2.ToImage(),
1030
+ v2.ToDtype(torch.float32, scale=True),
1031
+ ])
1032
+
1033
+
1034
+ def _process_one_tensor(img):
1035
+
1036
+ x = transforms(img)
1037
+
1038
+
1039
+ if mean.device != x.device:
1040
+ mean_dev = mean.to(x.device)
1041
+ std_dev = std.to(x.device)
1042
+ else:
1043
+ mean_dev, std_dev = mean, std
1044
+
1045
+
1046
+ x = (x - mean_dev) / (std_dev + 1e-6)
1047
+ return x
1048
+
1049
+ if isinstance(image_input, list):
1050
+ tensors = [_process_one_tensor(img) for img in image_input]
1051
+ return torch.stack(tensors)
1052
+ else:
1053
+
1054
+ return _process_one_tensor(image_input).unsqueeze(0)
1055
+
1056
+
1057
+ def process(self, pipe: QwenImagePipeline,example_origin,example_target,shape_1):
1058
+ if pipe.rformer is not None:
1059
+ return {}
1060
+ if pipe.extractor is not None:
1061
+ return {}
1062
+
1063
+
1064
+ h1,w1=shape_1
1065
+ new_shape_1 = [torch.tensor([h1]), torch.tensor([w1])]
1066
+ pipe.load_models_to_device(self.onload_model_names)
1067
+
1068
+
1069
+
1070
+
1071
+
1072
+
1073
+
1074
+ example_origin_pixel_values = self.process_image_for_tokenizer(example_origin).to(device=pipe.device, dtype=pipe.torch_dtype)
1075
+ example_target_pixel_values = self.process_image_for_tokenizer(example_target).to(device=pipe.device, dtype=pipe.torch_dtype)
1076
+ example_origin_hidden_states=pipe.mae(example_origin_pixel_values)
1077
+ example_target_hidden_states=pipe.mae(example_target_pixel_values)
1078
+ query_num = pipe.rformer2d.query_num
1079
+ latent_motion_tokens = pipe.rformer2d(
1080
+ cond_hidden_states=example_origin_hidden_states,
1081
+ target_hidden_states=example_target_hidden_states,sample1_shapes=new_shape_1).last_hidden_state[:, :query_num]
1082
+
1083
+ for layer in pipe.connector.encoder.layers:
1084
+ latent_motion_tokens=gradient_checkpoint_forward(layer,True,False,latent_motion_tokens)
1085
+ operation_emb=pipe.connector.proj(latent_motion_tokens)
1086
+
1087
+
1088
+ return {"operation_emb": operation_emb}
1089
+
1090
+
1091
+ class QwenImageUnit_PromptEmbedderWithExtractor(PipelineUnit):
1092
+ def __init__(self):
1093
+ super().__init__(
1094
+ input_params=("prompt", "edit_image", "tiled", "tile_size", "tile_stride", "edit_image_auto_resize"),
1095
+ output_params=("prompt_emb", "prompt_emb_mask", "image_vl_emb","edit_latents","edit_image"),
1096
+ onload_model_names=("vae", "extractor", "text_encoder")
1097
+ )
1098
+
1099
+ def extract_masked_hidden(self, hidden_states: torch.Tensor, mask: torch.Tensor):
1100
+ bool_mask = mask.bool()
1101
+ valid_lengths = bool_mask.sum(dim=1)
1102
+ selected = hidden_states[bool_mask]
1103
+ split_result = torch.split(selected, valid_lengths.tolist(), dim=0)
1104
+ return split_result
1105
+
1106
+ def calculate_dimensions(self, target_area, ratio):
1107
+ width = math.sqrt(target_area * ratio)
1108
+ height = width / ratio
1109
+ width = round(width / 32) * 32
1110
+ height = round(height / 32) * 32
1111
+ return width, height
1112
+
1113
+ def edit_image_auto_resize(self, edit_image):
1114
+ calculated_width, calculated_height = self.calculate_dimensions(1024 * 1024, edit_image.size[0] / edit_image.size[1])
1115
+ return edit_image.resize((calculated_width, calculated_height))
1116
+
1117
+ def encode_image_vl_emb(self, pipe: QwenImagePipeline, prompt, edit_image):
1118
+ template = "<|im_start|>system\nDescribe the key features of the input image (color, shape, size, texture, objects, background), then explain how the user's text instruction should alter or modify the image. Generate a new image that meets the user's requirements while maintaining consistency with the original input where appropriate.<|im_end|>\n<|im_start|>user\n<|vision_start|><|image_pad|><|vision_end|>{}<|im_end|>\n<|im_start|>assistant\n"
1119
+ drop_idx = 64
1120
+ txt = [template.format(e) for e in prompt]
1121
+ model_inputs = pipe.processor(text=txt, images=edit_image, padding=True, return_tensors="pt").to(pipe.device)
1122
+ hidden_states = pipe.text_encoder(input_ids=model_inputs.input_ids, attention_mask=model_inputs.attention_mask, pixel_values=model_inputs.pixel_values, image_grid_thw=model_inputs.image_grid_thw, output_hidden_states=True,)[-1]
1123
+ split_hidden_states = self.extract_masked_hidden(hidden_states, model_inputs.attention_mask)
1124
+ split_hidden_states = [e[drop_idx:] for e in split_hidden_states]
1125
+ return split_hidden_states
1126
+
1127
+
1128
+ def encode_prompt_edit(self, pipe: QwenImagePipeline, prompt, edit_image,extractor_emb):
1129
+ template = "<|im_start|>system\nDescribe the key features of the input image (color, shape, size, texture, objects, background), then explain how the user's text instruction should alter or modify the image. Generate a new image that meets the user's requirements while maintaining consistency with the original input where appropriate.<|im_end|>\n<|im_start|>user\n<|vision_start|><|image_pad|><|vision_end|>{}<|im_end|>\n<|im_start|>assistant\n"
1130
+ drop_idx = 64
1131
+ txt = [template.format(e) for e in prompt]
1132
+ model_inputs = pipe.processor(text=txt, images=edit_image, padding=True, return_tensors="pt").to(pipe.device)
1133
+
1134
+
1135
+ inputs_embeds,position_ids=pipe.text_encoder.vision_forward(input_ids=model_inputs.input_ids, attention_mask=model_inputs.attention_mask, pixel_values=model_inputs.pixel_values, image_grid_thw=model_inputs.image_grid_thw)
1136
+ batch_size = inputs_embeds.shape[0]
1137
+ num_queries = extractor_emb.shape[1]
1138
+ inputs_embeds = torch.cat([inputs_embeds, extractor_emb], dim=1)
1139
+
1140
+ max_pos = position_ids.max(dim=-1, keepdim=True).values
1141
+ query_pos = torch.arange(1, num_queries + 1, device=position_ids.device).view(1, 1, -1).expand(3, batch_size, -1)
1142
+ position_ids = torch.cat([position_ids, max_pos + query_pos], dim=-1)
1143
+ attention_mask = torch.cat([model_inputs.attention_mask, torch.ones((batch_size, num_queries), device=model_inputs.attention_mask.device, dtype=model_inputs.attention_mask.dtype)], dim=1)
1144
+ hidden_states=pipe.text_encoder.llm_forward(inputs_embeds=inputs_embeds,position_ids=position_ids,attention_mask=attention_mask,output_hidden_states=True)[-1]
1145
+
1146
+ return [h[-num_queries:] for h in hidden_states]
1147
+
1148
+
1149
+ def process(self, pipe: QwenImagePipeline, prompt, edit_image, tiled, tile_size, tile_stride, edit_image_auto_resize=False):
1150
+ if pipe.extractor is None:
1151
+
1152
+ return {}
1153
+
1154
+ print("using QwenImageUnit_PromptEmbedderWithExtractor")
1155
+
1156
+ pipe.load_models_to_device(self.onload_model_names)
1157
+
1158
+
1159
+ if isinstance(edit_image, list):
1160
+ edit_images_list = edit_image
1161
+ else:
1162
+ edit_images_list = [edit_image]
1163
+
1164
+ latents_list = []
1165
+ processed_images = []
1166
+
1167
+ for img in edit_images_list:
1168
+ if edit_image_auto_resize:
1169
+ img = self.edit_image_auto_resize(img)
1170
+ processed_images.append(img)
1171
+
1172
+
1173
+ img_tensor = pipe.preprocess_image(img).to(device=pipe.device, dtype=pipe.torch_dtype)
1174
+ latent = pipe.vae.encode(img_tensor, tiled=tiled, tile_size=tile_size, tile_stride=tile_stride)
1175
+ latents_list.append(latent)
1176
+
1177
+
1178
+
1179
+
1180
+ latents_stacked = torch.stack(latents_list, dim=1)
1181
+
1182
+
1183
+
1184
+
1185
+ extractor_emb = pipe.extractor(latents_stacked)
1186
+
1187
+
1188
+
1189
+
1190
+
1191
+ prompt1=[""]
1192
+ split_hidden_states = self.encode_prompt_edit(pipe, prompt1, edit_image[0],extractor_emb)
1193
+ prompt2=["Generate the retouched version of the input image."]
1194
+ image_split_hidden_states=self.encode_image_vl_emb(pipe, prompt2, edit_image[0])
1195
+
1196
+
1197
+
1198
+
1199
+ attn_mask_list = [torch.ones(e.size(0), dtype=torch.long, device=e.device) for e in split_hidden_states]
1200
+ max_seq_len = max([e.size(0) for e in split_hidden_states])
1201
+ prompt_embeds = torch.stack([torch.cat([u, u.new_zeros(max_seq_len - u.size(0), u.size(1))]) for u in split_hidden_states])
1202
+ encoder_attention_mask = torch.stack([torch.cat([u, u.new_zeros(max_seq_len - u.size(0))]) for u in attn_mask_list])
1203
+ prompt_embeds = prompt_embeds.to(dtype=pipe.torch_dtype, device=pipe.device)
1204
+
1205
+
1206
+ image_attn_mask_list = [torch.ones(e.size(0), dtype=torch.long, device=e.device) for e in image_split_hidden_states]
1207
+ image_max_seq_len = max([e.size(0) for e in image_split_hidden_states])
1208
+ image_embeds = torch.stack([torch.cat([u, u.new_zeros(image_max_seq_len - u.size(0), u.size(1))]) for u in image_split_hidden_states])
1209
+ image_encoder_attention_mask = torch.stack([torch.cat([u, u.new_zeros(image_max_seq_len - u.size(0))]) for u in image_attn_mask_list])
1210
+ image_embeds = image_embeds.to(dtype=pipe.torch_dtype, device=pipe.device)
1211
+
1212
+ combined_attention_mask = torch.cat([image_encoder_attention_mask,encoder_attention_mask], dim=1)
1213
+ edit_latents=[latents_list[0]]
1214
+ return {"prompt_emb": prompt_embeds, "prompt_emb_mask": combined_attention_mask,"image_vl_emb":image_embeds,"edit_latents": edit_latents, "edit_image": processed_images}
1215
+
1216
+
1217
+
1218
+
1219
+ def model_fn_qwen_image(
1220
+ dit: QwenImageDiT = None,
1221
+ blockwise_controlnet: QwenImageBlockwiseMultiControlNet = None,
1222
+
1223
+ latents=None,
1224
+ timestep=None,
1225
+ prompt_emb=None,
1226
+ prompt_emb_mask=None,
1227
+
1228
+ operation_emb=None,
1229
+ height=None,
1230
+ width=None,
1231
+ blockwise_controlnet_conditioning=None,
1232
+ blockwise_controlnet_inputs=None,
1233
+ progress_id=0,
1234
+ num_inference_steps=1,
1235
+ entity_prompt_emb=None,
1236
+ entity_prompt_emb_mask=None,
1237
+ entity_masks=None,
1238
+ edit_latents=None,
1239
+ context_latents=None,
1240
+ enable_fp8_attention=False,
1241
+ use_gradient_checkpointing=False,
1242
+ use_gradient_checkpointing_offload=False,
1243
+ edit_rope_interpolation=False,
1244
+ zero_cond_t=False,
1245
+ output_connector_embeds=False,
1246
+ no_qwenvl=False,
1247
+ **kwargs
1248
+ ):
1249
+
1250
+
1251
+
1252
+
1253
+
1254
+
1255
+ img_shapes = [(latents.shape[0], latents.shape[2]//2, latents.shape[3]//2)]
1256
+
1257
+ timestep = timestep / 1000
1258
+
1259
+ image = rearrange(latents, "B C (H P) (W Q) -> B (H W) (C P Q)", H=height//16, W=width//16, P=2, Q=2)
1260
+ image_seq_len = image.shape[1]
1261
+
1262
+
1263
+
1264
+ if context_latents is not None:
1265
+ img_shapes += [(context_latents.shape[0], context_latents.shape[2]//2, context_latents.shape[3]//2)]
1266
+ context_image = rearrange(context_latents, "B C (H P) (W Q) -> B (H W) (C P Q)", H=context_latents.shape[2]//2, W=context_latents.shape[3]//2, P=2, Q=2)
1267
+ image = torch.cat([image, context_image], dim=1)
1268
+ if edit_latents is not None:
1269
+ edit_latents_list = edit_latents if isinstance(edit_latents, list) else [edit_latents]
1270
+ img_shapes += [(e.shape[0], e.shape[2]//2, e.shape[3]//2) for e in edit_latents_list]
1271
+ edit_image = [rearrange(e, "B C (H P) (W Q) -> B (H W) (C P Q)", H=e.shape[2]//2, W=e.shape[3]//2, P=2, Q=2) for e in edit_latents_list]
1272
+
1273
+ image = torch.cat([image] + edit_image, dim=1)
1274
+
1275
+
1276
+
1277
+
1278
+ image = dit.img_in(image)
1279
+ if zero_cond_t:
1280
+ timestep = torch.cat([timestep, timestep * 0], dim=0)
1281
+ modulate_index = torch.tensor(
1282
+ [[0] * prod(sample[0]) + [1] * sum([prod(s) for s in sample[1:]]) for sample in [img_shapes]],
1283
+ device=timestep.device,
1284
+ dtype=torch.int,
1285
+ )
1286
+ else:
1287
+ modulate_index = None
1288
+ conditioning = dit.time_text_embed(timestep, image.dtype)
1289
+
1290
+
1291
+
1292
+
1293
+
1294
+
1295
+
1296
+
1297
+
1298
+
1299
+ if prompt_emb is not None and prompt_emb_mask is not None and not no_qwenvl:
1300
+ final_prompt_emb = torch.cat([prompt_emb,operation_emb], dim=1)
1301
+ txt_seq_lens = prompt_emb_mask.sum(dim=1).tolist()
1302
+ txt_seq_lens = [x + operation_emb.shape[1] for x in txt_seq_lens]
1303
+ else:
1304
+
1305
+
1306
+ final_prompt_emb = operation_emb
1307
+ txt_seq_lens = [operation_emb.shape[1]] * operation_emb.shape[0]
1308
+
1309
+
1310
+
1311
+
1312
+
1313
+
1314
+
1315
+
1316
+
1317
+
1318
+ if entity_prompt_emb is not None:
1319
+
1320
+ text, image_rotary_emb, attention_mask = dit.process_entity_masks(
1321
+ latents, final_prompt_emb, prompt_emb_mask, entity_prompt_emb, entity_prompt_emb_mask,
1322
+ entity_masks, height, width, image, img_shapes,
1323
+ )
1324
+ else:
1325
+
1326
+ text = dit.txt_in(dit.txt_norm(final_prompt_emb))
1327
+ if edit_rope_interpolation:
1328
+ image_rotary_emb = dit.pos_embed.forward_sampling(img_shapes, txt_seq_lens, device=latents.device)
1329
+ else:
1330
+ image_rotary_emb = dit.pos_embed(img_shapes, txt_seq_lens, device=latents.device)
1331
+ attention_mask = None
1332
+
1333
+ if blockwise_controlnet_conditioning is not None:
1334
+ blockwise_controlnet_conditioning = blockwise_controlnet.preprocess(
1335
+ blockwise_controlnet_inputs, blockwise_controlnet_conditioning)
1336
+
1337
+ for block_id, block in enumerate(dit.transformer_blocks):
1338
+ text, image = gradient_checkpoint_forward(
1339
+ block,
1340
+ use_gradient_checkpointing,
1341
+ use_gradient_checkpointing_offload,
1342
+ image=image,
1343
+ text=text,
1344
+ temb=conditioning,
1345
+ image_rotary_emb=image_rotary_emb,
1346
+ attention_mask=attention_mask,
1347
+ enable_fp8_attention=enable_fp8_attention,
1348
+ modulate_index=modulate_index,
1349
+ )
1350
+ if blockwise_controlnet_conditioning is not None:
1351
+ image_slice = image[:, :image_seq_len].clone()
1352
+ controlnet_output = blockwise_controlnet.blockwise_forward(
1353
+ image=image_slice, conditionings=blockwise_controlnet_conditioning,
1354
+ controlnet_inputs=blockwise_controlnet_inputs, block_id=block_id,
1355
+ progress_id=progress_id, num_inference_steps=num_inference_steps,
1356
+ )
1357
+ image[:, :image_seq_len] = image_slice + controlnet_output
1358
+
1359
+ if zero_cond_t:
1360
+ conditioning = conditioning.chunk(2, dim=0)[0]
1361
+
1362
+ image = dit.norm_out(image, conditioning)
1363
+ image = dit.proj_out(image)
1364
+ image = image[:, :image_seq_len]
1365
+
1366
+ latents = rearrange(image, "B (H W) (C P Q) -> B C (H P) (W Q)", H=height//16, W=width//16, P=2, Q=2)
1367
+
1368
+
1369
+ if output_connector_embeds:
1370
+ return latents,prompt_emb
1371
+ else:
1372
+ return latents
1373
+
1374
+
diffsynth/utils/__init__.py ADDED
File without changes
diffsynth/utils/controlnet/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .controlnet_input import ControlNetInput
diffsynth/utils/controlnet/controlnet_input.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass
2
+ from PIL import Image
3
+
4
+
5
+ @dataclass
6
+ class ControlNetInput:
7
+ controlnet_id: int = 0
8
+ scale: float = 1.0
9
+ start: float = 1.0
10
+ end: float = 0.0
11
+ image: Image.Image = None
12
+ inpaint_mask: Image.Image = None
13
+ processor_id: str = None
diffsynth/utils/lora/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .general import GeneralLoRALoader