multimodalart HF Staff commited on
Commit
40bccc7
·
verified ·
1 Parent(s): 66f36d3

Remove stale files from earlier run (unused backbones/assets)

Browse files
assets/cup.png DELETED

Git LFS Details

  • SHA256: 768162e4455a742ba0a4cbff756ecc5d8817ef7530353166b7a6f09bbc66c120
  • Pointer size: 131 Bytes
  • Size of remote file: 558 kB
assets/keyboard.png DELETED

Git LFS Details

  • SHA256: 4ceb2cc760df8d050eb9c36dbaeae8211979a2536cb541873880fee9a4f34733
  • Pointer size: 131 Bytes
  • Size of remote file: 505 kB
assets/slides.png DELETED

Git LFS Details

  • SHA256: 4814bf7c1a83517483eb2467c844886f67f182800dfd8190dea413ca993214b3
  • Pointer size: 131 Bytes
  • Size of remote file: 700 kB
assets/sunglasses.png DELETED

Git LFS Details

  • SHA256: 659e149486273f1d62ed378dc25fa6331fe98976bd8d5cd4496014e89173494d
  • Pointer size: 131 Bytes
  • Size of remote file: 547 kB
replan/pipelines/flux_kontext.py DELETED
@@ -1,1510 +0,0 @@
1
- # Copyright 2025 Black Forest Labs and The HuggingFace Team. All rights reserved.
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- import inspect
16
- from typing import Any, Callable, Dict, List, Optional, Union
17
-
18
- import numpy as np
19
- import math
20
- import PIL
21
- from PIL import Image
22
- import PIL.Image
23
- import torch
24
- from transformers import (
25
- CLIPImageProcessor,
26
- CLIPTextModel,
27
- CLIPTokenizer,
28
- CLIPVisionModelWithProjection,
29
- T5EncoderModel,
30
- T5TokenizerFast,
31
- )
32
-
33
- from diffusers.image_processor import PipelineImageInput, VaeImageProcessor
34
- from diffusers.loaders import FluxIPAdapterMixin, FluxLoraLoaderMixin, FromSingleFileMixin, TextualInversionLoaderMixin
35
- from diffusers.models import AutoencoderKL
36
- from diffusers.pipelines import FluxKontextPipeline
37
- from diffusers.models.transformers import FluxTransformer2DModel
38
- from diffusers.schedulers import FlowMatchEulerDiscreteScheduler
39
- from diffusers.utils import (
40
- USE_PEFT_BACKEND,
41
- is_torch_xla_available,
42
- logging,
43
- replace_example_docstring,
44
- scale_lora_layers,
45
- unscale_lora_layers,
46
- )
47
- from diffusers.utils.torch_utils import randn_tensor
48
- from diffusers.pipelines.pipeline_utils import DiffusionPipeline
49
- from diffusers.pipelines.flux.pipeline_flux_kontext import FluxPipelineOutput
50
- from diffusers.models.transformers.transformer_flux import FluxAttnProcessor
51
-
52
- from replan.pipelines.flex_attn import prepare_flex_attention_inputs, FluxFlexAttentionProcessor, create_flex_block_mask, create_score_mod
53
- from replan.pipelines.replan import generate_default_attention_rules
54
-
55
- if is_torch_xla_available():
56
- import torch_xla.core.xla_model as xm
57
-
58
- XLA_AVAILABLE = True
59
- else:
60
- XLA_AVAILABLE = False
61
-
62
-
63
- logger = logging.get_logger(__name__) # pylint: disable=invalid-name
64
-
65
- EXAMPLE_DOC_STRING = """
66
- Examples:
67
- ```py
68
- >>> import torch
69
- >>> from diffusers import FluxKontextPipeline
70
- >>> from diffusers.utils import load_image
71
-
72
- >>> pipe = FluxKontextPipeline.from_pretrained(
73
- ... "black-forest-labs/FLUX.1-Kontext-dev", torch_dtype=torch.bfloat16
74
- ... )
75
- >>> pipe.to("cuda")
76
-
77
- >>> image = load_image(
78
- ... "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/yarn-art-pikachu.png"
79
- ... ).convert("RGB")
80
- >>> prompt = "Make Pikachu hold a sign that says 'Black Forest Labs is awesome', yarn art style, detailed, vibrant colors"
81
- >>> image = pipe(
82
- ... image=image,
83
- ... prompt=prompt,
84
- ... guidance_scale=2.5,
85
- ... generator=torch.Generator().manual_seed(42),
86
- ... ).images[0]
87
- >>> image.save("output.png")
88
- ```
89
- """
90
-
91
- PREFERRED_KONTEXT_RESOLUTIONS = [
92
- (672, 1568),
93
- (688, 1504),
94
- (720, 1456),
95
- (752, 1392),
96
- (800, 1328),
97
- (832, 1248),
98
- (880, 1184),
99
- (944, 1104),
100
- (1024, 1024),
101
- (1104, 944),
102
- (1184, 880),
103
- (1248, 832),
104
- (1328, 800),
105
- (1392, 752),
106
- (1456, 720),
107
- (1504, 688),
108
- (1568, 672),
109
- ]
110
-
111
-
112
- def calculate_shift(
113
- image_seq_len,
114
- base_seq_len: int = 256,
115
- max_seq_len: int = 4096,
116
- base_shift: float = 0.5,
117
- max_shift: float = 1.15,
118
- ):
119
- m = (max_shift - base_shift) / (max_seq_len - base_seq_len)
120
- b = base_shift - m * base_seq_len
121
- mu = image_seq_len * m + b
122
- return mu
123
-
124
-
125
- # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
126
- def retrieve_timesteps(
127
- scheduler,
128
- num_inference_steps: Optional[int] = None,
129
- device: Optional[Union[str, torch.device]] = None,
130
- timesteps: Optional[List[int]] = None,
131
- sigmas: Optional[List[float]] = None,
132
- **kwargs,
133
- ):
134
- r"""
135
- Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
136
- custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
137
-
138
- Args:
139
- scheduler (`SchedulerMixin`):
140
- The scheduler to get timesteps from.
141
- num_inference_steps (`int`):
142
- The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
143
- must be `None`.
144
- device (`str` or `torch.device`, *optional*):
145
- The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
146
- timesteps (`List[int]`, *optional*):
147
- Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
148
- `num_inference_steps` and `sigmas` must be `None`.
149
- sigmas (`List[float]`, *optional*):
150
- Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
151
- `num_inference_steps` and `timesteps` must be `None`.
152
-
153
- Returns:
154
- `Tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
155
- second element is the number of inference steps.
156
- """
157
- if timesteps is not None and sigmas is not None:
158
- raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
159
- if timesteps is not None:
160
- accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
161
- if not accepts_timesteps:
162
- raise ValueError(
163
- f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
164
- f" timestep schedules. Please check whether you are using the correct scheduler."
165
- )
166
- scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
167
- timesteps = scheduler.timesteps
168
- num_inference_steps = len(timesteps)
169
- elif sigmas is not None:
170
- accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
171
- if not accept_sigmas:
172
- raise ValueError(
173
- f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
174
- f" sigmas schedules. Please check whether you are using the correct scheduler."
175
- )
176
- scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
177
- timesteps = scheduler.timesteps
178
- num_inference_steps = len(timesteps)
179
- else:
180
- scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
181
- timesteps = scheduler.timesteps
182
- num_inference_steps = len(timesteps)
183
- return timesteps, num_inference_steps
184
-
185
-
186
- # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img.retrieve_latents
187
- def retrieve_latents(
188
- encoder_output: torch.Tensor, generator: Optional[torch.Generator] = None, sample_mode: str = "sample"
189
- ):
190
- if hasattr(encoder_output, "latent_dist") and sample_mode == "sample":
191
- return encoder_output.latent_dist.sample(generator)
192
- elif hasattr(encoder_output, "latent_dist") and sample_mode == "argmax":
193
- return encoder_output.latent_dist.mode()
194
- elif hasattr(encoder_output, "latents"):
195
- return encoder_output.latents
196
- else:
197
- raise AttributeError("Could not access latents of provided encoder_output")
198
-
199
-
200
- class MultiRegionFluxKontextPipeline(
201
- DiffusionPipeline,
202
- FluxLoraLoaderMixin,
203
- FromSingleFileMixin,
204
- TextualInversionLoaderMixin,
205
- FluxIPAdapterMixin,
206
- ):
207
- r"""
208
- The Flux Kontext pipeline for image-to-image and text-to-image generation.
209
-
210
- Reference: https://bfl.ai/announcements/flux-1-kontext-dev
211
-
212
- Args:
213
- transformer ([`FluxTransformer2DModel`]):
214
- Conditional Transformer (MMDiT) architecture to denoise the encoded image latents.
215
- scheduler ([`FlowMatchEulerDiscreteScheduler`]):
216
- A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
217
- vae ([`AutoencoderKL`]):
218
- Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representations.
219
- text_encoder ([`CLIPTextModel`]):
220
- [CLIP](https://huggingface.co/docs/transformers/model_doc/clip#transformers.CLIPTextModel), specifically
221
- the [clip-vit-large-patch14](https://huggingface.co/openai/clip-vit-large-patch14) variant.
222
- text_encoder_2 ([`T5EncoderModel`]):
223
- [T5](https://huggingface.co/docs/transformers/en/model_doc/t5#transformers.T5EncoderModel), specifically
224
- the [google/t5-v1_1-xxl](https://huggingface.co/google/t5-v1_1-xxl) variant.
225
- tokenizer (`CLIPTokenizer`):
226
- Tokenizer of class
227
- [CLIPTokenizer](https://huggingface.co/docs/transformers/en/model_doc/clip#transformers.CLIPTokenizer).
228
- tokenizer_2 (`T5TokenizerFast`):
229
- Second Tokenizer of class
230
- [T5TokenizerFast](https://huggingface.co/docs/transformers/en/model_doc/t5#transformers.T5TokenizerFast).
231
- """
232
-
233
- model_cpu_offload_seq = "text_encoder->text_encoder_2->image_encoder->transformer->vae"
234
- transformer: FluxTransformer2DModel
235
- _optional_components = ["image_encoder", "feature_extractor"]
236
- _callback_tensor_inputs = ["latents", "prompt_embeds"]
237
-
238
- def __init__(
239
- self,
240
- scheduler: FlowMatchEulerDiscreteScheduler,
241
- vae: AutoencoderKL,
242
- text_encoder: CLIPTextModel,
243
- tokenizer: CLIPTokenizer,
244
- text_encoder_2: T5EncoderModel,
245
- tokenizer_2: T5TokenizerFast,
246
- transformer: FluxTransformer2DModel,
247
- image_encoder: CLIPVisionModelWithProjection = None,
248
- feature_extractor: CLIPImageProcessor = None,
249
- ):
250
- super().__init__()
251
-
252
- self.register_modules(
253
- vae=vae,
254
- text_encoder=text_encoder,
255
- text_encoder_2=text_encoder_2,
256
- tokenizer=tokenizer,
257
- tokenizer_2=tokenizer_2,
258
- transformer=transformer,
259
- scheduler=scheduler,
260
- image_encoder=image_encoder,
261
- feature_extractor=feature_extractor,
262
- )
263
- self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1) if getattr(self, "vae", None) else 8
264
- # Flux latents are turned into 2x2 patches and packed. This means the latent width and height has to be divisible
265
- # by the patch size. So the vae scale factor is multiplied by the patch size to account for this
266
- self.latent_channels = self.vae.config.latent_channels if getattr(self, "vae", None) else 16
267
- self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor * 2)
268
- self.tokenizer_max_length = (
269
- self.tokenizer.model_max_length if hasattr(self, "tokenizer") and self.tokenizer is not None else 77
270
- )
271
- self.default_sample_size = 128
272
-
273
- # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline._get_t5_prompt_embeds
274
- def _get_t5_prompt_embeds(
275
- self,
276
- prompt: Union[str, List[str]] = None,
277
- num_images_per_prompt: int = 1,
278
- max_sequence_length: int = 512,
279
- device: Optional[torch.device] = None,
280
- dtype: Optional[torch.dtype] = None,
281
- ):
282
- device = device or self._execution_device
283
- dtype = dtype or self.text_encoder.dtype
284
-
285
- prompt = [prompt] if isinstance(prompt, str) else prompt
286
- batch_size = len(prompt)
287
-
288
- if isinstance(self, TextualInversionLoaderMixin):
289
- prompt = self.maybe_convert_prompt(prompt, self.tokenizer_2)
290
-
291
- text_inputs = self.tokenizer_2(
292
- prompt,
293
- padding="max_length",
294
- max_length=max_sequence_length,
295
- truncation=True,
296
- return_length=False,
297
- return_overflowing_tokens=False,
298
- return_tensors="pt",
299
- )
300
- text_input_ids = text_inputs.input_ids
301
- untruncated_ids = self.tokenizer_2(prompt, padding="longest", return_tensors="pt").input_ids
302
-
303
- if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
304
- removed_text = self.tokenizer_2.batch_decode(untruncated_ids[:, self.tokenizer_max_length - 1 : -1])
305
- logger.warning(
306
- "The following part of your input was truncated because `max_sequence_length` is set to "
307
- f" {max_sequence_length} tokens: {removed_text}"
308
- )
309
-
310
- prompt_embeds = self.text_encoder_2(text_input_ids.to(device), output_hidden_states=False)[0]
311
-
312
- dtype = self.text_encoder_2.dtype
313
- prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
314
-
315
- _, seq_len, _ = prompt_embeds.shape
316
-
317
- # duplicate text embeddings and attention mask for each generation per prompt, using mps friendly method
318
- prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
319
- prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
320
-
321
- return prompt_embeds
322
-
323
- # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline._get_clip_prompt_embeds
324
- def _get_clip_prompt_embeds(
325
- self,
326
- prompt: Union[str, List[str]],
327
- num_images_per_prompt: int = 1,
328
- device: Optional[torch.device] = None,
329
- ):
330
- device = device or self._execution_device
331
-
332
- prompt = [prompt] if isinstance(prompt, str) else prompt
333
- batch_size = len(prompt)
334
-
335
- if isinstance(self, TextualInversionLoaderMixin):
336
- prompt = self.maybe_convert_prompt(prompt, self.tokenizer)
337
-
338
- text_inputs = self.tokenizer(
339
- prompt,
340
- padding="max_length",
341
- max_length=self.tokenizer_max_length,
342
- truncation=True,
343
- return_overflowing_tokens=False,
344
- return_length=False,
345
- return_tensors="pt",
346
- )
347
-
348
- text_input_ids = text_inputs.input_ids
349
- untruncated_ids = self.tokenizer(prompt, padding="longest", return_tensors="pt").input_ids
350
- if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
351
- removed_text = self.tokenizer.batch_decode(untruncated_ids[:, self.tokenizer_max_length - 1 : -1])
352
- logger.warning(
353
- "The following part of your input was truncated because CLIP can only handle sequences up to"
354
- f" {self.tokenizer_max_length} tokens: {removed_text}"
355
- )
356
- prompt_embeds = self.text_encoder(text_input_ids.to(device), output_hidden_states=False)
357
-
358
- # Use pooled output of CLIPTextModel
359
- prompt_embeds = prompt_embeds.pooler_output
360
- prompt_embeds = prompt_embeds.to(dtype=self.text_encoder.dtype, device=device)
361
-
362
- # duplicate text embeddings for each generation per prompt, using mps friendly method
363
- prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt)
364
- prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, -1)
365
-
366
- return prompt_embeds
367
-
368
- # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.encode_prompt
369
- def encode_prompt(
370
- self,
371
- prompt: Union[str, List[str]],
372
- prompt_2: Optional[Union[str, List[str]]] = None,
373
- device: Optional[torch.device] = None,
374
- num_images_per_prompt: int = 1,
375
- prompt_embeds: Optional[torch.FloatTensor] = None,
376
- pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
377
- max_sequence_length: int = 512,
378
- lora_scale: Optional[float] = None,
379
- ):
380
- r"""
381
-
382
- Args:
383
- prompt (`str` or `List[str]`, *optional*):
384
- prompt to be encoded
385
- prompt_2 (`str` or `List[str]`, *optional*):
386
- The prompt or prompts to be sent to the `tokenizer_2` and `text_encoder_2`. If not defined, `prompt` is
387
- used in all text-encoders
388
- device: (`torch.device`):
389
- torch device
390
- num_images_per_prompt (`int`):
391
- number of images that should be generated per prompt
392
- prompt_embeds (`torch.FloatTensor`, *optional*):
393
- Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
394
- provided, text embeddings will be generated from `prompt` input argument.
395
- pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
396
- Pre-generated pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting.
397
- If not provided, pooled text embeddings will be generated from `prompt` input argument.
398
- lora_scale (`float`, *optional*):
399
- A lora scale that will be applied to all LoRA layers of the text encoder if LoRA layers are loaded.
400
- """
401
- device = device or self._execution_device
402
-
403
- # set lora scale so that monkey patched LoRA
404
- # function of text encoder can correctly access it
405
- if lora_scale is not None and isinstance(self, FluxLoraLoaderMixin):
406
- self._lora_scale = lora_scale
407
-
408
- # dynamically adjust the LoRA scale
409
- if self.text_encoder is not None and USE_PEFT_BACKEND:
410
- scale_lora_layers(self.text_encoder, lora_scale)
411
- if self.text_encoder_2 is not None and USE_PEFT_BACKEND:
412
- scale_lora_layers(self.text_encoder_2, lora_scale)
413
-
414
- prompt = [prompt] if isinstance(prompt, str) else prompt
415
-
416
- if prompt_embeds is None:
417
- prompt_2 = prompt_2 or prompt
418
- prompt_2 = [prompt_2] if isinstance(prompt_2, str) else prompt_2
419
-
420
- # We only use the pooled prompt output from the CLIPTextModel
421
- pooled_prompt_embeds = self._get_clip_prompt_embeds(
422
- prompt=prompt,
423
- device=device,
424
- num_images_per_prompt=num_images_per_prompt,
425
- )
426
- prompt_embeds = self._get_t5_prompt_embeds(
427
- prompt=prompt_2,
428
- num_images_per_prompt=num_images_per_prompt,
429
- max_sequence_length=max_sequence_length,
430
- device=device,
431
- )
432
-
433
- if self.text_encoder is not None:
434
- if isinstance(self, FluxLoraLoaderMixin) and USE_PEFT_BACKEND:
435
- # Retrieve the original scale by scaling back the LoRA layers
436
- unscale_lora_layers(self.text_encoder, lora_scale)
437
-
438
- if self.text_encoder_2 is not None:
439
- if isinstance(self, FluxLoraLoaderMixin) and USE_PEFT_BACKEND:
440
- # Retrieve the original scale by scaling back the LoRA layers
441
- unscale_lora_layers(self.text_encoder_2, lora_scale)
442
-
443
- dtype = self.text_encoder.dtype if self.text_encoder is not None else self.transformer.dtype
444
- text_ids = torch.zeros(prompt_embeds.shape[1], 3).to(device=device, dtype=dtype)
445
-
446
- return prompt_embeds, pooled_prompt_embeds, text_ids
447
-
448
- # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.encode_image
449
- def encode_image(self, image, device, num_images_per_prompt):
450
- dtype = next(self.image_encoder.parameters()).dtype
451
-
452
- if not isinstance(image, torch.Tensor):
453
- image = self.feature_extractor(image, return_tensors="pt").pixel_values
454
-
455
- image = image.to(device=device, dtype=dtype)
456
- image_embeds = self.image_encoder(image).image_embeds
457
- image_embeds = image_embeds.repeat_interleave(num_images_per_prompt, dim=0)
458
- return image_embeds
459
-
460
- # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.prepare_ip_adapter_image_embeds
461
- def prepare_ip_adapter_image_embeds(
462
- self, ip_adapter_image, ip_adapter_image_embeds, device, num_images_per_prompt
463
- ):
464
- image_embeds = []
465
- if ip_adapter_image_embeds is None:
466
- if not isinstance(ip_adapter_image, list):
467
- ip_adapter_image = [ip_adapter_image]
468
-
469
- if len(ip_adapter_image) != self.transformer.encoder_hid_proj.num_ip_adapters:
470
- raise ValueError(
471
- f"`ip_adapter_image` must have same length as the number of IP Adapters. Got {len(ip_adapter_image)} images and {self.transformer.encoder_hid_proj.num_ip_adapters} IP Adapters."
472
- )
473
-
474
- for single_ip_adapter_image in ip_adapter_image:
475
- single_image_embeds = self.encode_image(single_ip_adapter_image, device, 1)
476
- image_embeds.append(single_image_embeds[None, :])
477
- else:
478
- if not isinstance(ip_adapter_image_embeds, list):
479
- ip_adapter_image_embeds = [ip_adapter_image_embeds]
480
-
481
- if len(ip_adapter_image_embeds) != self.transformer.encoder_hid_proj.num_ip_adapters:
482
- raise ValueError(
483
- f"`ip_adapter_image_embeds` must have same length as the number of IP Adapters. Got {len(ip_adapter_image_embeds)} image embeds and {self.transformer.encoder_hid_proj.num_ip_adapters} IP Adapters."
484
- )
485
-
486
- for single_image_embeds in ip_adapter_image_embeds:
487
- image_embeds.append(single_image_embeds)
488
-
489
- ip_adapter_image_embeds = []
490
- for single_image_embeds in image_embeds:
491
- single_image_embeds = torch.cat([single_image_embeds] * num_images_per_prompt, dim=0)
492
- single_image_embeds = single_image_embeds.to(device=device)
493
- ip_adapter_image_embeds.append(single_image_embeds)
494
-
495
- return ip_adapter_image_embeds
496
-
497
- # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.check_inputs
498
- def check_inputs(
499
- self,
500
- prompt,
501
- prompt_2,
502
- height,
503
- width,
504
- negative_prompt=None,
505
- negative_prompt_2=None,
506
- prompt_embeds=None,
507
- negative_prompt_embeds=None,
508
- pooled_prompt_embeds=None,
509
- negative_pooled_prompt_embeds=None,
510
- callback_on_step_end_tensor_inputs=None,
511
- max_sequence_length=None,
512
- ):
513
- if height % (self.vae_scale_factor * 2) != 0 or width % (self.vae_scale_factor * 2) != 0:
514
- logger.warning(
515
- f"`height` and `width` have to be divisible by {self.vae_scale_factor * 2} but are {height} and {width}. Dimensions will be resized accordingly"
516
- )
517
-
518
- if callback_on_step_end_tensor_inputs is not None and not all(
519
- k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
520
- ):
521
- raise ValueError(
522
- f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
523
- )
524
-
525
- if prompt is not None and prompt_embeds is not None:
526
- raise ValueError(
527
- f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
528
- " only forward one of the two."
529
- )
530
- elif prompt_2 is not None and prompt_embeds is not None:
531
- raise ValueError(
532
- f"Cannot forward both `prompt_2`: {prompt_2} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
533
- " only forward one of the two."
534
- )
535
- elif prompt is None and prompt_embeds is None:
536
- raise ValueError(
537
- "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
538
- )
539
- elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
540
- raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
541
- elif prompt_2 is not None and (not isinstance(prompt_2, str) and not isinstance(prompt_2, list)):
542
- raise ValueError(f"`prompt_2` has to be of type `str` or `list` but is {type(prompt_2)}")
543
-
544
- if negative_prompt is not None and negative_prompt_embeds is not None:
545
- raise ValueError(
546
- f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:"
547
- f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
548
- )
549
- elif negative_prompt_2 is not None and negative_prompt_embeds is not None:
550
- raise ValueError(
551
- f"Cannot forward both `negative_prompt_2`: {negative_prompt_2} and `negative_prompt_embeds`:"
552
- f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
553
- )
554
-
555
- if prompt_embeds is not None and pooled_prompt_embeds is None:
556
- raise ValueError(
557
- "If `prompt_embeds` are provided, `pooled_prompt_embeds` also have to be passed. Make sure to generate `pooled_prompt_embeds` from the same text encoder that was used to generate `prompt_embeds`."
558
- )
559
- if negative_prompt_embeds is not None and negative_pooled_prompt_embeds is None:
560
- raise ValueError(
561
- "If `negative_prompt_embeds` are provided, `negative_pooled_prompt_embeds` also have to be passed. Make sure to generate `negative_pooled_prompt_embeds` from the same text encoder that was used to generate `negative_prompt_embeds`."
562
- )
563
-
564
- if max_sequence_length is not None and max_sequence_length > 512:
565
- raise ValueError(f"`max_sequence_length` cannot be greater than 512 but is {max_sequence_length}")
566
-
567
- @staticmethod
568
- # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline._pack_latents
569
- def _pack_latents(latents, batch_size, num_channels_latents, height, width):
570
- latents = latents.view(batch_size, num_channels_latents, height // 2, 2, width // 2, 2)
571
- latents = latents.permute(0, 2, 4, 1, 3, 5)
572
- latents = latents.reshape(batch_size, (height // 2) * (width // 2), num_channels_latents * 4)
573
-
574
- return latents
575
-
576
- @staticmethod
577
- # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline._unpack_latents
578
- def _unpack_latents(latents, height, width, vae_scale_factor):
579
- batch_size, num_patches, channels = latents.shape
580
-
581
- # VAE applies 8x compression on images but we must also account for packing which requires
582
- # latent height and width to be divisible by 2.
583
- height = 2 * (int(height) // (vae_scale_factor * 2))
584
- width = 2 * (int(width) // (vae_scale_factor * 2))
585
-
586
- latents = latents.view(batch_size, height // 2, width // 2, channels // 4, 2, 2)
587
- latents = latents.permute(0, 3, 1, 4, 2, 5)
588
-
589
- latents = latents.reshape(batch_size, channels // (2 * 2), height, width)
590
-
591
- return latents
592
-
593
- def _encode_vae_image(self, image: torch.Tensor, generator: torch.Generator):
594
- if isinstance(generator, list):
595
- image_latents = [
596
- retrieve_latents(self.vae.encode(image[i : i + 1]), generator=generator[i], sample_mode="argmax")
597
- for i in range(image.shape[0])
598
- ]
599
- image_latents = torch.cat(image_latents, dim=0)
600
- else:
601
- image_latents = retrieve_latents(self.vae.encode(image), generator=generator, sample_mode="argmax")
602
-
603
- image_latents = (image_latents - self.vae.config.shift_factor) * self.vae.config.scaling_factor
604
-
605
- return image_latents
606
-
607
- # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.enable_vae_slicing
608
- def enable_vae_slicing(self):
609
- r"""
610
- Enable sliced VAE decoding. When this option is enabled, the VAE will split the input tensor in slices to
611
- compute decoding in several steps. This is useful to save some memory and allow larger batch sizes.
612
- """
613
- self.vae.enable_slicing()
614
-
615
- # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.disable_vae_slicing
616
- def disable_vae_slicing(self):
617
- r"""
618
- Disable sliced VAE decoding. If `enable_vae_slicing` was previously enabled, this method will go back to
619
- computing decoding in one step.
620
- """
621
- self.vae.disable_slicing()
622
-
623
- # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.enable_vae_tiling
624
- def enable_vae_tiling(self):
625
- r"""
626
- Enable tiled VAE decoding. When this option is enabled, the VAE will split the input tensor into tiles to
627
- compute decoding and encoding in several steps. This is useful for saving a large amount of memory and to allow
628
- processing larger images.
629
- """
630
- self.vae.enable_tiling()
631
-
632
- # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.disable_vae_tiling
633
- def disable_vae_tiling(self):
634
- r"""
635
- Disable tiled VAE decoding. If `enable_vae_tiling` was previously enabled, this method will go back to
636
- computing decoding in one step.
637
- """
638
- self.vae.disable_tiling()
639
-
640
- @property
641
- def guidance_scale(self):
642
- return self._guidance_scale
643
-
644
- @property
645
- def joint_attention_kwargs(self):
646
- return self._joint_attention_kwargs
647
-
648
- @property
649
- def num_timesteps(self):
650
- return self._num_timesteps
651
-
652
- @property
653
- def current_timestep(self):
654
- return self._current_timestep
655
-
656
- @property
657
- def interrupt(self):
658
- return self._interrupt
659
-
660
- def _create_attention_mask(
661
- self,
662
- attention_rules,
663
- num_patches,
664
- total_text_len,
665
- prompt_len,
666
- hint_lens,
667
- image_patch_indices_list,
668
- batch_size,
669
- num_images_per_prompt,
670
- device,
671
- mask_main_prompt_influence=False,
672
- symmetric_masking=False,
673
- delete_main_prompt=False,
674
- ):
675
- total_seq_len = total_text_len + 2 * num_patches
676
- attention_mask = torch.zeros(
677
- num_images_per_prompt * batch_size,
678
- 24, # attention heads
679
- total_seq_len,
680
- total_seq_len,
681
- device=device,
682
- dtype=torch.bool,
683
- )
684
-
685
- if attention_rules:
686
- # 1. Define component indices
687
- num_regions = len(image_patch_indices_list)
688
-
689
- # Get indices for text components
690
- text_indices = {}
691
- if not delete_main_prompt:
692
- text_indices['Main Prompt'] = list(range(prompt_len))
693
-
694
- hint_start_idx = prompt_len
695
- for i in range(num_regions):
696
- hint_len = hint_lens[i]
697
- text_indices[f'Hint {i+1}'] = list(range(hint_start_idx, hint_start_idx + hint_len))
698
- hint_start_idx += hint_len
699
-
700
- # Get indices for image patch components
701
- all_bbox_patches = set()
702
- for indices in image_patch_indices_list:
703
- all_bbox_patches.update(indices)
704
-
705
- all_patches = set(range(num_patches))
706
- bg_patches = all_patches - all_bbox_patches
707
-
708
- patch_indices = {}
709
- for i, indices in enumerate(image_patch_indices_list):
710
- patch_indices[f'BBox {i+1}'] = list(indices)
711
- patch_indices['Background'] = list(bg_patches)
712
-
713
- # Helper to get all indices for a component name
714
- def get_indices(comp_name):
715
- if comp_name in text_indices:
716
- return text_indices[comp_name]
717
-
718
- # Decouple Noise and Image patches
719
- if 'Noise' in comp_name:
720
- base_comp_name = comp_name.replace('Noise ', '')
721
- if base_comp_name in patch_indices:
722
- return [total_text_len + i for i in patch_indices[base_comp_name]]
723
- elif 'Image' in comp_name:
724
- base_comp_name = comp_name.replace('Image ', '')
725
- if base_comp_name in patch_indices:
726
- return [total_text_len + num_patches + i for i in patch_indices[base_comp_name]]
727
- # Fallback for old component names for backward compatibility
728
- elif comp_name in patch_indices:
729
- l1_indices = [total_text_len + i for i in patch_indices[comp_name]]
730
- l2_indices = [total_text_len + num_patches + i for i in patch_indices[comp_name]]
731
- return l1_indices + l2_indices
732
-
733
- return []
734
-
735
- # 2. Populate attention_mask based on rules
736
- for (q_comp, k_comp), allowed in attention_rules.items():
737
- if allowed:
738
- q_indices = get_indices(q_comp)
739
- k_indices = get_indices(k_comp)
740
- if q_indices and k_indices:
741
- # Create index tensors on the correct device
742
- q_indices_tensor = torch.tensor(q_indices, device=device, dtype=torch.long)
743
- k_indices_tensor = torch.tensor(k_indices, device=device, dtype=torch.long)
744
- # Use advanced indexing to set the mask values
745
- attention_mask[:, :, q_indices_tensor.view(-1, 1), k_indices_tensor.view(1, -1)] = True
746
-
747
- else:
748
- # Original attention mask logic
749
- # 1. Text-to-Text attention:
750
- # Main prompt attends to itself
751
- attention_mask[:, :, :prompt_len, :prompt_len] = True
752
- # Each hint attends to itself
753
- hint_start_idx = prompt_len
754
- for hint_len in hint_lens:
755
- attention_mask[:, :, hint_start_idx : hint_start_idx + hint_len, hint_start_idx : hint_start_idx + hint_len] = True
756
- hint_start_idx += hint_len
757
-
758
- # 2. Image-to-Image attention: all image patches attend to each other
759
- attention_mask[:, :, total_text_len:, total_text_len:] = True
760
-
761
- # 3. Image-to-Text attention (Region Guidance)
762
- if not mask_main_prompt_influence:
763
- # All patches attend to the main prompt
764
- attention_mask[:, :, total_text_len:, :prompt_len] = True
765
-
766
- # Specific patch regions attend to their corresponding hints
767
- hint_start_idx = prompt_len
768
- for i, patch_indices in enumerate(image_patch_indices_list):
769
- hint_len = hint_lens[i]
770
- if patch_indices:
771
- # Apply to both latent copies
772
- for p_idx in patch_indices:
773
- # First latent copy
774
- attention_mask[:, :, total_text_len + p_idx, hint_start_idx : hint_start_idx + hint_len] = True
775
- # Second latent copy
776
- attention_mask[:, :, total_text_len + num_patches + p_idx, hint_start_idx : hint_start_idx + hint_len] = True
777
- hint_start_idx += hint_len
778
-
779
- # 4. (Optional) Symmetric Text-to-Image attention
780
- if symmetric_masking:
781
- # Main prompt attends to all image patches
782
- if not mask_main_prompt_influence:
783
- attention_mask[:, :, :prompt_len, total_text_len:] = True
784
-
785
- # Regional hints attend to their corresponding image patches
786
- hint_start_idx = prompt_len
787
- for i, patch_indices in enumerate(image_patch_indices_list):
788
- hint_len = hint_lens[i]
789
- if patch_indices:
790
- for p_idx in patch_indices:
791
- # First latent copy
792
- attention_mask[:, :, hint_start_idx : hint_start_idx + hint_len, total_text_len + p_idx] = True
793
- # Second latent copy
794
- attention_mask[:, :, hint_start_idx : hint_start_idx + hint_len, total_text_len + num_patches + p_idx] = True
795
- hint_start_idx += hint_len
796
- else:
797
- # Main prompt attends to all image patches
798
- if not mask_main_prompt_influence:
799
- attention_mask[:, :, :prompt_len, total_text_len:] = True
800
-
801
- # All Regional hints attend to ALL image patches
802
- # hints range: [prompt_len : total_text_len]
803
- # image patches range: [total_text_len : end]
804
- attention_mask[:, :, prompt_len:total_text_len, total_text_len:] = True
805
-
806
- return attention_mask
807
-
808
- def process_region_guidance(
809
- self,
810
- prompt_embeds: torch.FloatTensor,
811
- text_ids: torch.FloatTensor,
812
- region_guidance: List[Dict],
813
- width: int, # resized width
814
- height: int, # resized height
815
- original_height: int,
816
- original_width: int,
817
- dtype: torch.dtype,
818
- device: torch.device,
819
- num_images_per_prompt: Optional[int] = 1,
820
- max_sequence_length: Optional[int] = 512,
821
- mask_main_prompt_influence: bool = False,
822
- delete_main_prompt: bool = False,
823
- symmetric_masking: bool = False,
824
- attention_rules: Optional[Dict] = None,
825
- return_attention_mask: bool = True,
826
- ):
827
- """
828
- Processes regional guidance to generate combined text embeddings and a self-attention mask.
829
-
830
- This function takes a list of regional guidance specifications (bounding boxes and text hints)
831
- and integrates them with the main prompt. It computes patch indices corresponding to each bounding
832
- box and constructs a custom self-attention mask for a sequence structured as `[text, image, image]`.
833
-
834
- The resulting attention mask enables:
835
- 1. Full attention within text tokens (Text-to-Text).
836
- 2. Full attention within image patch tokens (Image-to-Image).
837
- 3. Attention from all image patches to the main prompt tokens (Image-to-Text).
838
- 4. Focused attention from image patches within a specific bounding box to their corresponding
839
- regional text hint, enabling fine-grained, localized image generation.
840
-
841
- Args:
842
- prompt_embeds (`torch.FloatTensor`): Embeddings for the main prompt.
843
- text_ids (`torch.FloatTensor`): Text IDs for the main prompt.
844
- region_guidance (`List[Dict]`): A list where each dict contains a 'bbox' and a 'hint'.
845
- width (`int`): The target width of the image being generated.
846
- height (`int`): The target height of the image being generated.
847
- original_height (`int`): The original height provided by the user.
848
- original_width (`int`): The original width provided by the user.
849
- dtype (`torch.dtype`): The data type for new tensors.
850
- device (`torch.device`): The device for new tensors.
851
- num_images_per_prompt (`int`, *optional*): Number of images per prompt. Defaults to 1.
852
- max_sequence_length (`int`, *optional*): Max sequence length for text encoder. Defaults to 512.
853
- mask_main_prompt_influence (`bool`, *optional*): If True, prevents image patches from attending to the main prompt. Defaults to False.
854
- symmetric_masking (`bool`, *optional*): If True, makes the text-image attention mask symmetric. Defaults to False.
855
-
856
- Returns:
857
- Tuple: A tuple containing:
858
- - `final_prompt_embeds` (`torch.FloatTensor`): Concatenated embeddings of the main prompt and all hints.
859
- - `final_text_ids` (`torch.FloatTensor`): Concatenated text IDs.
860
- - `attention_mask` (`torch.FloatTensor`): The 4D self-attention mask for the combined sequence.
861
- - `prompt_len` (`int`): The sequence length of the original prompt.
862
- - `hint_lens` (`List[int]`): A list of sequence lengths for each hint.
863
- """
864
- batch_size = prompt_embeds.shape[0] // num_images_per_prompt
865
- hint_embeds_list = []
866
- text_ids_list = []
867
- for guidance in region_guidance:
868
- hint_i = guidance['hint']
869
- # NOTE: Assuming _get_t5_prompt_embeds exists and returns prompt_embeds
870
- hint_embeds_i = self._get_t5_prompt_embeds(
871
- prompt=hint_i,
872
- num_images_per_prompt=num_images_per_prompt,
873
- max_sequence_length=max_sequence_length,
874
- device=device,
875
- )
876
- if batch_size > 1:
877
- hint_embeds_i = hint_embeds_i.repeat(batch_size, 1, 1)
878
-
879
- dtype_ = self.text_encoder_2.dtype if self.text_encoder_2 is not None else self.transformer.dtype
880
- text_ids_i = torch.zeros(hint_embeds_i.shape[1], 3).to(device=device, dtype=dtype_)
881
- hint_embeds_list.append(hint_embeds_i)
882
- text_ids_list.append(text_ids_i)
883
-
884
- grid_h = height // self.vae_scale_factor // 2
885
- grid_w = width // self.vae_scale_factor // 2
886
- num_patches = grid_h * grid_w
887
-
888
- image_patch_indices_list = []
889
- for guidance in region_guidance:
890
- bbox = guidance["bbox"]
891
- x1, y1, x2, y2 = bbox
892
- scale_x = width / original_width
893
- scale_y = height / original_height
894
-
895
- start_col = int(math.floor(x1 * scale_x / self.vae_scale_factor / 2))
896
- end_col = int(math.ceil(x2 * scale_x / self.vae_scale_factor / 2))
897
- start_row = int(math.floor(y1 * scale_y / self.vae_scale_factor / 2))
898
- end_row = int(math.ceil(y2 * scale_y / self.vae_scale_factor / 2))
899
-
900
- start_col = max(0, start_col)
901
- end_col = min(grid_w, end_col)
902
- start_row = max(0, start_row)
903
- end_row = min(grid_h, end_row)
904
-
905
- patch_indices = []
906
- for r in range(start_row, end_row):
907
- for c in range(start_col, end_col):
908
- patch_indices.append(r * grid_w + c)
909
- image_patch_indices_list.append(patch_indices)
910
-
911
- if delete_main_prompt:
912
- final_prompt_embeds = torch.cat(hint_embeds_list, dim=1)
913
- final_text_ids = torch.cat(text_ids_list, dim=0)
914
- prompt_len = 0
915
- else:
916
- final_prompt_embeds = torch.cat([prompt_embeds] + hint_embeds_list, dim=1)
917
- final_text_ids = torch.cat([text_ids] + text_ids_list, dim=0)
918
- prompt_len = prompt_embeds.shape[1]
919
-
920
- hint_lens = [h.shape[1] for h in hint_embeds_list]
921
- total_text_len = final_prompt_embeds.shape[1]
922
-
923
- if not return_attention_mask:
924
- return final_prompt_embeds, final_text_ids, None, prompt_len, hint_lens, image_patch_indices_list, num_patches
925
-
926
- # New self-attention mask logic for [prompt, latents, latents] sequence
927
- attention_mask = self._create_attention_mask(
928
- attention_rules,
929
- num_patches,
930
- total_text_len,
931
- prompt_len,
932
- hint_lens,
933
- image_patch_indices_list,
934
- batch_size,
935
- num_images_per_prompt,
936
- device,
937
- mask_main_prompt_influence,
938
- symmetric_masking,
939
- delete_main_prompt,
940
- )
941
-
942
- return final_prompt_embeds, final_text_ids, attention_mask, prompt_len, hint_lens, image_patch_indices_list, num_patches
943
-
944
- def set_attn_processor(self, attn_processor_class, **kwargs):
945
- def fn_recursive_attn_processor(name: str, module: torch.nn.Module, processor_class, **kwargs):
946
- if hasattr(module, "set_processor"):
947
- module.set_processor(processor_class(**kwargs))
948
-
949
- for sub_name, child in module.named_children():
950
- fn_recursive_attn_processor(f"{name}.{sub_name}", child, processor_class, **kwargs)
951
-
952
- fn_recursive_attn_processor("transformer", self.transformer, attn_processor_class, **kwargs)
953
-
954
- @torch.no_grad()
955
- def __call__(
956
- self,
957
- image: Optional[PipelineImageInput] = None,
958
- prompt: Union[str, List[str]] = None,
959
- prompt_2: Optional[Union[str, List[str]]] = None,
960
- negative_prompt: Union[str, List[str]] = None,
961
- negative_prompt_2: Optional[Union[str, List[str]]] = None,
962
- true_cfg_scale: float = 1.0,
963
- height: Optional[int] = None,
964
- width: Optional[int] = None,
965
- num_inference_steps: int = 28,
966
- sigmas: Optional[List[float]] = None,
967
- guidance_scale: float = 3.5,
968
- num_images_per_prompt: Optional[int] = 1,
969
- generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
970
- latents: Optional[torch.FloatTensor] = None,
971
- prompt_embeds: Optional[torch.FloatTensor] = None,
972
- pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
973
- ip_adapter_image: Optional[PipelineImageInput] = None,
974
- ip_adapter_image_embeds: Optional[List[torch.Tensor]] = None,
975
- negative_ip_adapter_image: Optional[PipelineImageInput] = None,
976
- negative_ip_adapter_image_embeds: Optional[List[torch.Tensor]] = None,
977
- negative_prompt_embeds: Optional[torch.FloatTensor] = None,
978
- negative_pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
979
- output_type: Optional[str] = "pil",
980
- return_dict: bool = True,
981
- joint_attention_kwargs: Optional[Dict[str, Any]] = None,
982
- callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
983
- callback_on_step_end_tensor_inputs: List[str] = ["latents"],
984
- max_sequence_length: int = 512,
985
- max_area: int = 1024**2,
986
- _auto_resize: bool = True,
987
- region_guidance: Optional[List[Dict]] = None, # [{'bbox': [x1, y1, x2, y2], 'hint': 'xxx'}, ...]
988
- mask_main_prompt_influence: bool = False,
989
- symmetric_masking: bool = True,
990
- delete_main_prompt: bool = False,
991
- attention_rules: Optional[Dict] = None,
992
- enable_flex_attn: bool = True,
993
- flex_attn_use_bitmask: bool = True,
994
- ):
995
- print(enable_flex_attn)
996
- height = height or image.height
997
- width = width or image.width
998
-
999
- original_height, original_width = height, width
1000
- aspect_ratio = width / height
1001
- width = round((max_area * aspect_ratio) ** 0.5)
1002
- height = round((max_area / aspect_ratio) ** 0.5)
1003
-
1004
- # NOTE: Kontext is trained on specific resolutions, using one of them is recommended
1005
- _, width, height = min(
1006
- (abs(aspect_ratio - w / h), w, h) for w, h in PREFERRED_KONTEXT_RESOLUTIONS
1007
- )
1008
-
1009
- multiple_of = self.vae_scale_factor * 2
1010
- width = width // multiple_of * multiple_of
1011
- height = height // multiple_of * multiple_of
1012
-
1013
-
1014
- # 1. Check inputs. Raise error if not correct
1015
- self.check_inputs(
1016
- prompt,
1017
- prompt_2,
1018
- height,
1019
- width,
1020
- negative_prompt=negative_prompt,
1021
- negative_prompt_2=negative_prompt_2,
1022
- prompt_embeds=prompt_embeds,
1023
- negative_prompt_embeds=negative_prompt_embeds,
1024
- pooled_prompt_embeds=pooled_prompt_embeds,
1025
- negative_pooled_prompt_embeds=negative_pooled_prompt_embeds,
1026
- callback_on_step_end_tensor_inputs=callback_on_step_end_tensor_inputs,
1027
- max_sequence_length=max_sequence_length,
1028
- )
1029
-
1030
- self._guidance_scale = guidance_scale
1031
- self._joint_attention_kwargs = joint_attention_kwargs
1032
- self._current_timestep = None
1033
- self._interrupt = False
1034
-
1035
- if self._joint_attention_kwargs is None:
1036
- self._joint_attention_kwargs = {}
1037
-
1038
- # 2. Define call parameters
1039
- if prompt is not None and isinstance(prompt, str):
1040
- batch_size = 1
1041
- elif prompt is not None and isinstance(prompt, list):
1042
- batch_size = len(prompt)
1043
- else:
1044
- batch_size = prompt_embeds.shape[0]
1045
-
1046
- device = self._execution_device
1047
-
1048
- lora_scale = (
1049
- self.joint_attention_kwargs.get("scale", None) if self.joint_attention_kwargs is not None else None
1050
- )
1051
- has_neg_prompt = negative_prompt is not None or (
1052
- negative_prompt_embeds is not None and negative_pooled_prompt_embeds is not None
1053
- )
1054
- do_true_cfg = true_cfg_scale > 1 and has_neg_prompt
1055
- (
1056
- prompt_embeds,
1057
- pooled_prompt_embeds,
1058
- text_ids,
1059
- ) = self.encode_prompt(
1060
- prompt=prompt,
1061
- prompt_2=prompt_2,
1062
- prompt_embeds=prompt_embeds,
1063
- pooled_prompt_embeds=pooled_prompt_embeds,
1064
- device=device,
1065
- num_images_per_prompt=num_images_per_prompt,
1066
- max_sequence_length=max_sequence_length,
1067
- lora_scale=lora_scale,
1068
- )
1069
- if do_true_cfg:
1070
- (
1071
- negative_prompt_embeds,
1072
- negative_pooled_prompt_embeds,
1073
- negative_text_ids,
1074
- ) = self.encode_prompt(
1075
- prompt=negative_prompt,
1076
- prompt_2=negative_prompt_2,
1077
- prompt_embeds=negative_prompt_embeds,
1078
- pooled_prompt_embeds=negative_pooled_prompt_embeds,
1079
- device=device,
1080
- num_images_per_prompt=num_images_per_prompt,
1081
- max_sequence_length=max_sequence_length,
1082
- lora_scale=lora_scale,
1083
- )
1084
-
1085
- attention_mask = None
1086
- prompt_len = prompt_embeds.shape[1]
1087
- hint_lens = []
1088
- image_patch_indices_list = []
1089
- num_patches = 0
1090
-
1091
- step_attention_rules = {}
1092
- if attention_rules is None:
1093
- attention_rules = generate_default_attention_rules(region_guidance or [], delete_main_prompt=delete_main_prompt, bboxes_attend_to_each_other=True, has_image_prompt=False, symmetric_masking=symmetric_masking)
1094
- elif isinstance(attention_rules, dict) and any(isinstance(k, int) for k in attention_rules.keys()):
1095
- step_attention_rules = attention_rules
1096
- attention_rules = generate_default_attention_rules(region_guidance or [], delete_main_prompt=delete_main_prompt, bboxes_attend_to_each_other=True, has_image_prompt=False, symmetric_masking=symmetric_masking)
1097
-
1098
- if region_guidance:
1099
- prompt_embeds, text_ids, attention_mask, prompt_len, hint_lens, image_patch_indices_list, num_patches = self.process_region_guidance(
1100
- prompt_embeds=prompt_embeds,
1101
- text_ids=text_ids,
1102
- region_guidance=region_guidance,
1103
- width=width,
1104
- height=height,
1105
- original_height=original_height,
1106
- original_width=original_width,
1107
- dtype=prompt_embeds.dtype,
1108
- device=device,
1109
- num_images_per_prompt=num_images_per_prompt,
1110
- max_sequence_length=max_sequence_length,
1111
- mask_main_prompt_influence=mask_main_prompt_influence,
1112
- symmetric_masking=symmetric_masking,
1113
- delete_main_prompt=delete_main_prompt,
1114
- attention_rules=attention_rules,
1115
- return_attention_mask=not enable_flex_attn,
1116
- )
1117
-
1118
- total_text_len = prompt_embeds.shape[1]
1119
-
1120
-
1121
- indices_map = None
1122
- total_seq_len = 0
1123
-
1124
- if enable_flex_attn and region_guidance is not None:
1125
- self.set_attn_processor(FluxFlexAttentionProcessor)
1126
-
1127
- if num_patches == 0:
1128
- grid_h = height // self.vae_scale_factor // 2
1129
- grid_w = width // self.vae_scale_factor // 2
1130
- num_patches = grid_h * grid_w
1131
-
1132
- total_seq_len = total_text_len + 2 * num_patches
1133
-
1134
- # Construct indices_map for Flux (Text + Image + Image_Copy)
1135
- indices_map = {}
1136
-
1137
- # Text Components
1138
- current_txt_idx = 0
1139
- if not delete_main_prompt and prompt_len > 0:
1140
- indices_map['Main Prompt'] = list(range(prompt_len))
1141
- current_txt_idx = prompt_len
1142
-
1143
- # Hints
1144
- for i, h_len in enumerate(hint_lens):
1145
- indices_map[f'Hint {i+1}'] = list(range(current_txt_idx, current_txt_idx + h_len))
1146
- current_txt_idx += h_len
1147
-
1148
- # Image Components
1149
- img_start_idx = total_text_len
1150
- all_bbox_patches = set()
1151
- for indices in image_patch_indices_list:
1152
- all_bbox_patches.update(indices)
1153
-
1154
- all_patches = set(range(num_patches))
1155
- bg_patches = list(all_patches - all_bbox_patches)
1156
-
1157
- # Helper to add image indices (Copy 1 and Copy 2)
1158
- def get_combined_indices(patch_indices):
1159
- res = []
1160
- for p in patch_indices:
1161
- res.append(img_start_idx + p)
1162
- res.append(img_start_idx + num_patches + p)
1163
- return res
1164
-
1165
- def get_noise_indices(patch_indices):
1166
- return [img_start_idx + p for p in patch_indices]
1167
-
1168
- def get_image_indices(patch_indices):
1169
- return [img_start_idx + num_patches + p for p in patch_indices]
1170
-
1171
- indices_map['Background'] = get_combined_indices(bg_patches)
1172
- indices_map['Noise Background'] = get_noise_indices(bg_patches)
1173
- indices_map['Image Background'] = get_image_indices(bg_patches)
1174
-
1175
- for i, indices in enumerate(image_patch_indices_list):
1176
- indices_map[f'BBox {i+1}'] = get_combined_indices(indices)
1177
- indices_map[f'Noise BBox {i+1}'] = get_noise_indices(indices)
1178
- indices_map[f'Image BBox {i+1}'] = get_image_indices(indices)
1179
-
1180
- block_mask = create_flex_block_mask(
1181
- indices_map=indices_map,
1182
- total_seq_len=total_seq_len,
1183
- attention_rules=attention_rules,
1184
- device=device,
1185
- use_bitmask=flex_attn_use_bitmask
1186
- )
1187
- self._joint_attention_kwargs["flex_block_mask"] = block_mask
1188
- else:
1189
- self.set_attn_processor(FluxAttnProcessor)
1190
-
1191
- # 3. Preprocess image
1192
- if image is not None and not (isinstance(image, torch.Tensor) and image.size(1) == self.latent_channels):
1193
- image = self.image_processor.resize(image, height, width)
1194
- image = self.image_processor.preprocess(image, height, width)
1195
-
1196
- # 4. Prepare latent variables
1197
- num_channels_latents = self.transformer.config.in_channels // 4
1198
- latents, image_latents, latent_ids, image_ids = self.prepare_latents(
1199
- image,
1200
- batch_size * num_images_per_prompt,
1201
- num_channels_latents,
1202
- height,
1203
- width,
1204
- prompt_embeds.dtype,
1205
- device,
1206
- generator,
1207
- latents,
1208
- )
1209
- if image_ids is not None:
1210
- latent_ids = torch.cat([latent_ids, image_ids], dim=0) # dim 0 is sequence dimension
1211
-
1212
- # 5. Prepare timesteps
1213
- sigmas = np.linspace(1.0, 1 / num_inference_steps, num_inference_steps) if sigmas is None else sigmas
1214
- image_seq_len = latents.shape[1]
1215
- mu = calculate_shift(
1216
- image_seq_len,
1217
- self.scheduler.config.get("base_image_seq_len", 256),
1218
- self.scheduler.config.get("max_image_seq_len", 4096),
1219
- self.scheduler.config.get("base_shift", 0.5),
1220
- self.scheduler.config.get("max_shift", 1.15),
1221
- )
1222
- timesteps, num_inference_steps = retrieve_timesteps(
1223
- self.scheduler,
1224
- num_inference_steps,
1225
- device,
1226
- sigmas=sigmas,
1227
- mu=mu,
1228
- )
1229
- num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
1230
- self._num_timesteps = len(timesteps)
1231
-
1232
- # handle guidance
1233
- if self.transformer.config.guidance_embeds:
1234
- guidance = torch.full([1], guidance_scale, device=device, dtype=torch.float32)
1235
- guidance = guidance.expand(latents.shape[0])
1236
- else:
1237
- guidance = None
1238
-
1239
- if (ip_adapter_image is not None or ip_adapter_image_embeds is not None) and (
1240
- negative_ip_adapter_image is None and negative_ip_adapter_image_embeds is None
1241
- ):
1242
- negative_ip_adapter_image = np.zeros((width, height, 3), dtype=np.uint8)
1243
- negative_ip_adapter_image = [negative_ip_adapter_image] * self.transformer.encoder_hid_proj.num_ip_adapters
1244
-
1245
- elif (ip_adapter_image is None and ip_adapter_image_embeds is None) and (
1246
- negative_ip_adapter_image is not None or negative_ip_adapter_image_embeds is not None
1247
- ):
1248
- ip_adapter_image = np.zeros((width, height, 3), dtype=np.uint8)
1249
- ip_adapter_image = [ip_adapter_image] * self.transformer.encoder_hid_proj.num_ip_adapters
1250
-
1251
- if self.joint_attention_kwargs is None:
1252
- self._joint_attention_kwargs = {}
1253
-
1254
- image_embeds = None
1255
- negative_image_embeds = None
1256
- if ip_adapter_image is not None or ip_adapter_image_embeds is not None:
1257
- image_embeds = self.prepare_ip_adapter_image_embeds(
1258
- ip_adapter_image,
1259
- ip_adapter_image_embeds,
1260
- device,
1261
- batch_size * num_images_per_prompt,
1262
- )
1263
- if negative_ip_adapter_image is not None or negative_ip_adapter_image_embeds is not None:
1264
- negative_image_embeds = self.prepare_ip_adapter_image_embeds(
1265
- negative_ip_adapter_image,
1266
- negative_ip_adapter_image_embeds,
1267
- device,
1268
- batch_size * num_images_per_prompt,
1269
- )
1270
-
1271
- # 6. Denoising loop
1272
- # We set the index here to remove DtoH sync, helpful especially during compilation.
1273
- # Check out more details here: https://github.com/huggingface/diffusers/pull/11696
1274
- self.scheduler.set_begin_index(0)
1275
-
1276
- active_rules = attention_rules
1277
-
1278
- with self.progress_bar(total=num_inference_steps) as progress_bar:
1279
- for i, t in enumerate(timesteps):
1280
- if self.interrupt:
1281
- continue
1282
-
1283
- # Check for step-specific attention rules
1284
- current_rules = step_attention_rules.get(i, attention_rules)
1285
- if current_rules is not active_rules:
1286
- if enable_flex_attn and region_guidance is not None and indices_map is not None:
1287
- block_mask = create_flex_block_mask(
1288
- indices_map=indices_map,
1289
- total_seq_len=total_seq_len,
1290
- attention_rules=current_rules,
1291
- device=device,
1292
- use_bitmask=flex_attn_use_bitmask
1293
- )
1294
- self._joint_attention_kwargs["flex_block_mask"] = block_mask
1295
- elif region_guidance is not None:
1296
- attention_mask = self._create_attention_mask(
1297
- current_rules,
1298
- num_patches,
1299
- total_text_len,
1300
- prompt_len,
1301
- hint_lens,
1302
- image_patch_indices_list,
1303
- batch_size,
1304
- num_images_per_prompt,
1305
- device,
1306
- mask_main_prompt_influence,
1307
- symmetric_masking,
1308
- delete_main_prompt,
1309
- )
1310
- self._joint_attention_kwargs["attention_mask"] = attention_mask
1311
- active_rules = current_rules
1312
-
1313
- self._current_timestep = t
1314
- if image_embeds is not None:
1315
- self._joint_attention_kwargs["ip_adapter_image_embeds"] = image_embeds
1316
-
1317
- if attention_mask is not None:
1318
- self._joint_attention_kwargs["attention_mask"] = attention_mask
1319
-
1320
- latent_model_input = latents
1321
- if image_latents is not None:
1322
- latent_model_input = torch.cat([latents, image_latents], dim=1)
1323
- timestep = t.expand(latents.shape[0]).to(latents.dtype)
1324
-
1325
- noise_pred = self.transformer(
1326
- hidden_states=latent_model_input,
1327
- timestep=timestep / 1000,
1328
- guidance=guidance,
1329
- pooled_projections=pooled_prompt_embeds,
1330
- encoder_hidden_states=prompt_embeds,
1331
- txt_ids=text_ids,
1332
- img_ids=latent_ids,
1333
- joint_attention_kwargs=self.joint_attention_kwargs,
1334
- return_dict=False,
1335
- )[0]
1336
- noise_pred = noise_pred[:, : latents.size(1)]
1337
-
1338
- if do_true_cfg:
1339
- if negative_image_embeds is not None:
1340
- self._joint_attention_kwargs["ip_adapter_image_embeds"] = negative_image_embeds
1341
- neg_noise_pred = self.transformer(
1342
- hidden_states=latent_model_input,
1343
- timestep=timestep / 1000,
1344
- guidance=guidance,
1345
- pooled_projections=negative_pooled_prompt_embeds,
1346
- encoder_hidden_states=negative_prompt_embeds,
1347
- txt_ids=negative_text_ids,
1348
- img_ids=latent_ids,
1349
- joint_attention_kwargs=self.joint_attention_kwargs,
1350
- return_dict=False,
1351
- )[0]
1352
- neg_noise_pred = neg_noise_pred[:, : latents.size(1)]
1353
- noise_pred = neg_noise_pred + true_cfg_scale * (noise_pred - neg_noise_pred)
1354
-
1355
- # compute the previous noisy sample x_t -> x_t-1
1356
- latents_dtype = latents.dtype
1357
- latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
1358
-
1359
- if latents.dtype != latents_dtype:
1360
- if torch.backends.mps.is_available():
1361
- # some platforms (eg. apple mps) misbehave due to a pytorch bug: https://github.com/pytorch/pytorch/pull/99272
1362
- latents = latents.to(latents_dtype)
1363
-
1364
- if callback_on_step_end is not None:
1365
- callback_kwargs = {}
1366
- for k in callback_on_step_end_tensor_inputs:
1367
- callback_kwargs[k] = locals()[k]
1368
- callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
1369
-
1370
- latents = callback_outputs.pop("latents", latents)
1371
- prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
1372
-
1373
- # call the callback, if provided
1374
- if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
1375
- progress_bar.update()
1376
-
1377
- if XLA_AVAILABLE:
1378
- xm.mark_step()
1379
-
1380
- self._current_timestep = None
1381
-
1382
- if output_type == "latent":
1383
- raise NotImplementedError("Latent output is not supported")
1384
- image = latents
1385
- else:
1386
- latents = self._unpack_latents(latents, height, width, self.vae_scale_factor)
1387
- latents = (latents / self.vae.config.scaling_factor) + self.vae.config.shift_factor
1388
- image = self.vae.decode(latents, return_dict=False)[0]
1389
- image = self.image_processor.postprocess(image, output_type=output_type)
1390
- image[0] = image[0].resize((original_width, original_height))
1391
-
1392
- # Offload all models
1393
- self.maybe_free_model_hooks()
1394
-
1395
- if not return_dict:
1396
- return (image,)
1397
-
1398
- return FluxPipelineOutput(images=image)
1399
-
1400
-
1401
- def prepare_latents(
1402
- self,
1403
- image: Optional[torch.Tensor],
1404
- batch_size: int,
1405
- num_channels_latents: int,
1406
- height: int,
1407
- width: int,
1408
- dtype: torch.dtype,
1409
- device: torch.device,
1410
- generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
1411
- latents: Optional[torch.Tensor] = None,
1412
- ):
1413
- if isinstance(generator, list) and len(generator) != batch_size:
1414
- raise ValueError(
1415
- f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
1416
- f" size of {batch_size}. Make sure the batch size matches the length of the generators."
1417
- )
1418
-
1419
- height = 2 * (int(height) // (self.vae_scale_factor * 2))
1420
- width = 2 * (int(width) // (self.vae_scale_factor * 2))
1421
- shape = (batch_size, num_channels_latents, height, width)
1422
-
1423
- image_latents = image_ids = None
1424
- if image is not None:
1425
- image = image.to(device=device, dtype=dtype)
1426
- if image.shape[1] != self.latent_channels:
1427
- image_latents = self._encode_vae_image(image=image, generator=generator)
1428
- else:
1429
- image_latents = image
1430
- if batch_size > image_latents.shape[0] and batch_size % image_latents.shape[0] == 0:
1431
- # expand init_latents for batch_size
1432
- additional_image_per_prompt = batch_size // image_latents.shape[0]
1433
- image_latents = torch.cat([image_latents] * additional_image_per_prompt, dim=0)
1434
- elif batch_size > image_latents.shape[0] and batch_size % image_latents.shape[0] != 0:
1435
- raise ValueError(
1436
- f"Cannot duplicate `image` of batch size {image_latents.shape[0]} to {batch_size} text prompts."
1437
- )
1438
- else:
1439
- image_latents = torch.cat([image_latents], dim=0)
1440
-
1441
- image_latent_height, image_latent_width = image_latents.shape[2:]
1442
- image_latents = self._pack_latents(
1443
- image_latents, batch_size, num_channels_latents, image_latent_height, image_latent_width
1444
- )
1445
- image_ids = self._prepare_latent_image_ids(
1446
- batch_size, image_latent_height // 2, image_latent_width // 2, device, dtype
1447
- )
1448
- # image ids are the same as latent ids with the first dimension set to 1 instead of 0
1449
- image_ids[..., 0] = 1
1450
-
1451
- latent_ids = self._prepare_latent_image_ids(batch_size, height // 2, width // 2, device, dtype)
1452
-
1453
- if latents is None:
1454
- latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
1455
- latents = self._pack_latents(latents, batch_size, num_channels_latents, height, width)
1456
- else:
1457
- latents = latents.to(device=device, dtype=dtype)
1458
-
1459
-
1460
- return latents, image_latents, latent_ids, image_ids
1461
-
1462
- @staticmethod
1463
- # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline._prepare_latent_image_ids
1464
- def _prepare_latent_image_ids(batch_size, height, width, device, dtype):
1465
- latent_image_ids = torch.zeros(height, width, 3)
1466
- latent_image_ids[..., 1] = latent_image_ids[..., 1] + torch.arange(height)[:, None]
1467
- latent_image_ids[..., 2] = latent_image_ids[..., 2] + torch.arange(width)[None, :]
1468
-
1469
- latent_image_id_height, latent_image_id_width, latent_image_id_channels = latent_image_ids.shape
1470
-
1471
- latent_image_ids = latent_image_ids.reshape(
1472
- latent_image_id_height * latent_image_id_width, latent_image_id_channels
1473
- )
1474
- # print("latent_image_ids", latent_image_ids.shape)
1475
- return latent_image_ids.to(device=device, dtype=dtype)
1476
-
1477
-
1478
-
1479
- if __name__ == "__main__":
1480
- image_path = "assets/crowd.png"
1481
- image = Image.open(image_path).convert("RGB")
1482
- global_prompt = "keep remaining part of image unchanged."
1483
- region_guidance = [{"bbox_2d": [446, 98, 542, 356], "point_2d": [498, 180], "hint": "change the color of her shoes to red"}]
1484
-
1485
- pipeline = MultiRegionFluxKontextPipeline.from_pretrained(
1486
- "black-forest-labs/FLUX.1-Kontext-dev",
1487
- ).to(dtype=torch.bfloat16, device="cuda")
1488
-
1489
- attention_rules = generate_default_attention_rules(
1490
- region_guidance,
1491
- delete_main_prompt=False,
1492
- bboxes_attend_to_each_other=True,
1493
- has_image_prompt=False
1494
- )
1495
-
1496
- inputs = {
1497
- "image": image,
1498
- "prompt": global_prompt,
1499
- "guidance_scale": 2.5,
1500
- "region_guidance": region_guidance,
1501
- "attention_rules": attention_rules,
1502
- "delete_main_prompt": False,
1503
- "symmetric_masking": True,
1504
- "height": image.height,
1505
- "width": image.width,
1506
- "enable_flex_attn": True,
1507
- "flex_attn_use_bitmask": True,
1508
- }
1509
- image = pipeline(**inputs).images[0]
1510
- image.save("output_image.png")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
replan/pipelines/qwen_image.py DELETED
@@ -1,1557 +0,0 @@
1
- # Copyright 2025 Qwen-Image Team and The HuggingFace Team. All rights reserved.
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- import inspect
16
- import math
17
- from typing import Any, Callable, Dict, List, Optional, Union
18
- from PIL import Image
19
- import numpy as np
20
- import torch
21
- from transformers import Qwen2_5_VLForConditionalGeneration, Qwen2Tokenizer, Qwen2VLProcessor
22
-
23
- from diffusers.image_processor import PipelineImageInput, VaeImageProcessor
24
- from diffusers.loaders import QwenImageLoraLoaderMixin
25
- from diffusers.models import AutoencoderKLQwenImage
26
- from diffusers.schedulers import FlowMatchEulerDiscreteScheduler
27
- from diffusers.utils import is_torch_xla_available, logging, replace_example_docstring
28
- from diffusers.utils.torch_utils import randn_tensor
29
- from diffusers.pipelines.pipeline_utils import DiffusionPipeline
30
- from diffusers.pipelines.qwenimage.pipeline_output import QwenImagePipelineOutput
31
-
32
- from transformers.generation.utils import GenerationConfig
33
- from diffusers.models.transformers.transformer_qwenimage import QwenImageTransformer2DModel, QwenDoubleStreamAttnProcessor2_0
34
-
35
-
36
- from replan.pipelines.flex_attn import prepare_flex_attention_inputs, QwenFlexAttentionProcessor, create_flex_block_mask
37
- from replan.pipelines.replan import generate_default_attention_rules
38
-
39
- if is_torch_xla_available():
40
- import torch_xla.core.xla_model as xm
41
-
42
- XLA_AVAILABLE = True
43
- else:
44
- XLA_AVAILABLE = False
45
-
46
-
47
- logger = logging.get_logger(__name__) # pylint: disable=invalid-name
48
-
49
- EXAMPLE_DOC_STRING = """
50
- Examples:
51
- ```py
52
- >>> import torch
53
- >>> from PIL import Image
54
- >>> from diffusers import QwenImageEditPipeline
55
- >>> from diffusers.utils import load_image
56
-
57
- >>> pipe = QwenImageEditPipeline.from_pretrained("Qwen/Qwen-Image-Edit", torch_dtype=torch.bfloat16)
58
- >>> pipe.to("cuda")
59
- >>> image = load_image(
60
- ... "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/yarn-art-pikachu.png"
61
- ... ).convert("RGB")
62
- >>> prompt = (
63
- ... "Make Pikachu hold a sign that says 'Qwen Edit is awesome', yarn art style, detailed, vibrant colors"
64
- ... )
65
- >>> # Depending on the variant being used, the pipeline call will slightly vary.
66
- >>> # Refer to the pipeline documentation for more details.
67
- >>> image = pipe(image, prompt, num_inference_steps=50).images[0]
68
- >>> image.save("qwenimage_edit.png")
69
- ```
70
- """
71
-
72
-
73
- # Copied from diffusers.pipelines.qwenimage.pipeline_qwenimage.calculate_shift
74
- def calculate_shift(
75
- image_seq_len,
76
- base_seq_len: int = 256,
77
- max_seq_len: int = 4096,
78
- base_shift: float = 0.5,
79
- max_shift: float = 1.15,
80
- ):
81
- m = (max_shift - base_shift) / (max_seq_len - base_seq_len)
82
- b = base_shift - m * base_seq_len
83
- mu = image_seq_len * m + b
84
- return mu
85
-
86
-
87
- # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
88
- def retrieve_timesteps(
89
- scheduler,
90
- num_inference_steps: Optional[int] = None,
91
- device: Optional[Union[str, torch.device]] = None,
92
- timesteps: Optional[List[int]] = None,
93
- sigmas: Optional[List[float]] = None,
94
- **kwargs,
95
- ):
96
- r"""
97
- Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
98
- custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
99
-
100
- Args:
101
- scheduler (`SchedulerMixin`):
102
- The scheduler to get timesteps from.
103
- num_inference_steps (`int`):
104
- The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
105
- must be `None`.
106
- device (`str` or `torch.device`, *optional*):
107
- The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
108
- timesteps (`List[int]`, *optional*):
109
- Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
110
- `num_inference_steps` and `sigmas` must be `None`.
111
- sigmas (`List[float]`, *optional*):
112
- Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
113
- `num_inference_steps` and `timesteps` must be `None`.
114
-
115
- Returns:
116
- `Tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
117
- second element is the number of inference steps.
118
- """
119
- if timesteps is not None and sigmas is not None:
120
- raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
121
- if timesteps is not None:
122
- accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
123
- if not accepts_timesteps:
124
- raise ValueError(
125
- f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
126
- f" timestep schedules. Please check whether you are using the correct scheduler."
127
- )
128
- scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
129
- timesteps = scheduler.timesteps
130
- num_inference_steps = len(timesteps)
131
- elif sigmas is not None:
132
- accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
133
- if not accept_sigmas:
134
- raise ValueError(
135
- f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
136
- f" sigmas schedules. Please check whether you are using the correct scheduler."
137
- )
138
- scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
139
- timesteps = scheduler.timesteps
140
- num_inference_steps = len(timesteps)
141
- else:
142
- scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
143
- timesteps = scheduler.timesteps
144
- return timesteps, num_inference_steps
145
-
146
-
147
- # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img.retrieve_latents
148
- def retrieve_latents(
149
- encoder_output: torch.Tensor, generator: Optional[torch.Generator] = None, sample_mode: str = "sample"
150
- ):
151
- if hasattr(encoder_output, "latent_dist") and sample_mode == "sample":
152
- return encoder_output.latent_dist.sample(generator)
153
- elif hasattr(encoder_output, "latent_dist") and sample_mode == "argmax":
154
- return encoder_output.latent_dist.mode()
155
- elif hasattr(encoder_output, "latents"):
156
- return encoder_output.latents
157
- else:
158
- raise AttributeError("Could not access latents of provided encoder_output")
159
-
160
-
161
- def calculate_dimensions(target_area, ratio):
162
- width = math.sqrt(target_area * ratio)
163
- height = width / ratio
164
-
165
- width = round(width / 32) * 32
166
- height = round(height / 32) * 32
167
-
168
- return width, height, None
169
-
170
-
171
- class MultiRegionQwenImageEditPipeline(DiffusionPipeline, QwenImageLoraLoaderMixin):
172
- r"""
173
- The Qwen-Image-Edit pipeline for image editing.
174
-
175
- Args:
176
- transformer ([`QwenImageTransformer2DModel`]):
177
- Conditional Transformer (MMDiT) architecture to denoise the encoded image latents.
178
- scheduler ([`FlowMatchEulerDiscreteScheduler`]):
179
- A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
180
- vae ([`AutoencoderKL`]):
181
- Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representations.
182
- text_encoder ([`Qwen2.5-VL-7B-Instruct`]):
183
- [Qwen2.5-VL-7B-Instruct](https://huggingface.co/Qwen/Qwen2.5-VL-7B-Instruct), specifically the
184
- [Qwen2.5-VL-7B-Instruct](https://huggingface.co/Qwen/Qwen2.5-VL-7B-Instruct) variant.
185
- tokenizer (`QwenTokenizer`):
186
- Tokenizer of class
187
- [CLIPTokenizer](https://huggingface.co/docs/transformers/en/model_doc/clip#transformers.CLIPTokenizer).
188
- """
189
-
190
- model_cpu_offload_seq = "text_encoder->transformer->vae"
191
- _callback_tensor_inputs = ["latents", "prompt_embeds"]
192
-
193
- def __init__(
194
- self,
195
- scheduler: FlowMatchEulerDiscreteScheduler,
196
- vae: AutoencoderKLQwenImage,
197
- text_encoder: Qwen2_5_VLForConditionalGeneration,
198
- tokenizer: Qwen2Tokenizer,
199
- processor: Qwen2VLProcessor,
200
- transformer: QwenImageTransformer2DModel,
201
- ):
202
- super().__init__()
203
-
204
- self.register_modules(
205
- vae=vae,
206
- text_encoder=text_encoder,
207
- tokenizer=tokenizer,
208
- processor=processor,
209
- transformer=transformer,
210
- scheduler=scheduler,
211
- )
212
- self.vae_scale_factor = 2 ** len(self.vae.temperal_downsample) if getattr(self, "vae", None) else 8
213
- self.latent_channels = self.vae.config.z_dim if getattr(self, "vae", None) else 16
214
- # QwenImage latents are turned into 2x2 patches and packed. This means the latent width and height has to be divisible
215
- # by the patch size. So the vae scale factor is multiplied by the patch size to account for this
216
- self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor * 2)
217
- self.vl_processor = processor
218
- self.tokenizer_max_length = 1024
219
-
220
- self.prompt_template_encode = "<|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"
221
- self.prompt_template_encode_start_idx = 64
222
- self.default_sample_size = 128
223
-
224
- # Copied from diffusers.pipelines.qwenimage.pipeline_qwenimage.QwenImagePipeline._extract_masked_hidden
225
- def _extract_masked_hidden(self, hidden_states: torch.Tensor, mask: torch.Tensor):
226
- bool_mask = mask.bool()
227
- valid_lengths = bool_mask.sum(dim=1)
228
- selected = hidden_states[bool_mask]
229
- split_result = torch.split(selected, valid_lengths.tolist(), dim=0)
230
-
231
- return split_result
232
-
233
- def encode_prompts_batch(
234
- self,
235
- prompts: List[str],
236
- image: Optional[torch.Tensor] = None,
237
- device: Optional[torch.device] = None,
238
- num_images_per_prompt: int = 1,
239
- max_sequence_length: int = 1024,
240
- keep_image_tokens_flags: Optional[List[bool]] = None, # New parameter
241
- ):
242
- """
243
- Encode multiple prompts in a single batch forward pass.
244
-
245
- Args:
246
- prompts (`List[str]`): List of prompts to encode (can include main prompt and hints).
247
- image (`torch.Tensor`, *optional*): Image tensor for encoding.
248
- device (`torch.device`, *optional*): Device to use.
249
- num_images_per_prompt (`int`): Number of images per prompt.
250
- max_sequence_length (`int`): Maximum sequence length.
251
- keep_image_tokens_flags (`List[bool]`, *optional*):
252
- Flags for each prompt. True=keep image tokens (for main prompt), False=remove (for hints).
253
-
254
- Returns:
255
- Tuple: A tuple containing:
256
- - `prompt_embeds_list` (`List[torch.FloatTensor]`): List of embeddings for each prompt.
257
- - `prompt_embeds_mask_list` (`List[torch.FloatTensor]`): List of masks for each prompt.
258
- """
259
- device = device or self._execution_device
260
-
261
- # If not specified, default to keeping image tokens for all
262
- if keep_image_tokens_flags is None:
263
- keep_image_tokens_flags = [True] * len(prompts)
264
-
265
- all_embeds = []
266
- all_masks = []
267
- for i in range(len(prompts)):
268
- embeds, masks = self._get_qwen_prompt_embeds(
269
- prompt=prompts[i],
270
- image=image,
271
- device=device,
272
- keep_image_tokens=keep_image_tokens_flags[i], # Pass flag
273
- )
274
- all_embeds.append(embeds)
275
- all_masks.append(masks)
276
-
277
- return all_embeds, all_masks
278
-
279
- def _create_attention_mask(
280
- self,
281
- attention_rules,
282
- num_patches,
283
- total_text_len,
284
- prompt_len,
285
- hint_lens,
286
- image_patch_indices_list,
287
- main_prompt_indices,
288
- image_prompt_indices,
289
- batch_size,
290
- num_images_per_prompt,
291
- device,
292
- mask_main_prompt_influence=False,
293
- symmetric_masking=False,
294
- delete_main_prompt=False,
295
- ):
296
- total_seq_len = total_text_len + 2 * num_patches
297
- attention_mask = torch.zeros(
298
- num_images_per_prompt * batch_size,
299
- self.transformer.config.num_attention_heads,
300
- total_seq_len,
301
- total_seq_len,
302
- device=device,
303
- dtype=torch.bool,
304
- )
305
-
306
- if attention_rules:
307
- # 1. Define component indices
308
- num_regions = len(image_patch_indices_list)
309
-
310
- # Get indices for text components
311
- text_indices = {}
312
- text_indices['Main Prompt'] = main_prompt_indices
313
- if image_prompt_indices:
314
- text_indices['Image Prompt'] = image_prompt_indices
315
-
316
- hint_start_idx = prompt_len
317
- for i in range(num_regions):
318
- hint_len = hint_lens[i]
319
- text_indices[f'Hint {i+1}'] = list(range(hint_start_idx, hint_start_idx + hint_len))
320
- hint_start_idx += hint_len
321
-
322
- # Get indices for image patch components
323
- all_bbox_patches = set()
324
- for indices in image_patch_indices_list:
325
- all_bbox_patches.update(indices)
326
-
327
- all_patches = set(range(num_patches))
328
- bg_patches = all_patches - all_bbox_patches
329
-
330
- patch_indices = {}
331
- for i, indices in enumerate(image_patch_indices_list):
332
- patch_indices[f'BBox {i+1}'] = list(indices)
333
- patch_indices['Background'] = list(bg_patches)
334
-
335
- # Helper to get all indices for a component name
336
- def get_indices(comp_name):
337
- if comp_name in text_indices:
338
- return text_indices[comp_name]
339
-
340
- # Distinguish between Noise patches and Image patches
341
- if 'Noise' in comp_name:
342
- base_comp_name = comp_name.replace('Noise ', '')
343
- if base_comp_name in patch_indices:
344
- # Noise patches are in the first group after text
345
- return [total_text_len + i for i in patch_indices[base_comp_name]]
346
- elif 'Image' in comp_name:
347
- base_comp_name = comp_name.replace('Image ', '')
348
- if base_comp_name in patch_indices:
349
- # Image patches are in the second group
350
- return [total_text_len + num_patches + i for i in patch_indices[base_comp_name]]
351
- # Fallback for old component names (returns both noise and image)
352
- elif comp_name in patch_indices:
353
- noise_indices = [total_text_len + i for i in patch_indices[comp_name]]
354
- image_indices = [total_text_len + num_patches + i for i in patch_indices[comp_name]]
355
- return noise_indices + image_indices
356
-
357
- return []
358
-
359
- # 2. Populate attention_mask based on rules
360
- for (q_comp, k_comp), allowed in attention_rules.items():
361
- if allowed:
362
- q_indices = get_indices(q_comp)
363
- k_indices = get_indices(k_comp)
364
- if q_indices and k_indices:
365
- q_indices_tensor = torch.tensor(q_indices, device=device, dtype=torch.long)
366
- k_indices_tensor = torch.tensor(k_indices, device=device, dtype=torch.long)
367
- attention_mask[:, :, q_indices_tensor.view(-1, 1), k_indices_tensor.view(1, -1)] = True
368
-
369
- else:
370
- # Original attention mask logic
371
- # 1. Text-to-Text attention:
372
- # Main prompt attends to itself
373
- if prompt_len > 0:
374
- attention_mask[:, :, :prompt_len, :prompt_len] = True
375
- # Each hint attends to itself
376
- hint_start_idx = prompt_len
377
- for hint_len in hint_lens:
378
- attention_mask[:, :, hint_start_idx : hint_start_idx + hint_len, hint_start_idx : hint_start_idx + hint_len] = True
379
- hint_start_idx += hint_len
380
-
381
- # 2. Patch-to-Patch attention: all patches attend to each other (both noise and image)
382
- attention_mask[:, :, total_text_len:, total_text_len:] = True
383
-
384
- # 3. Patch-to-Text attention (Region Guidance)
385
- if not mask_main_prompt_influence and prompt_len > 0:
386
- # All patches attend to the main prompt
387
- attention_mask[:, :, total_text_len:, :prompt_len] = True
388
-
389
- # Specific patch regions attend to their corresponding hints (apply to both noise and image patches)
390
- hint_start_idx = prompt_len
391
- for i, patch_indices in enumerate(image_patch_indices_list):
392
- hint_len = hint_lens[i]
393
- if patch_indices:
394
- for p_idx in patch_indices:
395
- # Noise patches
396
- attention_mask[:, :, total_text_len + p_idx, hint_start_idx : hint_start_idx + hint_len] = True
397
- # Image patches
398
- attention_mask[:, :, total_text_len + num_patches + p_idx, hint_start_idx : hint_start_idx + hint_len] = True
399
- hint_start_idx += hint_len
400
-
401
- # 4. (Optional) Symmetric Text-to-Patch attention
402
- if symmetric_masking:
403
- # Main prompt attends to all patches
404
- if not mask_main_prompt_influence and prompt_len > 0:
405
- attention_mask[:, :, :prompt_len, total_text_len:] = True
406
-
407
- # Regional hints attend to their corresponding patches (both noise and image)
408
- hint_start_idx = prompt_len
409
- for i, patch_indices in enumerate(image_patch_indices_list):
410
- hint_len = hint_lens[i]
411
- if patch_indices:
412
- for p_idx in patch_indices:
413
- # Noise patches
414
- attention_mask[:, :, hint_start_idx : hint_start_idx + hint_len, total_text_len + p_idx] = True
415
- # Image patches
416
- attention_mask[:, :, hint_start_idx : hint_start_idx + hint_len, total_text_len + num_patches + p_idx] = True
417
- hint_start_idx += hint_len
418
- else:
419
- # Main prompt attends to all image patches
420
- if not mask_main_prompt_influence:
421
- attention_mask[:, :, :prompt_len, total_text_len:] = True
422
-
423
- # All Regional hints attend to ALL image patches
424
- # hints range: [prompt_len : total_text_len]
425
- # image patches range: [total_text_len : end]
426
- attention_mask[:, :, prompt_len:total_text_len, total_text_len:] = True
427
-
428
- return attention_mask
429
-
430
- def process_region_guidance(
431
- self,
432
- prompt_embeds: torch.FloatTensor,
433
- prompt_embeds_mask: torch.FloatTensor,
434
- region_guidance: List[Dict],
435
- width: int, # resized width
436
- height: int, # resized height
437
- original_height: int,
438
- original_width: int,
439
- dtype: torch.dtype,
440
- device: torch.device,
441
- num_images_per_prompt: Optional[int] = 1,
442
- max_sequence_length: Optional[int] = 1024,
443
- mask_main_prompt_influence: bool = False,
444
- delete_main_prompt: bool = False,
445
- symmetric_masking: bool = False,
446
- attention_rules: Optional[Dict] = None,
447
- prompt_image: Optional[torch.Tensor] = None,
448
- main_prompt: Optional[Union[str, List[str]]] = None, # Add main_prompt parameter
449
- return_attention_mask: bool = True,
450
- ):
451
- """
452
- Processes regional guidance to generate combined text embeddings and a self-attention mask.
453
-
454
- This function takes a list of regional guidance specifications (bounding boxes and text hints)
455
- and integrates them with the main prompt. It computes patch indices corresponding to each bounding
456
- box and constructs a custom self-attention mask for a sequence structured as `[text, image]`.
457
-
458
- Args:
459
- prompt_embeds (`torch.FloatTensor`): Embeddings for the main prompt (can be ignored if main_prompt is provided).
460
- prompt_embeds_mask (`torch.FloatTensor`): Mask for the main prompt embeddings.
461
- region_guidance (`List[Dict]`): A list where each dict contains a 'bbox' and a 'hint'.
462
- width (`int`): The target width of the image being generated.
463
- height (`int`): The target height of the image being generated.
464
- original_height (`int`): The original height provided by the user.
465
- original_width (`int`): The original width provided by the user.
466
- dtype (`torch.dtype`): The data type for new tensors.
467
- device (`torch.device`): The device for new tensors.
468
- num_images_per_prompt (`int`, *optional*): Number of images per prompt. Defaults to 1.
469
- max_sequence_length (`int`, *optional*): Max sequence length for text encoder. Defaults to 1024.
470
- mask_main_prompt_influence (`bool`, *optional*): If True, prevents image patches from attending to the main prompt. Defaults to False.
471
- symmetric_masking (`bool`, *optional*): If True, makes the text-image attention mask symmetric. Defaults to False.
472
- attention_rules (`Dict`, *optional*): Custom attention rules dictionary.
473
- prompt_image (`torch.Tensor`, *optional*): Image tensor for encoding hints.
474
- main_prompt (`str` or `List[str]`, *optional*): Main prompt text from __call__. If provided, will batch encode with hints.
475
-
476
- Returns:
477
- Tuple: A tuple containing:
478
- - `final_prompt_embeds` (`torch.FloatTensor`): Concatenated embeddings of the main prompt and all hints.
479
- - `final_prompt_embeds_mask` (`torch.FloatTensor`): Concatenated mask.
480
- - `attention_mask` (`torch.FloatTensor`): The 4D self-attention mask for the combined sequence.
481
- - `prompt_len` (`int`): The sequence length of the original prompt.
482
- - `hint_lens` (`List[int]`): A list of sequence lengths for each hint.
483
- - `image_patch_indices_list` (`List[List[int]]`): List of patch indices for each region.
484
- """
485
- if prompt_embeds is not None:
486
- batch_size = prompt_embeds.shape[0] // num_images_per_prompt
487
- elif main_prompt is not None:
488
- if isinstance(main_prompt, str):
489
- batch_size = 1
490
- else:
491
- batch_size = len(main_prompt)
492
- else:
493
- batch_size = 1
494
-
495
- # Collect all prompts for batch encoding: [main_prompt] + [hint1, hint2, ...]
496
- all_prompts = []
497
- if main_prompt is not None:
498
- # Use provided main_prompt for batch encoding
499
- if isinstance(main_prompt, str):
500
- all_prompts.append(main_prompt)
501
- else:
502
- # If main_prompt is a list, assume it's a single batch item
503
- all_prompts.extend(main_prompt)
504
-
505
- # Add all hints
506
- hint_prompts = [guidance['hint'] for guidance in region_guidance]
507
- all_prompts.extend(hint_prompts)
508
-
509
- # Before calling encode_prompts_batch
510
-
511
- # Batch encode all prompts (main + hints) in one forward pass
512
- # Create keep_image_tokens_flags:
513
- # - main prompt: True (always keep image tokens)
514
- # - hints: False (remove image tokens, keep only text)
515
- if main_prompt is not None:
516
- num_main_prompts = 1 if isinstance(main_prompt, str) else len(main_prompt)
517
- else:
518
- num_main_prompts = 0
519
-
520
- keep_image_tokens_flags = [True] * num_main_prompts + [False] * len(region_guidance)
521
-
522
- all_embeds_list, all_masks_list = self.encode_prompts_batch(
523
- prompts=all_prompts,
524
- image=prompt_image,
525
- device=device,
526
- num_images_per_prompt=1, # Start with 1, will expand later
527
- max_sequence_length=max_sequence_length,
528
- keep_image_tokens_flags=keep_image_tokens_flags, # Pass flags
529
- )
530
-
531
- # Split results: first is main prompt, rest are hints
532
- if main_prompt is not None:
533
- main_prompt_count = 1 if isinstance(main_prompt, str) else len(main_prompt)
534
- main_embeds = all_embeds_list[0] if main_prompt_count == 1 else torch.cat(all_embeds_list[:main_prompt_count], dim=0)
535
- main_masks = all_masks_list[0] if main_prompt_count == 1 else torch.cat(all_masks_list[:main_prompt_count], dim=0)
536
- hint_embeds_list = all_embeds_list[main_prompt_count:]
537
- hint_masks_list = all_masks_list[main_prompt_count:]
538
- else:
539
- # Use the provided prompt_embeds
540
- main_embeds = prompt_embeds
541
- main_masks = prompt_embeds_mask
542
- hint_embeds_list = all_embeds_list
543
- hint_masks_list = all_masks_list
544
-
545
- # Handle batch_size and num_images_per_prompt expansion
546
- # Expand main prompt
547
- if main_prompt is not None:
548
- if batch_size > 1:
549
- main_embeds = main_embeds.repeat(batch_size, 1, 1)
550
- main_masks = main_masks.repeat(batch_size, 1)
551
- if num_images_per_prompt > 1:
552
- _, seq_len, hidden_dim = main_embeds.shape
553
- main_embeds = main_embeds.repeat(1, num_images_per_prompt, 1)
554
- main_embeds = main_embeds.view(batch_size * num_images_per_prompt, seq_len, hidden_dim)
555
- main_masks = main_masks.repeat(1, num_images_per_prompt)
556
- main_masks = main_masks.view(batch_size * num_images_per_prompt, -1)
557
-
558
- # Expand hints
559
- expanded_hint_embeds_list = []
560
- expanded_hint_masks_list = []
561
- for hint_embeds_i, hint_mask_i in zip(hint_embeds_list, hint_masks_list):
562
- if batch_size > 1:
563
- hint_embeds_i = hint_embeds_i.repeat(batch_size, 1, 1)
564
- hint_mask_i = hint_mask_i.repeat(batch_size, 1)
565
-
566
- if num_images_per_prompt > 1:
567
- _, seq_len, hidden_dim = hint_embeds_i.shape
568
- hint_embeds_i = hint_embeds_i.repeat(1, num_images_per_prompt, 1)
569
- hint_embeds_i = hint_embeds_i.view(batch_size * num_images_per_prompt, seq_len, hidden_dim)
570
- hint_mask_i = hint_mask_i.repeat(1, num_images_per_prompt)
571
- hint_mask_i = hint_mask_i.view(batch_size * num_images_per_prompt, -1)
572
-
573
- expanded_hint_embeds_list.append(hint_embeds_i)
574
- expanded_hint_masks_list.append(hint_mask_i)
575
-
576
- grid_h = height // self.vae_scale_factor // 2
577
- grid_w = width // self.vae_scale_factor // 2
578
- num_patches = grid_h * grid_w
579
-
580
- image_patch_indices_list = []
581
- for guidance in region_guidance:
582
- bbox = guidance["bbox"]
583
- x1, y1, x2, y2 = bbox
584
- scale_x = width / original_width
585
- scale_y = height / original_height
586
-
587
- start_col = int(math.floor(x1 * scale_x / self.vae_scale_factor / 2))
588
- end_col = int(math.ceil(x2 * scale_x / self.vae_scale_factor / 2))
589
- start_row = int(math.floor(y1 * scale_y / self.vae_scale_factor / 2))
590
- end_row = int(math.ceil(y2 * scale_y / self.vae_scale_factor / 2))
591
-
592
- start_col = max(0, start_col)
593
- end_col = min(grid_w, end_col)
594
- start_row = max(0, start_row)
595
- end_row = min(grid_h, end_row)
596
-
597
- patch_indices = []
598
- for r in range(start_row, end_row):
599
- for c in range(start_col, end_col):
600
- patch_indices.append(r * grid_w + c)
601
- image_patch_indices_list.append(patch_indices)
602
-
603
- if delete_main_prompt:
604
- final_prompt_embeds = torch.cat(expanded_hint_embeds_list, dim=1)
605
- final_prompt_embeds_mask = torch.cat(expanded_hint_masks_list, dim=1)
606
- prompt_len = 0
607
- else:
608
- final_prompt_embeds = torch.cat([main_embeds] + expanded_hint_embeds_list, dim=1)
609
- final_prompt_embeds_mask = torch.cat([main_masks] + expanded_hint_masks_list, dim=1)
610
- prompt_len = main_embeds.shape[1]
611
-
612
- hint_lens = [h.shape[1] for h in expanded_hint_embeds_list]
613
- total_text_len = final_prompt_embeds.shape[1]
614
-
615
- # Calculate Main Prompt and Image Prompt indices if applicable
616
- main_prompt_indices = list(range(prompt_len))
617
- image_prompt_indices = []
618
-
619
- if not delete_main_prompt and main_embeds is not None and prompt_image is not None and main_prompt is not None:
620
- # Logic to split Main Prompt and Image Prompt
621
- vision_start_id = self.tokenizer.convert_tokens_to_ids("<|vision_start|>")
622
- vision_end_id = self.tokenizer.convert_tokens_to_ids("<|vision_end|>")
623
-
624
- # Use the first prompt as reference
625
- template = self.prompt_template_encode
626
- drop_idx = self.prompt_template_encode_start_idx
627
- txt = template.format(main_prompt[0] if isinstance(main_prompt, list) else main_prompt)
628
- model_inputs = self.processor(
629
- text=[txt],
630
- images=prompt_image,
631
- padding=True,
632
- return_tensors="pt",
633
- ).to(device)
634
-
635
- input_ids_sample = model_inputs.input_ids[0]
636
- attention_mask_sample = model_inputs.attention_mask[0]
637
- input_ids_sample = input_ids_sample[attention_mask_sample.bool()]
638
-
639
- vision_start_idx = None
640
- vision_end_idx = None
641
- for j, token_id in enumerate(input_ids_sample):
642
- if token_id == vision_start_id:
643
- vision_start_idx = j
644
- elif token_id == vision_end_id and vision_start_idx is not None:
645
- vision_end_idx = j + 1
646
- break
647
-
648
- if vision_start_idx is not None and vision_end_idx is not None:
649
- vision_start_idx = max(0, vision_start_idx - drop_idx)
650
- vision_end_idx = max(0, vision_end_idx - drop_idx)
651
-
652
- image_prompt_indices = list(range(vision_start_idx, vision_end_idx))
653
- main_prompt_indices = list(range(0, vision_start_idx)) + list(range(vision_end_idx, prompt_len))
654
-
655
- if not return_attention_mask:
656
- return final_prompt_embeds, final_prompt_embeds_mask, None, prompt_len, hint_lens, image_patch_indices_list, main_prompt_indices, image_prompt_indices
657
-
658
- # Self-attention mask for [text, noise_patches, image_patches] sequence
659
- attention_mask = self._create_attention_mask(
660
- attention_rules,
661
- num_patches,
662
- total_text_len,
663
- prompt_len,
664
- hint_lens,
665
- image_patch_indices_list,
666
- main_prompt_indices,
667
- image_prompt_indices,
668
- batch_size,
669
- num_images_per_prompt,
670
- device,
671
- mask_main_prompt_influence,
672
- symmetric_masking,
673
- delete_main_prompt,
674
- )
675
-
676
- return final_prompt_embeds, final_prompt_embeds_mask, attention_mask, prompt_len, hint_lens, image_patch_indices_list, main_prompt_indices, image_prompt_indices
677
-
678
- def _get_qwen_prompt_embeds(
679
- self,
680
- prompt: Union[str, List[str]] = None,
681
- image: Optional[torch.Tensor] = None,
682
- device: Optional[torch.device] = None,
683
- dtype: Optional[torch.dtype] = None,
684
- keep_image_tokens: bool = True, # Default True - used for main prompt
685
- ):
686
- device = device or self._execution_device
687
- dtype = dtype or self.text_encoder.dtype
688
-
689
- prompt = [prompt] if isinstance(prompt, str) else prompt
690
-
691
- template = self.prompt_template_encode
692
- drop_idx = self.prompt_template_encode_start_idx
693
- txt = [template.format(e) for e in prompt]
694
- model_inputs = self.processor(
695
- text=txt,
696
- images=image,
697
- padding=True,
698
- return_tensors="pt",
699
- ).to(device)
700
-
701
- # All prompts keep image tokens during encoding (text encoder needs full context)
702
- outputs = self.text_encoder(
703
- input_ids=model_inputs.input_ids,
704
- attention_mask=model_inputs.attention_mask,
705
- pixel_values=model_inputs.pixel_values,
706
- image_grid_thw=model_inputs.image_grid_thw,
707
- output_hidden_states=True,
708
- )
709
-
710
- hidden_states = outputs.hidden_states[-1]
711
- split_hidden_states = self._extract_masked_hidden(hidden_states, model_inputs.attention_mask)
712
-
713
- # Only when keep_image_tokens=False (hints), remove hidden states corresponding to image tokens
714
- if not keep_image_tokens and image is not None:
715
- # Find image tokens positions
716
- vision_start_id = self.tokenizer.convert_tokens_to_ids("<|vision_start|>")
717
- vision_end_id = self.tokenizer.convert_tokens_to_ids("<|vision_end|>")
718
-
719
- new_split_hidden_states = []
720
- for i, (hidden, input_ids_sample) in enumerate(zip(split_hidden_states, model_inputs.input_ids)):
721
- # Keep only valid token ids (based on attention mask)
722
- input_ids_sample = input_ids_sample[model_inputs.attention_mask[i].bool()]
723
-
724
- # Find vision tokens range
725
- vision_start_idx = None
726
- vision_end_idx = None
727
- for j, token_id in enumerate(input_ids_sample):
728
- if token_id == vision_start_id:
729
- vision_start_idx = j
730
- elif token_id == vision_end_id and vision_start_idx is not None:
731
- vision_end_idx = j + 1 # Include vision_end token
732
- break
733
-
734
- # If vision tokens found, remove their corresponding hidden states
735
- if vision_start_idx is not None and vision_end_idx is not None:
736
- before_vision = hidden[:vision_start_idx]
737
- after_vision = hidden[vision_end_idx:]
738
- hidden = torch.cat([before_vision, after_vision], dim=0)
739
-
740
- new_split_hidden_states.append(hidden)
741
- split_hidden_states = new_split_hidden_states
742
-
743
- # drop_idx truncation (remove template prefix)
744
- split_hidden_states = [e[drop_idx:] for e in split_hidden_states]
745
- attn_mask_list = [torch.ones(e.size(0), dtype=torch.long, device=e.device) for e in split_hidden_states]
746
- max_seq_len = max([e.size(0) for e in split_hidden_states])
747
- prompt_embeds = torch.stack(
748
- [torch.cat([u, u.new_zeros(max_seq_len - u.size(0), u.size(1))]) for u in split_hidden_states]
749
- )
750
- encoder_attention_mask = torch.stack(
751
- [torch.cat([u, u.new_zeros(max_seq_len - u.size(0))]) for u in attn_mask_list]
752
- )
753
-
754
- prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
755
-
756
- return prompt_embeds, encoder_attention_mask
757
-
758
- def encode_prompt(
759
- self,
760
- prompt: Union[str, List[str]],
761
- image: Optional[torch.Tensor] = None,
762
- device: Optional[torch.device] = None,
763
- num_images_per_prompt: int = 1,
764
- prompt_embeds: Optional[torch.Tensor] = None,
765
- prompt_embeds_mask: Optional[torch.Tensor] = None,
766
- max_sequence_length: int = 1024,
767
- ):
768
- r"""
769
-
770
- Args:
771
- prompt (`str` or `List[str]`, *optional*):
772
- prompt to be encoded
773
- image (`torch.Tensor`, *optional*):
774
- image to be encoded
775
- device: (`torch.device`):
776
- torch device
777
- num_images_per_prompt (`int`):
778
- number of images that should be generated per prompt
779
- prompt_embeds (`torch.Tensor`, *optional*):
780
- Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
781
- provided, text embeddings will be generated from `prompt` input argument.
782
- """
783
- device = device or self._execution_device
784
-
785
- prompt = [prompt] if isinstance(prompt, str) else prompt
786
- batch_size = len(prompt) if prompt_embeds is None else prompt_embeds.shape[0]
787
-
788
- if prompt_embeds is None:
789
- prompt_embeds, prompt_embeds_mask = self._get_qwen_prompt_embeds(prompt, image, device)
790
-
791
- _, seq_len, _ = prompt_embeds.shape
792
- prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
793
- prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
794
- prompt_embeds_mask = prompt_embeds_mask.repeat(1, num_images_per_prompt, 1)
795
- prompt_embeds_mask = prompt_embeds_mask.view(batch_size * num_images_per_prompt, seq_len)
796
-
797
- return prompt_embeds, prompt_embeds_mask
798
-
799
- def check_inputs(
800
- self,
801
- prompt,
802
- height,
803
- width,
804
- negative_prompt=None,
805
- prompt_embeds=None,
806
- negative_prompt_embeds=None,
807
- prompt_embeds_mask=None,
808
- negative_prompt_embeds_mask=None,
809
- callback_on_step_end_tensor_inputs=None,
810
- max_sequence_length=None,
811
- ):
812
- if height % (self.vae_scale_factor * 2) != 0 or width % (self.vae_scale_factor * 2) != 0:
813
- logger.warning(
814
- f"`height` and `width` have to be divisible by {self.vae_scale_factor * 2} but are {height} and {width}. Dimensions will be resized accordingly"
815
- )
816
-
817
- if callback_on_step_end_tensor_inputs is not None and not all(
818
- k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
819
- ):
820
- raise ValueError(
821
- f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
822
- )
823
-
824
- if prompt is not None and prompt_embeds is not None:
825
- raise ValueError(
826
- f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
827
- " only forward one of the two."
828
- )
829
- elif prompt is None and prompt_embeds is None:
830
- raise ValueError(
831
- "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
832
- )
833
- elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
834
- raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
835
-
836
- if negative_prompt is not None and negative_prompt_embeds is not None:
837
- raise ValueError(
838
- f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:"
839
- f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
840
- )
841
-
842
- if prompt_embeds is not None and prompt_embeds_mask is None:
843
- raise ValueError(
844
- "If `prompt_embeds` are provided, `prompt_embeds_mask` also have to be passed. Make sure to generate `prompt_embeds_mask` from the same text encoder that was used to generate `prompt_embeds`."
845
- )
846
- if negative_prompt_embeds is not None and negative_prompt_embeds_mask is None:
847
- raise ValueError(
848
- "If `negative_prompt_embeds` are provided, `negative_prompt_embeds_mask` also have to be passed. Make sure to generate `negative_prompt_embeds_mask` from the same text encoder that was used to generate `negative_prompt_embeds`."
849
- )
850
-
851
- if max_sequence_length is not None and max_sequence_length > 1024:
852
- raise ValueError(f"`max_sequence_length` cannot be greater than 1024 but is {max_sequence_length}")
853
-
854
- @staticmethod
855
- # Copied from diffusers.pipelines.qwenimage.pipeline_qwenimage.QwenImagePipeline._pack_latents
856
- def _pack_latents(latents, batch_size, num_channels_latents, height, width):
857
- latents = latents.view(batch_size, num_channels_latents, height // 2, 2, width // 2, 2)
858
- latents = latents.permute(0, 2, 4, 1, 3, 5)
859
- latents = latents.reshape(batch_size, (height // 2) * (width // 2), num_channels_latents * 4)
860
-
861
- return latents
862
-
863
- @staticmethod
864
- # Copied from diffusers.pipelines.qwenimage.pipeline_qwenimage.QwenImagePipeline._unpack_latents
865
- def _unpack_latents(latents, height, width, vae_scale_factor):
866
- batch_size, num_patches, channels = latents.shape
867
-
868
- # VAE applies 8x compression on images but we must also account for packing which requires
869
- # latent height and width to be divisible by 2.
870
- height = 2 * (int(height) // (vae_scale_factor * 2))
871
- width = 2 * (int(width) // (vae_scale_factor * 2))
872
-
873
- latents = latents.view(batch_size, height // 2, width // 2, channels // 4, 2, 2)
874
- latents = latents.permute(0, 3, 1, 4, 2, 5)
875
-
876
- latents = latents.reshape(batch_size, channels // (2 * 2), 1, height, width)
877
-
878
- return latents
879
-
880
- def _encode_vae_image(self, image: torch.Tensor, generator: torch.Generator):
881
- if isinstance(generator, list):
882
- image_latents = [
883
- retrieve_latents(self.vae.encode(image[i : i + 1]), generator=generator[i], sample_mode="argmax")
884
- for i in range(image.shape[0])
885
- ]
886
- image_latents = torch.cat(image_latents, dim=0)
887
- else:
888
- image_latents = retrieve_latents(self.vae.encode(image), generator=generator, sample_mode="argmax")
889
- latents_mean = (
890
- torch.tensor(self.vae.config.latents_mean)
891
- .view(1, self.latent_channels, 1, 1, 1)
892
- .to(image_latents.device, image_latents.dtype)
893
- )
894
- latents_std = (
895
- torch.tensor(self.vae.config.latents_std)
896
- .view(1, self.latent_channels, 1, 1, 1)
897
- .to(image_latents.device, image_latents.dtype)
898
- )
899
- image_latents = (image_latents - latents_mean) / latents_std
900
-
901
- return image_latents
902
-
903
- def enable_vae_slicing(self):
904
- r"""
905
- Enable sliced VAE decoding. When this option is enabled, the VAE will split the input tensor in slices to
906
- compute decoding in several steps. This is useful to save some memory and allow larger batch sizes.
907
- """
908
- self.vae.enable_slicing()
909
-
910
- def disable_vae_slicing(self):
911
- r"""
912
- Disable sliced VAE decoding. If `enable_vae_slicing` was previously enabled, this method will go back to
913
- computing decoding in one step.
914
- """
915
- self.vae.disable_slicing()
916
-
917
- def enable_vae_tiling(self):
918
- r"""
919
- Enable tiled VAE decoding. When this option is enabled, the VAE will split the input tensor into tiles to
920
- compute decoding and encoding in several steps. This is useful for saving a large amount of memory and to allow
921
- processing larger images.
922
- """
923
- self.vae.enable_tiling()
924
-
925
- def disable_vae_tiling(self):
926
- r"""
927
- Disable tiled VAE decoding. If `enable_vae_tiling` was previously enabled, this method will go back to
928
- computing decoding in one step.
929
- """
930
- self.vae.disable_tiling()
931
-
932
- def prepare_latents(
933
- self,
934
- image,
935
- batch_size,
936
- num_channels_latents,
937
- height,
938
- width,
939
- dtype,
940
- device,
941
- generator,
942
- latents=None,
943
- ):
944
- # VAE applies 8x compression on images but we must also account for packing which requires
945
- # latent height and width to be divisible by 2.
946
- height = 2 * (int(height) // (self.vae_scale_factor * 2))
947
- width = 2 * (int(width) // (self.vae_scale_factor * 2))
948
-
949
- shape = (batch_size, 1, num_channels_latents, height, width)
950
-
951
- image_latents = None
952
- if image is not None:
953
- image = image.to(device=device, dtype=dtype)
954
- if image.shape[1] != self.latent_channels:
955
- image_latents = self._encode_vae_image(image=image, generator=generator)
956
- else:
957
- image_latents = image
958
- if batch_size > image_latents.shape[0] and batch_size % image_latents.shape[0] == 0:
959
- # expand init_latents for batch_size
960
- additional_image_per_prompt = batch_size // image_latents.shape[0]
961
- image_latents = torch.cat([image_latents] * additional_image_per_prompt, dim=0)
962
- elif batch_size > image_latents.shape[0] and batch_size % image_latents.shape[0] != 0:
963
- raise ValueError(
964
- f"Cannot duplicate `image` of batch size {image_latents.shape[0]} to {batch_size} text prompts."
965
- )
966
- else:
967
- image_latents = torch.cat([image_latents], dim=0)
968
-
969
- image_latent_height, image_latent_width = image_latents.shape[3:]
970
- image_latents = self._pack_latents(
971
- image_latents, batch_size, num_channels_latents, image_latent_height, image_latent_width
972
- )
973
-
974
- if isinstance(generator, list) and len(generator) != batch_size:
975
- raise ValueError(
976
- f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
977
- f" size of {batch_size}. Make sure the batch size matches the length of the generators."
978
- )
979
- if latents is None:
980
- latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
981
- latents = self._pack_latents(latents, batch_size, num_channels_latents, height, width)
982
- else:
983
- latents = latents.to(device=device, dtype=dtype)
984
-
985
- return latents, image_latents
986
-
987
- @property
988
- def guidance_scale(self):
989
- return self._guidance_scale
990
-
991
- @property
992
- def attention_kwargs(self):
993
- return self._attention_kwargs
994
-
995
- @property
996
- def num_timesteps(self):
997
- return self._num_timesteps
998
-
999
- @property
1000
- def current_timestep(self):
1001
- return self._current_timestep
1002
-
1003
- @property
1004
- def interrupt(self):
1005
- return self._interrupt
1006
-
1007
- def set_attn_processor(self, attn_processor_class, **kwargs):
1008
- def fn_recursive_attn_processor(name: str, module: torch.nn.Module, processor_class, **kwargs):
1009
- if hasattr(module, "set_processor"):
1010
- module.set_processor(processor_class(**kwargs))
1011
-
1012
- for sub_name, child in module.named_children():
1013
- fn_recursive_attn_processor(f"{name}.{sub_name}", child, processor_class, **kwargs)
1014
-
1015
- fn_recursive_attn_processor("transformer", self.transformer, attn_processor_class, **kwargs)
1016
-
1017
- @torch.no_grad()
1018
- @replace_example_docstring(EXAMPLE_DOC_STRING)
1019
- def __call__(
1020
- self,
1021
- image: Optional[PipelineImageInput] = None,
1022
- prompt: Union[str, List[str]] = None,
1023
- negative_prompt: Union[str, List[str]] = None,
1024
- true_cfg_scale: float = 4.0,
1025
- height: Optional[int] = None,
1026
- width: Optional[int] = None,
1027
- num_inference_steps: int = 50,
1028
- sigmas: Optional[List[float]] = None,
1029
- guidance_scale: float = 1.0,
1030
- num_images_per_prompt: int = 1,
1031
- generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
1032
- latents: Optional[torch.Tensor] = None,
1033
- prompt_embeds: Optional[torch.Tensor] = None,
1034
- prompt_embeds_mask: Optional[torch.Tensor] = None,
1035
- negative_prompt_embeds: Optional[torch.Tensor] = None,
1036
- negative_prompt_embeds_mask: Optional[torch.Tensor] = None,
1037
- output_type: Optional[str] = "pil",
1038
- return_dict: bool = True,
1039
- attention_kwargs: Optional[Dict[str, Any]] = None,
1040
- callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
1041
- callback_on_step_end_tensor_inputs: List[str] = ["latents"],
1042
- max_sequence_length: int = 512,
1043
- region_guidance: Optional[List[Dict]] = None, # [{'bbox': [x1, y1, x2, y2], 'hint': 'xxx'}, ...]
1044
- mask_main_prompt_influence: bool = False,
1045
- symmetric_masking: bool = False,
1046
- delete_main_prompt: bool = False,
1047
- attention_rules: Optional[Dict] = None,
1048
- enable_flex_attn: bool = True,
1049
- flex_attn_use_bitmask: bool = True,
1050
- ):
1051
- r"""
1052
- Function invoked when calling the pipeline for generation.
1053
-
1054
- Args:
1055
- prompt (`str` or `List[str]`, *optional*):
1056
- The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
1057
- instead.
1058
- negative_prompt (`str` or `List[str]`, *optional*):
1059
- The prompt or prompts not to guide the image generation. If not defined, one has to pass
1060
- `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `true_cfg_scale` is
1061
- not greater than `1`).
1062
- true_cfg_scale (`float`, *optional*, defaults to 1.0):
1063
- When > 1.0 and a provided `negative_prompt`, enables true classifier-free guidance.
1064
- height (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
1065
- The height in pixels of the generated image. This is set to 1024 by default for the best results.
1066
- width (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
1067
- The width in pixels of the generated image. This is set to 1024 by default for the best results.
1068
- num_inference_steps (`int`, *optional*, defaults to 50):
1069
- The number of denoising steps. More denoising steps usually lead to a higher quality image at the
1070
- expense of slower inference.
1071
- sigmas (`List[float]`, *optional*):
1072
- Custom sigmas to use for the denoising process with schedulers which support a `sigmas` argument in
1073
- their `set_timesteps` method. If not defined, the default behavior when `num_inference_steps` is passed
1074
- will be used.
1075
- guidance_scale (`float`, *optional*, defaults to 3.5):
1076
- Guidance scale as defined in [Classifier-Free Diffusion
1077
- Guidance](https://huggingface.co/papers/2207.12598). `guidance_scale` is defined as `w` of equation 2.
1078
- of [Imagen Paper](https://huggingface.co/papers/2205.11487). Guidance scale is enabled by setting
1079
- `guidance_scale > 1`. Higher guidance scale encourages to generate images that are closely linked to
1080
- the text `prompt`, usually at the expense of lower image quality.
1081
-
1082
- This parameter in the pipeline is there to support future guidance-distilled models when they come up.
1083
- Note that passing `guidance_scale` to the pipeline is ineffective. To enable classifier-free guidance,
1084
- please pass `true_cfg_scale` and `negative_prompt` (even an empty negative prompt like " ") should
1085
- enable classifier-free guidance computations.
1086
- num_images_per_prompt (`int`, *optional*, defaults to 1):
1087
- The number of images to generate per prompt.
1088
- generator (`torch.Generator` or `List[torch.Generator]`, *optional*):
1089
- One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)
1090
- to make generation deterministic.
1091
- latents (`torch.Tensor`, *optional*):
1092
- Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image
1093
- generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
1094
- tensor will be generated by sampling using the supplied random `generator`.
1095
- prompt_embeds (`torch.Tensor`, *optional*):
1096
- Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
1097
- provided, text embeddings will be generated from `prompt` input argument.
1098
- negative_prompt_embeds (`torch.Tensor`, *optional*):
1099
- Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
1100
- weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
1101
- argument.
1102
- output_type (`str`, *optional*, defaults to `"pil"`):
1103
- The output format of the generate image. Choose between
1104
- [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`.
1105
- return_dict (`bool`, *optional*, defaults to `True`):
1106
- Whether or not to return a [`~pipelines.qwenimage.QwenImagePipelineOutput`] instead of a plain tuple.
1107
- attention_kwargs (`dict`, *optional*):
1108
- A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under
1109
- `self.processor` in
1110
- [diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
1111
- callback_on_step_end (`Callable`, *optional*):
1112
- A function that calls at the end of each denoising steps during the inference. The function is called
1113
- with the following arguments: `callback_on_step_end(self: DiffusionPipeline, step: int, timestep: int,
1114
- callback_kwargs: Dict)`. `callback_kwargs` will include a list of all tensors as specified by
1115
- `callback_on_step_end_tensor_inputs`.
1116
- callback_on_step_end_tensor_inputs (`List`, *optional*):
1117
- The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
1118
- will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
1119
- `._callback_tensor_inputs` attribute of your pipeline class.
1120
- max_sequence_length (`int` defaults to 512): Maximum sequence length to use with the `prompt`.
1121
- region_guidance (`List[Dict]`, *optional*): Regional guidance specifications.
1122
- visualize_attention_path (`str`, *optional*): Path to save attention mask visualization.
1123
- mask_main_prompt_influence (`bool`, *optional*): If True, prevents image patches from attending to main prompt.
1124
- symmetric_masking (`bool`, *optional*): If True, makes text-image attention symmetric.
1125
- delete_main_prompt (`bool`, *optional*): If True, removes main prompt from attention.
1126
- attention_rules (`Dict`, *optional*): Custom attention rules dictionary.
1127
-
1128
- Examples:
1129
-
1130
- Returns:
1131
- [`~pipelines.qwenimage.QwenImagePipelineOutput`] or `tuple`:
1132
- [`~pipelines.qwenimage.QwenImagePipelineOutput`] if `return_dict` is True, otherwise a `tuple`. When
1133
- returning a tuple, the first element is a list with the generated images.
1134
- """
1135
- image_size = image[0].size if isinstance(image, list) else image.size
1136
- original_height, original_width = height or image_size[1], width or image_size[0]
1137
- calculated_width, calculated_height, _ = calculate_dimensions(1024 * 1024, image_size[0] / image_size[1])
1138
- height = height or calculated_height
1139
- width = width or calculated_width
1140
-
1141
- multiple_of = self.vae_scale_factor * 2
1142
- width = width // multiple_of * multiple_of
1143
- height = height // multiple_of * multiple_of
1144
- image = image.resize((width, height))
1145
- calculated_width = width
1146
- calculated_height = height
1147
-
1148
-
1149
- # 1. Check inputs. Raise error if not correct
1150
- self.check_inputs(
1151
- prompt,
1152
- height,
1153
- width,
1154
- negative_prompt=negative_prompt,
1155
- prompt_embeds=prompt_embeds,
1156
- negative_prompt_embeds=negative_prompt_embeds,
1157
- prompt_embeds_mask=prompt_embeds_mask,
1158
- negative_prompt_embeds_mask=negative_prompt_embeds_mask,
1159
- callback_on_step_end_tensor_inputs=callback_on_step_end_tensor_inputs,
1160
- max_sequence_length=max_sequence_length,
1161
- )
1162
-
1163
- self._guidance_scale = guidance_scale
1164
- self._attention_kwargs = attention_kwargs
1165
- self._current_timestep = None
1166
- self._interrupt = False
1167
-
1168
- # 2. Define call parameters
1169
- if prompt is not None and isinstance(prompt, str):
1170
- batch_size = 1
1171
- elif prompt is not None and isinstance(prompt, list):
1172
- batch_size = len(prompt)
1173
- else:
1174
- batch_size = prompt_embeds.shape[0]
1175
-
1176
- device = self._execution_device
1177
- # 3. Preprocess image
1178
- if image is not None and not (isinstance(image, torch.Tensor) and image.size(1) == self.latent_channels):
1179
- image = self.image_processor.resize(image, calculated_height, calculated_width)
1180
- prompt_image = image
1181
- image = self.image_processor.preprocess(image, calculated_height, calculated_width)
1182
- image = image.unsqueeze(2)
1183
- else:
1184
- prompt_image = None
1185
-
1186
- has_neg_prompt = negative_prompt is not None or (
1187
- negative_prompt_embeds is not None and negative_prompt_embeds_mask is not None
1188
- )
1189
- do_true_cfg = true_cfg_scale > 1 and has_neg_prompt
1190
-
1191
- attention_mask = None
1192
- hint_lens = []
1193
- image_patch_indices_list = []
1194
- prompt_len = 0
1195
-
1196
- step_attention_rules = {}
1197
- has_image_prompt = prompt_image is not None
1198
- if attention_rules is None:
1199
- attention_rules = generate_default_attention_rules(region_guidance or [], delete_main_prompt=delete_main_prompt, bboxes_attend_to_each_other=True, has_image_prompt=has_image_prompt, symmetric_masking=symmetric_masking)
1200
- elif isinstance(attention_rules, dict) and any(isinstance(k, int) for k in attention_rules.keys()):
1201
- step_attention_rules = attention_rules
1202
- attention_rules = generate_default_attention_rules(region_guidance or [], delete_main_prompt=delete_main_prompt, bboxes_attend_to_each_other=True, has_image_prompt=has_image_prompt, symmetric_masking=symmetric_masking)
1203
-
1204
- if region_guidance:
1205
- # When region_guidance is provided, batch encode main prompt + hints together
1206
- # Skip the first encode_prompt call to avoid redundant encoding
1207
- prompt_embeds, prompt_embeds_mask, attention_mask, prompt_len, hint_lens, image_patch_indices_list, main_prompt_indices, image_prompt_indices = self.process_region_guidance(
1208
- prompt_embeds=None, # Not used when main_prompt is provided
1209
- prompt_embeds_mask=None, # Not used when main_prompt is provided
1210
- region_guidance=region_guidance,
1211
- width=width,
1212
- height=height,
1213
- original_height=original_height,
1214
- original_width=original_width,
1215
- dtype=self.text_encoder.dtype if self.text_encoder else torch.float32,
1216
- device=device,
1217
- num_images_per_prompt=num_images_per_prompt,
1218
- max_sequence_length=max_sequence_length,
1219
- mask_main_prompt_influence=mask_main_prompt_influence,
1220
- symmetric_masking=symmetric_masking,
1221
- delete_main_prompt=delete_main_prompt,
1222
- attention_rules=attention_rules,
1223
- prompt_image=prompt_image,
1224
- main_prompt=prompt, # Pass the original prompt to batch encode with hints
1225
- return_attention_mask=not enable_flex_attn,
1226
- )
1227
- else:
1228
- # When no region_guidance, encode prompt normally
1229
- prompt_embeds, prompt_embeds_mask = self.encode_prompt(
1230
- image=prompt_image,
1231
- prompt=prompt,
1232
- prompt_embeds=prompt_embeds,
1233
- prompt_embeds_mask=prompt_embeds_mask,
1234
- device=device,
1235
- num_images_per_prompt=num_images_per_prompt,
1236
- max_sequence_length=max_sequence_length,
1237
- )
1238
- prompt_len = prompt_embeds.shape[1]
1239
- # Initialize indices for consistency if needed or handle appropriately
1240
- main_prompt_indices = list(range(prompt_len))
1241
- image_prompt_indices = []
1242
-
1243
- total_text_len = prompt_embeds.shape[1]
1244
-
1245
-
1246
- indices_map = None
1247
- total_seq_len = 0
1248
-
1249
- if enable_flex_attn and region_guidance is not None:
1250
- self.set_attn_processor(QwenFlexAttentionProcessor)
1251
-
1252
- grid_h = height // self.vae_scale_factor // 2
1253
- grid_w = width // self.vae_scale_factor // 2
1254
- num_patches = grid_h * grid_w
1255
-
1256
- total_seq_len = total_text_len + 2 * num_patches
1257
-
1258
- # Construct indices_map
1259
- indices_map = {}
1260
-
1261
- # Text Components
1262
- if not delete_main_prompt:
1263
- indices_map['Main Prompt'] = main_prompt_indices
1264
-
1265
- if image_prompt_indices:
1266
- indices_map['Image Prompt'] = image_prompt_indices
1267
-
1268
- # Hints
1269
- current_txt_idx = prompt_len
1270
- for i, h_len in enumerate(hint_lens):
1271
- indices_map[f'Hint {i+1}'] = list(range(current_txt_idx, current_txt_idx + h_len))
1272
- current_txt_idx += h_len
1273
-
1274
- # Image Components
1275
- img_start_idx = total_text_len
1276
- all_bbox_patches = set()
1277
- for indices in image_patch_indices_list:
1278
- all_bbox_patches.update(indices)
1279
-
1280
- all_patches = set(range(num_patches))
1281
- bg_patches = list(all_patches - all_bbox_patches)
1282
-
1283
- def get_combined_indices(patch_indices):
1284
- res = []
1285
- for p in patch_indices:
1286
- res.append(img_start_idx + p)
1287
- res.append(img_start_idx + num_patches + p)
1288
- return res
1289
-
1290
- def get_noise_indices(patch_indices):
1291
- return [img_start_idx + p for p in patch_indices]
1292
-
1293
- def get_image_indices(patch_indices):
1294
- return [img_start_idx + num_patches + p for p in patch_indices]
1295
-
1296
- indices_map['Background'] = get_combined_indices(bg_patches)
1297
- indices_map['Noise Background'] = get_noise_indices(bg_patches)
1298
- indices_map['Image Background'] = get_image_indices(bg_patches)
1299
-
1300
- for i, indices in enumerate(image_patch_indices_list):
1301
- indices_map[f'BBox {i+1}'] = get_combined_indices(indices)
1302
- indices_map[f'Noise BBox {i+1}'] = get_noise_indices(indices)
1303
- indices_map[f'Image BBox {i+1}'] = get_image_indices(indices)
1304
-
1305
- block_mask = create_flex_block_mask(
1306
- indices_map=indices_map,
1307
- total_seq_len=total_seq_len,
1308
- attention_rules=attention_rules,
1309
- device=device,
1310
- use_bitmask=flex_attn_use_bitmask
1311
- )
1312
-
1313
- if self.attention_kwargs is None:
1314
- self._attention_kwargs = {}
1315
- self._attention_kwargs["flex_block_mask"] = block_mask
1316
- else:
1317
- self.set_attn_processor(QwenDoubleStreamAttnProcessor2_0)
1318
-
1319
- if do_true_cfg:
1320
- negative_prompt_embeds, negative_prompt_embeds_mask = self.encode_prompt(
1321
- image=prompt_image,
1322
- prompt=negative_prompt,
1323
- prompt_embeds=negative_prompt_embeds,
1324
- prompt_embeds_mask=negative_prompt_embeds_mask,
1325
- device=device,
1326
- num_images_per_prompt=num_images_per_prompt,
1327
- max_sequence_length=max_sequence_length,
1328
- )
1329
-
1330
- # 4. Prepare latent variables
1331
- num_channels_latents = self.transformer.config.in_channels // 4
1332
- latents, image_latents = self.prepare_latents(
1333
- image,
1334
- batch_size * num_images_per_prompt,
1335
- num_channels_latents,
1336
- height,
1337
- width,
1338
- prompt_embeds.dtype,
1339
- device,
1340
- generator,
1341
- latents,
1342
- )
1343
-
1344
- img_shapes = [
1345
- [
1346
- (1, height // self.vae_scale_factor // 2, width // self.vae_scale_factor // 2),
1347
- (1, calculated_height // self.vae_scale_factor // 2, calculated_width // self.vae_scale_factor // 2),
1348
- ]
1349
- ] * batch_size
1350
-
1351
- # 5. Prepare timesteps
1352
- sigmas = np.linspace(1.0, 1 / num_inference_steps, num_inference_steps) if sigmas is None else sigmas
1353
- image_seq_len = latents.shape[1]
1354
- mu = calculate_shift(
1355
- image_seq_len,
1356
- self.scheduler.config.get("base_image_seq_len", 256),
1357
- self.scheduler.config.get("max_image_seq_len", 4096),
1358
- self.scheduler.config.get("base_shift", 0.5),
1359
- self.scheduler.config.get("max_shift", 1.15),
1360
- )
1361
- timesteps, num_inference_steps = retrieve_timesteps(
1362
- self.scheduler,
1363
- num_inference_steps,
1364
- device,
1365
- sigmas=sigmas,
1366
- mu=mu,
1367
- )
1368
- num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
1369
- self._num_timesteps = len(timesteps)
1370
-
1371
- # handle guidance
1372
- if self.transformer.config.guidance_embeds:
1373
- guidance = torch.full([1], guidance_scale, device=device, dtype=torch.float32)
1374
- guidance = guidance.expand(latents.shape[0])
1375
- else:
1376
- guidance = None
1377
-
1378
- if self.attention_kwargs is None:
1379
- self._attention_kwargs = {}
1380
-
1381
- txt_seq_lens = prompt_embeds_mask.sum(dim=1).tolist() if prompt_embeds_mask is not None else None
1382
- negative_txt_seq_lens = (
1383
- negative_prompt_embeds_mask.sum(dim=1).tolist() if negative_prompt_embeds_mask is not None else None
1384
- )
1385
-
1386
- # 6. Denoising loop
1387
- self.scheduler.set_begin_index(0)
1388
- active_rules = attention_rules
1389
-
1390
- with self.progress_bar(total=num_inference_steps) as progress_bar:
1391
- for i, t in enumerate(timesteps):
1392
- if self.interrupt:
1393
- continue
1394
-
1395
- # Check for step-specific attention rules
1396
- current_rules = step_attention_rules.get(i, attention_rules)
1397
- if current_rules is not active_rules:
1398
- if enable_flex_attn and region_guidance is not None and indices_map is not None:
1399
- block_mask = create_flex_block_mask(
1400
- indices_map=indices_map,
1401
- total_seq_len=total_seq_len,
1402
- attention_rules=current_rules,
1403
- device=device,
1404
- use_bitmask=flex_attn_use_bitmask
1405
- )
1406
- if self.attention_kwargs is None:
1407
- self._attention_kwargs = {}
1408
- self._attention_kwargs["flex_block_mask"] = block_mask
1409
- elif region_guidance is not None:
1410
- attention_mask = self._create_attention_mask(
1411
- current_rules,
1412
- num_patches,
1413
- total_text_len,
1414
- prompt_len,
1415
- hint_lens,
1416
- image_patch_indices_list,
1417
- main_prompt_indices,
1418
- image_prompt_indices,
1419
- batch_size,
1420
- num_images_per_prompt,
1421
- device,
1422
- mask_main_prompt_influence,
1423
- symmetric_masking,
1424
- delete_main_prompt,
1425
- )
1426
- active_rules = current_rules
1427
-
1428
- self._current_timestep = t
1429
-
1430
- latent_model_input = latents
1431
- if image_latents is not None:
1432
- latent_model_input = torch.cat([latents, image_latents], dim=1)
1433
-
1434
- # broadcast to batch dimension in a way that's compatible with ONNX/Core ML
1435
- timestep = t.expand(latents.shape[0]).to(latents.dtype)
1436
-
1437
- # Prepare attention kwargs with region mask if available
1438
- current_attention_kwargs = self.attention_kwargs.copy() if self.attention_kwargs else {}
1439
- if attention_mask is not None:
1440
- current_attention_kwargs["attention_mask"] = attention_mask
1441
- with self.transformer.cache_context("cond"):
1442
- noise_pred = self.transformer(
1443
- hidden_states=latent_model_input,
1444
- timestep=timestep / 1000,
1445
- guidance=guidance,
1446
- encoder_hidden_states_mask=prompt_embeds_mask,
1447
- encoder_hidden_states=prompt_embeds,
1448
- img_shapes=img_shapes,
1449
- txt_seq_lens=txt_seq_lens,
1450
- attention_kwargs=current_attention_kwargs,
1451
- return_dict=False,
1452
- )[0]
1453
- noise_pred = noise_pred[:, : latents.size(1)]
1454
-
1455
- if do_true_cfg:
1456
- with self.transformer.cache_context("uncond"):
1457
- neg_noise_pred = self.transformer(
1458
- hidden_states=latent_model_input,
1459
- timestep=timestep / 1000,
1460
- guidance=guidance,
1461
- encoder_hidden_states_mask=negative_prompt_embeds_mask,
1462
- encoder_hidden_states=negative_prompt_embeds,
1463
- img_shapes=img_shapes,
1464
- txt_seq_lens=negative_txt_seq_lens,
1465
- attention_kwargs={},
1466
- return_dict=False,
1467
- )[0]
1468
- neg_noise_pred = neg_noise_pred[:, : latents.size(1)]
1469
- comb_pred = neg_noise_pred + true_cfg_scale * (noise_pred - neg_noise_pred)
1470
-
1471
- cond_norm = torch.norm(noise_pred, dim=-1, keepdim=True)
1472
- noise_norm = torch.norm(comb_pred, dim=-1, keepdim=True)
1473
- noise_pred = comb_pred * (cond_norm / noise_norm)
1474
-
1475
- # compute the previous noisy sample x_t -> x_t-1
1476
- latents_dtype = latents.dtype
1477
- latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
1478
-
1479
- if latents.dtype != latents_dtype:
1480
- if torch.backends.mps.is_available():
1481
- # some platforms (eg. apple mps) misbehave due to a pytorch bug: https://github.com/pytorch/pytorch/pull/99272
1482
- latents = latents.to(latents_dtype)
1483
-
1484
- if callback_on_step_end is not None:
1485
- callback_kwargs = {}
1486
- for k in callback_on_step_end_tensor_inputs:
1487
- callback_kwargs[k] = locals()[k]
1488
- callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
1489
-
1490
- latents = callback_outputs.pop("latents", latents)
1491
- prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
1492
-
1493
- # call the callback, if provided
1494
- if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
1495
- progress_bar.update()
1496
-
1497
- if XLA_AVAILABLE:
1498
- xm.mark_step()
1499
-
1500
- self._current_timestep = None
1501
- if output_type == "latent":
1502
- image = latents
1503
- else:
1504
- latents = self._unpack_latents(latents, height, width, self.vae_scale_factor)
1505
- latents = latents.to(self.vae.dtype)
1506
- latents_mean = (
1507
- torch.tensor(self.vae.config.latents_mean)
1508
- .view(1, self.vae.config.z_dim, 1, 1, 1)
1509
- .to(latents.device, latents.dtype)
1510
- )
1511
- latents_std = 1.0 / torch.tensor(self.vae.config.latents_std).view(1, self.vae.config.z_dim, 1, 1, 1).to(
1512
- latents.device, latents.dtype
1513
- )
1514
- latents = latents / latents_std + latents_mean
1515
- image = self.vae.decode(latents, return_dict=False)[0][:, :, 0]
1516
- image = self.image_processor.postprocess(image, output_type=output_type)
1517
-
1518
- # Offload all models
1519
- self.maybe_free_model_hooks()
1520
-
1521
- if not return_dict:
1522
- return (image,)
1523
-
1524
- return QwenImagePipelineOutput(images=[img.resize((original_width, original_height)) for img in image])
1525
-
1526
-
1527
-
1528
- if __name__ == "__main__":
1529
- image_path = "assets/crowd.png"
1530
- image = Image.open(image_path).convert("RGB")
1531
- global_prompt = "keep remaining part of image unchanged."
1532
- region_guidance = [{"bbox_2d": [446, 98, 542, 356], "point_2d": [498, 180], "hint": "change the color of her shoes to red"}]
1533
- pipeline = MultiRegionQwenImageEditPipeline.from_pretrained("Qwen/Qwen-Image-Edit")
1534
-
1535
- pipeline.to(torch.bfloat16)
1536
- pipeline.to("cuda")
1537
-
1538
- attention_rules = generate_default_attention_rules(
1539
- region_guidance,
1540
- delete_main_prompt=False,
1541
- bboxes_attend_to_each_other=True,
1542
- has_image_prompt=True,
1543
- symmetric_masking=False
1544
- )
1545
-
1546
- inputs = {
1547
- "image": image,
1548
- "prompt": global_prompt,
1549
- "generator": torch.manual_seed(0),
1550
- "true_cfg_scale": 4.0,
1551
- "negative_prompt": " ",
1552
- "num_inference_steps": 50,
1553
- "region_guidance": region_guidance,
1554
- "attention_rules": attention_rules,
1555
- }
1556
- image = pipeline(**inputs).images[0]
1557
- image.save("output_image.png")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
replan/pipelines/qwen_image_plus.py DELETED
@@ -1,1501 +0,0 @@
1
- # Copyright 2025 Qwen-Image Team and The HuggingFace Team. All rights reserved.
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- import inspect
16
- import math
17
- from typing import Any, Callable, Dict, List, Optional, Tuple, Union
18
-
19
- import numpy as np
20
- import torch
21
- from transformers import Qwen2_5_VLForConditionalGeneration, Qwen2Tokenizer, Qwen2VLProcessor
22
-
23
- from diffusers.image_processor import PipelineImageInput, VaeImageProcessor
24
- from diffusers.loaders import QwenImageLoraLoaderMixin
25
- from diffusers.models import AutoencoderKLQwenImage
26
- from diffusers.models.transformers.transformer_qwenimage import QwenDoubleStreamAttnProcessor2_0, QwenImageTransformer2DModel
27
- from diffusers.schedulers import FlowMatchEulerDiscreteScheduler
28
- from diffusers.utils import is_torch_xla_available, logging, replace_example_docstring
29
- from diffusers.utils.torch_utils import randn_tensor
30
- from diffusers.pipelines.pipeline_utils import DiffusionPipeline
31
- from diffusers.pipelines.qwenimage.pipeline_output import QwenImagePipelineOutput
32
-
33
- from replan.pipelines.flex_attn import QwenFlexAttentionProcessor, create_flex_block_mask
34
- from replan.pipelines.replan import generate_default_attention_rules
35
-
36
-
37
- if is_torch_xla_available():
38
- import torch_xla.core.xla_model as xm
39
-
40
- XLA_AVAILABLE = True
41
- else:
42
- XLA_AVAILABLE = False
43
-
44
-
45
- logger = logging.get_logger(__name__) # pylint: disable=invalid-name
46
-
47
- EXAMPLE_DOC_STRING = """
48
- Examples:
49
- ```py
50
- >>> import torch
51
- >>> from PIL import Image
52
- >>> from diffusers import QwenImageEditPlusPipeline
53
- >>> from diffusers.utils import load_image
54
-
55
- >>> pipe = QwenImageEditPlusPipeline.from_pretrained("Qwen/Qwen-Image-Edit-2509", torch_dtype=torch.bfloat16)
56
- >>> pipe.to("cuda")
57
- >>> image = load_image(
58
- ... "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/yarn-art-pikachu.png"
59
- ... ).convert("RGB")
60
- >>> prompt = (
61
- ... "Make Pikachu hold a sign that says 'Qwen Edit is awesome', yarn art style, detailed, vibrant colors"
62
- ... )
63
- >>> # Depending on the variant being used, the pipeline call will slightly vary.
64
- >>> # Refer to the pipeline documentation for more details.
65
- >>> image = pipe(image, prompt, num_inference_steps=50).images[0]
66
- >>> image.save("qwenimage_edit_plus.png")
67
- ```
68
- """
69
-
70
- CONDITION_IMAGE_SIZE = 384 * 384
71
- VAE_IMAGE_SIZE = 1024 * 1024
72
-
73
-
74
- # Copied from diffusers.pipelines.qwenimage.pipeline_qwenimage.calculate_shift
75
- def calculate_shift(
76
- image_seq_len,
77
- base_seq_len: int = 256,
78
- max_seq_len: int = 4096,
79
- base_shift: float = 0.5,
80
- max_shift: float = 1.15,
81
- ):
82
- m = (max_shift - base_shift) / (max_seq_len - base_seq_len)
83
- b = base_shift - m * base_seq_len
84
- mu = image_seq_len * m + b
85
- return mu
86
-
87
-
88
- # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
89
- def retrieve_timesteps(
90
- scheduler,
91
- num_inference_steps: Optional[int] = None,
92
- device: Optional[Union[str, torch.device]] = None,
93
- timesteps: Optional[List[int]] = None,
94
- sigmas: Optional[List[float]] = None,
95
- **kwargs,
96
- ):
97
- r"""
98
- Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
99
- custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
100
-
101
- Args:
102
- scheduler (`SchedulerMixin`):
103
- The scheduler to get timesteps from.
104
- num_inference_steps (`int`):
105
- The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
106
- must be `None`.
107
- device (`str` or `torch.device`, *optional*):
108
- The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
109
- timesteps (`List[int]`, *optional*):
110
- Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
111
- `num_inference_steps` and `sigmas` must be `None`.
112
- sigmas (`List[float]`, *optional*):
113
- Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
114
- `num_inference_steps` and `timesteps` must be `None`.
115
-
116
- Returns:
117
- `Tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
118
- second element is the number of inference steps.
119
- """
120
- if timesteps is not None and sigmas is not None:
121
- raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
122
- if timesteps is not None:
123
- accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
124
- if not accepts_timesteps:
125
- raise ValueError(
126
- f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
127
- f" timestep schedules. Please check whether you are using the correct scheduler."
128
- )
129
- scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
130
- timesteps = scheduler.timesteps
131
- num_inference_steps = len(timesteps)
132
- elif sigmas is not None:
133
- accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
134
- if not accept_sigmas:
135
- raise ValueError(
136
- f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
137
- f" sigmas schedules. Please check whether you are using the correct scheduler."
138
- )
139
- scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
140
- timesteps = scheduler.timesteps
141
- num_inference_steps = len(timesteps)
142
- else:
143
- scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
144
- timesteps = scheduler.timesteps
145
- return timesteps, num_inference_steps
146
-
147
-
148
- # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img.retrieve_latents
149
- def retrieve_latents(
150
- encoder_output: torch.Tensor, generator: Optional[torch.Generator] = None, sample_mode: str = "sample"
151
- ):
152
- if hasattr(encoder_output, "latent_dist") and sample_mode == "sample":
153
- return encoder_output.latent_dist.sample(generator)
154
- elif hasattr(encoder_output, "latent_dist") and sample_mode == "argmax":
155
- return encoder_output.latent_dist.mode()
156
- elif hasattr(encoder_output, "latents"):
157
- return encoder_output.latents
158
- else:
159
- raise AttributeError("Could not access latents of provided encoder_output")
160
-
161
-
162
- def calculate_dimensions(target_area, ratio):
163
- width = math.sqrt(target_area * ratio)
164
- height = width / ratio
165
-
166
- width = round(width / 32) * 32
167
- height = round(height / 32) * 32
168
-
169
- return width, height
170
-
171
-
172
- class QwenImageEditPlusPipeline(DiffusionPipeline, QwenImageLoraLoaderMixin):
173
- r"""
174
- The Qwen-Image-Edit pipeline for image editing.
175
-
176
- Args:
177
- transformer ([`QwenImageTransformer2DModel`]):
178
- Conditional Transformer (MMDiT) architecture to denoise the encoded image latents.
179
- scheduler ([`FlowMatchEulerDiscreteScheduler`]):
180
- A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
181
- vae ([`AutoencoderKL`]):
182
- Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representations.
183
- text_encoder ([`Qwen2.5-VL-7B-Instruct`]):
184
- [Qwen2.5-VL-7B-Instruct](https://huggingface.co/Qwen/Qwen2.5-VL-7B-Instruct), specifically the
185
- [Qwen2.5-VL-7B-Instruct](https://huggingface.co/Qwen/Qwen2.5-VL-7B-Instruct) variant.
186
- tokenizer (`QwenTokenizer`):
187
- Tokenizer of class
188
- [CLIPTokenizer](https://huggingface.co/docs/transformers/en/model_doc/clip#transformers.CLIPTokenizer).
189
- """
190
-
191
- model_cpu_offload_seq = "text_encoder->transformer->vae"
192
- _callback_tensor_inputs = ["latents", "prompt_embeds"]
193
-
194
- def __init__(
195
- self,
196
- scheduler: FlowMatchEulerDiscreteScheduler,
197
- vae: AutoencoderKLQwenImage,
198
- text_encoder: Qwen2_5_VLForConditionalGeneration,
199
- tokenizer: Qwen2Tokenizer,
200
- processor: Qwen2VLProcessor,
201
- transformer: QwenImageTransformer2DModel,
202
- ):
203
- super().__init__()
204
-
205
- self.register_modules(
206
- vae=vae,
207
- text_encoder=text_encoder,
208
- tokenizer=tokenizer,
209
- processor=processor,
210
- transformer=transformer,
211
- scheduler=scheduler,
212
- )
213
- self.vae_scale_factor = 2 ** len(self.vae.temperal_downsample) if getattr(self, "vae", None) else 8
214
- self.latent_channels = self.vae.config.z_dim if getattr(self, "vae", None) else 16
215
- # QwenImage latents are turned into 2x2 patches and packed. This means the latent width and height has to be divisible
216
- # by the patch size. So the vae scale factor is multiplied by the patch size to account for this
217
- self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor * 2)
218
- self.tokenizer_max_length = 1024
219
-
220
- self.prompt_template_encode = "<|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"
221
- self.prompt_template_encode_start_idx = 64
222
- self.default_sample_size = 128
223
-
224
- def set_attn_processor(self, attn_processor_class, **kwargs):
225
- """
226
- Recursively set the attention processor on all attention modules in the transformer.
227
- Mirrors the helper used in `qwen_image.py` so we can switch between SDPA/Flash and FlexAttention.
228
- """
229
-
230
- def fn_recursive_attn_processor(name: str, module: torch.nn.Module, processor_class, **kwargs):
231
- if hasattr(module, "set_processor"):
232
- module.set_processor(processor_class(**kwargs))
233
-
234
- for sub_name, child in module.named_children():
235
- fn_recursive_attn_processor(f"{name}.{sub_name}", child, processor_class, **kwargs)
236
-
237
- fn_recursive_attn_processor("transformer", self.transformer, attn_processor_class, **kwargs)
238
-
239
- # Copied from diffusers.pipelines.qwenimage.pipeline_qwenimage.QwenImagePipeline._extract_masked_hidden
240
- def _extract_masked_hidden(self, hidden_states: torch.Tensor, mask: torch.Tensor):
241
- bool_mask = mask.bool()
242
- valid_lengths = bool_mask.sum(dim=1)
243
- selected = hidden_states[bool_mask]
244
- split_result = torch.split(selected, valid_lengths.tolist(), dim=0)
245
-
246
- return split_result
247
-
248
- def _get_qwen_prompt_embeds(
249
- self,
250
- prompt: Union[str, List[str]] = None,
251
- image: Optional[torch.Tensor] = None,
252
- device: Optional[torch.device] = None,
253
- dtype: Optional[torch.dtype] = None,
254
- keep_image_tokens: bool = True,
255
- ):
256
- device = device or self._execution_device
257
- dtype = dtype or self.text_encoder.dtype
258
-
259
- prompt = [prompt] if isinstance(prompt, str) else prompt
260
- # For the main prompt, keep the existing "Picture i:" prefix (backwards compatible).
261
- # For regional hints, we keep the vision tokens (so VL encoder can condition on the image),
262
- # but drop the "Picture i:" text to avoid polluting hint tokens with extra words.
263
- img_prompt_template = (
264
- "Picture {}: <|vision_start|><|image_pad|><|vision_end|>" if keep_image_tokens else "<|vision_start|><|image_pad|><|vision_end|>"
265
- )
266
- if isinstance(image, list):
267
- base_img_prompt = ""
268
- for i, img in enumerate(image):
269
- if keep_image_tokens:
270
- base_img_prompt += img_prompt_template.format(i + 1)
271
- else:
272
- base_img_prompt += img_prompt_template
273
- elif image is not None:
274
- base_img_prompt = img_prompt_template.format(1) if keep_image_tokens else img_prompt_template
275
- else:
276
- base_img_prompt = ""
277
-
278
-
279
- template = self.prompt_template_encode
280
-
281
- drop_idx = self.prompt_template_encode_start_idx
282
- txt = [template.format(base_img_prompt + e) for e in prompt]
283
-
284
- model_inputs = self.processor(
285
- text=txt,
286
- images=image,
287
- padding=True,
288
- return_tensors="pt",
289
- ).to(device)
290
-
291
- outputs = self.text_encoder(
292
- input_ids=model_inputs.input_ids,
293
- attention_mask=model_inputs.attention_mask,
294
- pixel_values=model_inputs.pixel_values,
295
- image_grid_thw=model_inputs.image_grid_thw,
296
- output_hidden_states=True,
297
- )
298
-
299
- hidden_states = outputs.hidden_states[-1]
300
- split_hidden_states = self._extract_masked_hidden(hidden_states, model_inputs.attention_mask)
301
-
302
- if not keep_image_tokens and image is not None:
303
- # Remove hidden states for all vision token spans (<|vision_start|> ... <|vision_end|>).
304
- # This keeps only text tokens in the final prompt_embeds while still allowing the VL encoder
305
- # to use visual context during encoding.
306
- vision_start_id = self.tokenizer.convert_tokens_to_ids("<|vision_start|>")
307
- vision_end_id = self.tokenizer.convert_tokens_to_ids("<|vision_end|>")
308
-
309
- new_split_hidden_states: List[torch.Tensor] = []
310
- for i, (hidden, input_ids_sample) in enumerate(zip(split_hidden_states, model_inputs.input_ids)):
311
- input_ids_sample = input_ids_sample[model_inputs.attention_mask[i].bool()]
312
-
313
- # Collect all [start, end) spans to drop.
314
- spans: List[Tuple[int, int]] = []
315
- j = 0
316
- while j < input_ids_sample.shape[0]:
317
- if int(input_ids_sample[j]) == vision_start_id:
318
- k = j + 1
319
- while k < input_ids_sample.shape[0] and int(input_ids_sample[k]) != vision_end_id:
320
- k += 1
321
- if k < input_ids_sample.shape[0] and int(input_ids_sample[k]) == vision_end_id:
322
- spans.append((j, k + 1)) # include vision_end
323
- j = k + 1
324
- continue
325
- j += 1
326
-
327
- if spans:
328
- # Drop spans from hidden.
329
- kept_chunks = []
330
- last = 0
331
- for s, e in spans:
332
- if s > last:
333
- kept_chunks.append(hidden[last:s])
334
- last = e
335
- if last < hidden.shape[0]:
336
- kept_chunks.append(hidden[last:])
337
- hidden = torch.cat(kept_chunks, dim=0) if kept_chunks else hidden[:0]
338
-
339
- new_split_hidden_states.append(hidden)
340
- split_hidden_states = new_split_hidden_states
341
-
342
- split_hidden_states = [e[drop_idx:] for e in split_hidden_states]
343
- attn_mask_list = [torch.ones(e.size(0), dtype=torch.long, device=e.device) for e in split_hidden_states]
344
- max_seq_len = max([e.size(0) for e in split_hidden_states])
345
- prompt_embeds = torch.stack(
346
- [torch.cat([u, u.new_zeros(max_seq_len - u.size(0), u.size(1))]) for u in split_hidden_states]
347
- )
348
- encoder_attention_mask = torch.stack(
349
- [torch.cat([u, u.new_zeros(max_seq_len - u.size(0))]) for u in attn_mask_list]
350
- )
351
-
352
- prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
353
-
354
- return prompt_embeds, encoder_attention_mask
355
-
356
- def encode_prompts_batch(
357
- self,
358
- prompts: List[str],
359
- image: Optional[torch.Tensor] = None,
360
- device: Optional[torch.device] = None,
361
- max_sequence_length: int = 1024,
362
- keep_image_tokens_flags: Optional[List[bool]] = None,
363
- ):
364
- """
365
- Encode multiple prompts (main prompt + hints) reusing the same VL encoder.
366
- This matches `qwen_image.py`'s batching behavior but is specialized for EditPlus.
367
- """
368
- device = device or self._execution_device
369
- if keep_image_tokens_flags is None:
370
- keep_image_tokens_flags = [True] * len(prompts)
371
-
372
- all_embeds = []
373
- all_masks = []
374
- print(prompts)
375
- for p, keep_img in zip(prompts, keep_image_tokens_flags):
376
- embeds, masks = self._get_qwen_prompt_embeds(
377
- prompt=p,
378
- image=image,
379
- device=device,
380
- keep_image_tokens=keep_img,
381
- )
382
- all_embeds.append(embeds)
383
- all_masks.append(masks)
384
-
385
- return all_embeds, all_masks
386
-
387
- def _bbox_to_patch_indices(
388
- self,
389
- bbox: List[float],
390
- *,
391
- target_width: int,
392
- target_height: int,
393
- original_width: int,
394
- original_height: int,
395
- grid_w: int,
396
- grid_h: int,
397
- ) -> List[int]:
398
- x1, y1, x2, y2 = bbox
399
- scale_x = target_width / float(original_width)
400
- scale_y = target_height / float(original_height)
401
-
402
- start_col = int(math.floor(x1 * scale_x / self.vae_scale_factor / 2))
403
- end_col = int(math.ceil(x2 * scale_x / self.vae_scale_factor / 2))
404
- start_row = int(math.floor(y1 * scale_y / self.vae_scale_factor / 2))
405
- end_row = int(math.ceil(y2 * scale_y / self.vae_scale_factor / 2))
406
-
407
- start_col = max(0, start_col)
408
- end_col = min(grid_w, end_col)
409
- start_row = max(0, start_row)
410
- end_row = min(grid_h, end_row)
411
-
412
- patch_indices = []
413
- for r in range(start_row, end_row):
414
- for c in range(start_col, end_col):
415
- patch_indices.append(r * grid_w + c)
416
- return patch_indices
417
-
418
- def process_region_guidance(
419
- self,
420
- *,
421
- main_prompt: Union[str, List[str]],
422
- prompt_image: Optional[torch.Tensor],
423
- region_guidance: List[Dict],
424
- width: int,
425
- height: int,
426
- original_width: int,
427
- original_height: int,
428
- device: torch.device,
429
- num_images_per_prompt: int,
430
- max_sequence_length: int,
431
- delete_main_prompt: bool,
432
- mask_main_prompt_influence: bool,
433
- symmetric_masking: bool,
434
- attention_rules: Optional[Dict],
435
- enable_flex_attn: bool,
436
- flex_attn_use_bitmask: bool,
437
- # Patch layout for image latents stream: (num_noise_patches, [num_img_patches_i...], main_image_idx)
438
- num_noise_patches: int,
439
- num_img_patches_list: List[int],
440
- main_image_idx: int,
441
- ):
442
- """
443
- Build concatenated prompt embeddings for [main prompt + hints] and prepare region guidance
444
- masks for the joint sequence [text, noise_patches, cond_image_patches...].
445
-
446
- Returns:
447
- final_prompt_embeds, final_prompt_embeds_mask, attention_mask_or_None, attention_kwargs_update
448
- """
449
- # ---- 1) Encode main prompt + hints in one go (main keeps image tokens; hints drop them) ----
450
- hint_prompts = [g["hint"] for g in region_guidance]
451
- if isinstance(main_prompt, str):
452
- all_prompts = [main_prompt] + hint_prompts
453
- num_main = 1
454
- else:
455
- # EditPlus enforces batch_size==1 in __call__; still accept list for API symmetry.
456
- all_prompts = list(main_prompt) + hint_prompts
457
- num_main = len(main_prompt)
458
-
459
- keep_image_tokens_flags = [True] * num_main + [False] * len(hint_prompts)
460
- all_embeds_list, all_masks_list = self.encode_prompts_batch(
461
- prompts=all_prompts,
462
- image=prompt_image,
463
- device=device,
464
- max_sequence_length=max_sequence_length,
465
- keep_image_tokens_flags=keep_image_tokens_flags,
466
- )
467
-
468
- main_embeds = all_embeds_list[0] if num_main == 1 else torch.cat(all_embeds_list[:num_main], dim=0)
469
- main_masks = all_masks_list[0] if num_main == 1 else torch.cat(all_masks_list[:num_main], dim=0)
470
- hint_embeds_list = all_embeds_list[num_main:]
471
- hint_masks_list = all_masks_list[num_main:]
472
-
473
- # Expand to num_images_per_prompt
474
- batch_size = 1
475
- _, main_seq_len, main_hidden = main_embeds.shape
476
- main_embeds = main_embeds.repeat(1, num_images_per_prompt, 1).view(batch_size * num_images_per_prompt, main_seq_len, main_hidden)
477
- main_masks = main_masks.repeat(1, num_images_per_prompt).view(batch_size * num_images_per_prompt, -1)
478
-
479
- expanded_hint_embeds_list = []
480
- expanded_hint_masks_list = []
481
- for he, hm in zip(hint_embeds_list, hint_masks_list):
482
- _, h_seq_len, h_hidden = he.shape
483
- he = he.repeat(1, num_images_per_prompt, 1).view(batch_size * num_images_per_prompt, h_seq_len, h_hidden)
484
- hm = hm.repeat(1, num_images_per_prompt).view(batch_size * num_images_per_prompt, -1)
485
- expanded_hint_embeds_list.append(he)
486
- expanded_hint_masks_list.append(hm)
487
-
488
- if delete_main_prompt:
489
- final_prompt_embeds = torch.cat(expanded_hint_embeds_list, dim=1) if expanded_hint_embeds_list else main_embeds[:, :0]
490
- final_prompt_embeds_mask = torch.cat(expanded_hint_masks_list, dim=1) if expanded_hint_masks_list else main_masks[:, :0]
491
- prompt_len = 0
492
- main_prompt_indices: List[int] = []
493
- image_prompt_indices: List[int] = []
494
- else:
495
- final_prompt_embeds = torch.cat([main_embeds] + expanded_hint_embeds_list, dim=1)
496
- final_prompt_embeds_mask = torch.cat([main_masks] + expanded_hint_masks_list, dim=1)
497
- prompt_len = main_embeds.shape[1]
498
-
499
- # Split "Main Prompt" vs "Image Prompt" token indices (optional, improves rule expressiveness).
500
- main_prompt_indices = list(range(prompt_len))
501
- image_prompt_indices = []
502
- if prompt_image is not None:
503
- vision_start_id = self.tokenizer.convert_tokens_to_ids("<|vision_start|>")
504
- vision_end_id = self.tokenizer.convert_tokens_to_ids("<|vision_end|>")
505
- drop_idx = self.prompt_template_encode_start_idx
506
- template = self.prompt_template_encode
507
- # Use the first prompt as reference.
508
- txt = template.format((main_prompt[0] if isinstance(main_prompt, list) else main_prompt))
509
- model_inputs = self.processor(
510
- text=[txt],
511
- images=prompt_image,
512
- padding=True,
513
- return_tensors="pt",
514
- ).to(device)
515
- input_ids_sample = model_inputs.input_ids[0]
516
- attention_mask_sample = model_inputs.attention_mask[0]
517
- input_ids_sample = input_ids_sample[attention_mask_sample.bool()]
518
-
519
- spans = []
520
- j = 0
521
- while j < input_ids_sample.shape[0]:
522
- if int(input_ids_sample[j]) == vision_start_id:
523
- k = j + 1
524
- while k < input_ids_sample.shape[0] and int(input_ids_sample[k]) != vision_end_id:
525
- k += 1
526
- if k < input_ids_sample.shape[0] and int(input_ids_sample[k]) == vision_end_id:
527
- spans.append((j, k + 1))
528
- j = k + 1
529
- continue
530
- j += 1
531
-
532
- img_indices = []
533
- for s, e in spans:
534
- s2 = max(0, s - drop_idx)
535
- e2 = max(0, e - drop_idx)
536
- img_indices.extend(list(range(s2, min(e2, prompt_len))))
537
- if img_indices:
538
- image_prompt_indices = sorted(set([i for i in img_indices if 0 <= i < prompt_len]))
539
- main_prompt_indices = [i for i in range(prompt_len) if i not in set(image_prompt_indices)]
540
-
541
- hint_lens = [h.shape[1] for h in expanded_hint_embeds_list]
542
- total_text_len = final_prompt_embeds.shape[1]
543
-
544
- # IMPORTANT: Keep text length consistent with what the transformer will consume.
545
- # In practice (and in the official inference UI), the effective text length can be capped (e.g. 512).
546
- # If we build a FlexAttention block mask for a longer sequence than the model actually uses,
547
- # FlexAttention will throw a shape mismatch (block_mask.q_len != actual q_len).
548
- print(f"total_text_len: {total_text_len}, max_sequence_length: {max_sequence_length}")
549
- print()
550
- if max_sequence_length is not None and total_text_len > int(max_sequence_length):
551
- cap = int(max_sequence_length)
552
- final_prompt_embeds = final_prompt_embeds[:, :cap]
553
- final_prompt_embeds_mask = final_prompt_embeds_mask[:, :cap]
554
-
555
- if delete_main_prompt:
556
- prompt_len = 0
557
- main_prompt_indices = []
558
- image_prompt_indices = []
559
- # Only hints remain; keep list length stable (one per region) with zero lengths when truncated away.
560
- remaining = cap
561
- new_hint_lens: List[int] = []
562
- for hl in hint_lens:
563
- take = min(int(hl), remaining)
564
- new_hint_lens.append(take)
565
- remaining -= take
566
- hint_lens = new_hint_lens
567
- else:
568
- # First truncate main prompt, then truncate hints in order.
569
- new_prompt_len = min(int(prompt_len), cap)
570
- main_prompt_indices = [i for i in main_prompt_indices if i < new_prompt_len]
571
- image_prompt_indices = [i for i in image_prompt_indices if i < new_prompt_len]
572
- prompt_len = new_prompt_len
573
-
574
- remaining = cap - new_prompt_len
575
- new_hint_lens = []
576
- for hl in hint_lens:
577
- take = min(int(hl), remaining)
578
- new_hint_lens.append(take)
579
- remaining -= take
580
- hint_lens = new_hint_lens
581
-
582
- total_text_len = cap
583
-
584
- # ---- 2) Patch indices for each bbox (noise grid) ----
585
- grid_h = height // self.vae_scale_factor // 2
586
- grid_w = width // self.vae_scale_factor // 2
587
-
588
- noise_patch_indices_list: List[List[int]] = []
589
- for g in region_guidance:
590
- noise_patch_indices_list.append(
591
- self._bbox_to_patch_indices(
592
- g["bbox"],
593
- target_width=width,
594
- target_height=height,
595
- original_width=original_width,
596
- original_height=original_height,
597
- grid_w=grid_w,
598
- grid_h=grid_h,
599
- )
600
- )
601
-
602
- # ---- 3) Build attention controls (FlexAttention block mask OR explicit 4D attention_mask) ----
603
- # Attention rules: keep same defaults as `qwen_image.py` (logical keys: BBox i / Background / Main Prompt / Hint i / Image Prompt)
604
- if attention_rules is None:
605
- has_image_prompt = len(image_prompt_indices) > 0
606
- attention_rules = generate_default_attention_rules(
607
- region_guidance or [],
608
- delete_main_prompt=delete_main_prompt,
609
- bboxes_attend_to_each_other=True,
610
- has_image_prompt=has_image_prompt,
611
- symmetric_masking=symmetric_masking,
612
- )
613
-
614
- # Compute patch offsets for the latent stream: [noise_patches] + [img_patches_0] + [img_patches_1] + ...
615
- img_offsets = [0]
616
- for n in num_img_patches_list[:-1]:
617
- img_offsets.append(img_offsets[-1] + n)
618
- main_img_offset = img_offsets[main_image_idx]
619
-
620
- def joint_noise_indices(patch_indices: List[int]) -> List[int]:
621
- return [total_text_len + p for p in patch_indices]
622
-
623
- def joint_img_indices_for_main(patch_indices: List[int]) -> List[int]:
624
- # Condition images start after noise patches inside `hidden_states` stream.
625
- base = total_text_len + num_noise_patches + main_img_offset
626
- return [base + p for p in patch_indices]
627
-
628
- # Background / BBox combined indices (for FlexAttention rules that use unsplit keys)
629
- all_noise = set(range(num_noise_patches))
630
- all_bbox_noise = set()
631
- for inds in noise_patch_indices_list:
632
- all_bbox_noise.update(inds)
633
- noise_bg = sorted(list(all_noise - all_bbox_noise))
634
-
635
- # All condition image patches across all images are treated as background by default.
636
- total_img_patches = sum(num_img_patches_list)
637
- all_img_indices = list(range(total_img_patches))
638
-
639
- # For the "main" condition image, compute its bg and bbox subsets (optional but keeps behavior similar).
640
- main_img_num_patches = num_img_patches_list[main_image_idx]
641
- main_img_all = set(range(main_img_num_patches))
642
- main_img_bbox = set()
643
- for inds in noise_patch_indices_list:
644
- # Only valid if main image grid matches noise grid; for EditPlus default, it does.
645
- main_img_bbox.update([p for p in inds if 0 <= p < main_img_num_patches])
646
- main_img_bg = sorted(list(main_img_all - main_img_bbox))
647
-
648
- indices_map: Dict[str, List[int]] = {}
649
- if not delete_main_prompt:
650
- indices_map["Main Prompt"] = main_prompt_indices
651
- if image_prompt_indices:
652
- indices_map["Image Prompt"] = image_prompt_indices
653
- cur = prompt_len
654
- for i, hlen in enumerate(hint_lens):
655
- indices_map[f"Hint {i+1}"] = list(range(cur, cur + hlen))
656
- cur += hlen
657
-
658
- # Background groups (combined + split)
659
- # Combined Background covers: noise_bg + all condition image patches (all images).
660
- indices_map["Background"] = joint_noise_indices(noise_bg) + [total_text_len + num_noise_patches + p for p in all_img_indices]
661
- indices_map["Noise Background"] = joint_noise_indices(noise_bg)
662
- indices_map["Image Background"] = [total_text_len + num_noise_patches + p for p in all_img_indices]
663
-
664
- # Per-bbox groups (combined + split)
665
- for i, inds in enumerate(noise_patch_indices_list):
666
- indices_map[f"BBox {i+1}"] = joint_noise_indices(inds) + joint_img_indices_for_main(inds)
667
- indices_map[f"Noise BBox {i+1}"] = joint_noise_indices(inds)
668
- indices_map[f"Image BBox {i+1}"] = joint_img_indices_for_main(inds)
669
-
670
- total_seq_len = total_text_len + num_noise_patches + total_img_patches
671
-
672
- if enable_flex_attn:
673
- block_mask = create_flex_block_mask(
674
- indices_map=indices_map,
675
- total_seq_len=total_seq_len,
676
- attention_rules=attention_rules,
677
- device=device,
678
- use_bitmask=flex_attn_use_bitmask,
679
- )
680
- return (
681
- final_prompt_embeds,
682
- final_prompt_embeds_mask,
683
- None,
684
- {"flex_block_mask": block_mask},
685
- indices_map,
686
- total_seq_len,
687
- )
688
-
689
- # Fallback: materialize an explicit 4D attention_mask (memory heavy for large grids).
690
- attn = torch.zeros(
691
- final_prompt_embeds.shape[0],
692
- self.transformer.config.num_attention_heads,
693
- total_seq_len,
694
- total_seq_len,
695
- device=device,
696
- dtype=torch.bool,
697
- )
698
- # Use "allowed edges" from rules by mapping component -> indices_map.
699
- for (q_comp, k_comp), allowed in attention_rules.items():
700
- if not allowed:
701
- continue
702
- q_idx = indices_map.get(q_comp, [])
703
- k_idx = indices_map.get(k_comp, [])
704
- if not q_idx or not k_idx:
705
- continue
706
- q_t = torch.tensor(q_idx, device=device, dtype=torch.long)
707
- k_t = torch.tensor(k_idx, device=device, dtype=torch.long)
708
- attn[:, :, q_t.view(-1, 1), k_t.view(1, -1)] = True
709
-
710
- return final_prompt_embeds, final_prompt_embeds_mask, attn, {}, indices_map, total_seq_len
711
-
712
- # Copied from diffusers.pipelines.qwenimage.pipeline_qwenimage_edit.QwenImageEditPipeline.encode_prompt
713
- def encode_prompt(
714
- self,
715
- prompt: Union[str, List[str]],
716
- image: Optional[torch.Tensor] = None,
717
- device: Optional[torch.device] = None,
718
- num_images_per_prompt: int = 1,
719
- prompt_embeds: Optional[torch.Tensor] = None,
720
- prompt_embeds_mask: Optional[torch.Tensor] = None,
721
- max_sequence_length: int = 1024,
722
- ):
723
- r"""
724
-
725
- Args:
726
- prompt (`str` or `List[str]`, *optional*):
727
- prompt to be encoded
728
- image (`torch.Tensor`, *optional*):
729
- image to be encoded
730
- device: (`torch.device`):
731
- torch device
732
- num_images_per_prompt (`int`):
733
- number of images that should be generated per prompt
734
- prompt_embeds (`torch.Tensor`, *optional*):
735
- Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
736
- provided, text embeddings will be generated from `prompt` input argument.
737
- """
738
- device = device or self._execution_device
739
-
740
- prompt = [prompt] if isinstance(prompt, str) else prompt
741
- batch_size = len(prompt) if prompt_embeds is None else prompt_embeds.shape[0]
742
-
743
- if prompt_embeds is None:
744
- prompt_embeds, prompt_embeds_mask = self._get_qwen_prompt_embeds(prompt, image, device)
745
-
746
- _, seq_len, _ = prompt_embeds.shape
747
- prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
748
- prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
749
- prompt_embeds_mask = prompt_embeds_mask.repeat(1, num_images_per_prompt, 1)
750
- prompt_embeds_mask = prompt_embeds_mask.view(batch_size * num_images_per_prompt, seq_len)
751
-
752
- return prompt_embeds, prompt_embeds_mask
753
-
754
- # Copied from diffusers.pipelines.qwenimage.pipeline_qwenimage_edit.QwenImageEditPipeline.check_inputs
755
- def check_inputs(
756
- self,
757
- prompt,
758
- height,
759
- width,
760
- negative_prompt=None,
761
- prompt_embeds=None,
762
- negative_prompt_embeds=None,
763
- prompt_embeds_mask=None,
764
- negative_prompt_embeds_mask=None,
765
- callback_on_step_end_tensor_inputs=None,
766
- max_sequence_length=None,
767
- ):
768
- if height % (self.vae_scale_factor * 2) != 0 or width % (self.vae_scale_factor * 2) != 0:
769
- logger.warning(
770
- f"`height` and `width` have to be divisible by {self.vae_scale_factor * 2} but are {height} and {width}. Dimensions will be resized accordingly"
771
- )
772
-
773
- if callback_on_step_end_tensor_inputs is not None and not all(
774
- k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
775
- ):
776
- raise ValueError(
777
- f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
778
- )
779
-
780
- if prompt is not None and prompt_embeds is not None:
781
- raise ValueError(
782
- f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
783
- " only forward one of the two."
784
- )
785
- elif prompt is None and prompt_embeds is None:
786
- raise ValueError(
787
- "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
788
- )
789
- elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
790
- raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
791
-
792
- if negative_prompt is not None and negative_prompt_embeds is not None:
793
- raise ValueError(
794
- f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:"
795
- f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
796
- )
797
-
798
- if prompt_embeds is not None and prompt_embeds_mask is None:
799
- raise ValueError(
800
- "If `prompt_embeds` are provided, `prompt_embeds_mask` also have to be passed. Make sure to generate `prompt_embeds_mask` from the same text encoder that was used to generate `prompt_embeds`."
801
- )
802
- if negative_prompt_embeds is not None and negative_prompt_embeds_mask is None:
803
- raise ValueError(
804
- "If `negative_prompt_embeds` are provided, `negative_prompt_embeds_mask` also have to be passed. Make sure to generate `negative_prompt_embeds_mask` from the same text encoder that was used to generate `negative_prompt_embeds`."
805
- )
806
-
807
- if max_sequence_length is not None and max_sequence_length > 1024:
808
- raise ValueError(f"`max_sequence_length` cannot be greater than 1024 but is {max_sequence_length}")
809
-
810
- @staticmethod
811
- # Copied from diffusers.pipelines.qwenimage.pipeline_qwenimage.QwenImagePipeline._pack_latents
812
- def _pack_latents(latents, batch_size, num_channels_latents, height, width):
813
- latents = latents.view(batch_size, num_channels_latents, height // 2, 2, width // 2, 2)
814
- latents = latents.permute(0, 2, 4, 1, 3, 5)
815
- latents = latents.reshape(batch_size, (height // 2) * (width // 2), num_channels_latents * 4)
816
-
817
- return latents
818
-
819
- @staticmethod
820
- # Copied from diffusers.pipelines.qwenimage.pipeline_qwenimage.QwenImagePipeline._unpack_latents
821
- def _unpack_latents(latents, height, width, vae_scale_factor):
822
- batch_size, num_patches, channels = latents.shape
823
-
824
- # VAE applies 8x compression on images but we must also account for packing which requires
825
- # latent height and width to be divisible by 2.
826
- height = 2 * (int(height) // (vae_scale_factor * 2))
827
- width = 2 * (int(width) // (vae_scale_factor * 2))
828
-
829
- latents = latents.view(batch_size, height // 2, width // 2, channels // 4, 2, 2)
830
- latents = latents.permute(0, 3, 1, 4, 2, 5)
831
-
832
- latents = latents.reshape(batch_size, channels // (2 * 2), 1, height, width)
833
-
834
- return latents
835
-
836
- # Copied from diffusers.pipelines.qwenimage.pipeline_qwenimage_edit.QwenImageEditPipeline._encode_vae_image
837
- def _encode_vae_image(self, image: torch.Tensor, generator: torch.Generator):
838
- if isinstance(generator, list):
839
- image_latents = [
840
- retrieve_latents(self.vae.encode(image[i : i + 1]), generator=generator[i], sample_mode="argmax")
841
- for i in range(image.shape[0])
842
- ]
843
- image_latents = torch.cat(image_latents, dim=0)
844
- else:
845
- image_latents = retrieve_latents(self.vae.encode(image), generator=generator, sample_mode="argmax")
846
- latents_mean = (
847
- torch.tensor(self.vae.config.latents_mean)
848
- .view(1, self.latent_channels, 1, 1, 1)
849
- .to(image_latents.device, image_latents.dtype)
850
- )
851
- latents_std = (
852
- torch.tensor(self.vae.config.latents_std)
853
- .view(1, self.latent_channels, 1, 1, 1)
854
- .to(image_latents.device, image_latents.dtype)
855
- )
856
- image_latents = (image_latents - latents_mean) / latents_std
857
-
858
- return image_latents
859
-
860
- def prepare_latents(
861
- self,
862
- images,
863
- batch_size,
864
- num_channels_latents,
865
- height,
866
- width,
867
- dtype,
868
- device,
869
- generator,
870
- latents=None,
871
- ):
872
- # VAE applies 8x compression on images but we must also account for packing which requires
873
- # latent height and width to be divisible by 2.
874
- height = 2 * (int(height) // (self.vae_scale_factor * 2))
875
- width = 2 * (int(width) // (self.vae_scale_factor * 2))
876
-
877
- shape = (batch_size, 1, num_channels_latents, height, width)
878
-
879
- image_latents = None
880
- if images is not None:
881
- if not isinstance(images, list):
882
- images = [images]
883
- all_image_latents = []
884
- for image in images:
885
- image = image.to(device=device, dtype=dtype)
886
- if image.shape[1] != self.latent_channels:
887
- image_latents = self._encode_vae_image(image=image, generator=generator)
888
- else:
889
- image_latents = image
890
- if batch_size > image_latents.shape[0] and batch_size % image_latents.shape[0] == 0:
891
- # expand init_latents for batch_size
892
- additional_image_per_prompt = batch_size // image_latents.shape[0]
893
- image_latents = torch.cat([image_latents] * additional_image_per_prompt, dim=0)
894
- elif batch_size > image_latents.shape[0] and batch_size % image_latents.shape[0] != 0:
895
- raise ValueError(
896
- f"Cannot duplicate `image` of batch size {image_latents.shape[0]} to {batch_size} text prompts."
897
- )
898
- else:
899
- image_latents = torch.cat([image_latents], dim=0)
900
-
901
- image_latent_height, image_latent_width = image_latents.shape[3:]
902
- image_latents = self._pack_latents(
903
- image_latents, batch_size, num_channels_latents, image_latent_height, image_latent_width
904
- )
905
- all_image_latents.append(image_latents)
906
- image_latents = torch.cat(all_image_latents, dim=1)
907
-
908
- if isinstance(generator, list) and len(generator) != batch_size:
909
- raise ValueError(
910
- f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
911
- f" size of {batch_size}. Make sure the batch size matches the length of the generators."
912
- )
913
- if latents is None:
914
- latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
915
- latents = self._pack_latents(latents, batch_size, num_channels_latents, height, width)
916
- else:
917
- latents = latents.to(device=device, dtype=dtype)
918
-
919
- return latents, image_latents
920
-
921
- @property
922
- def guidance_scale(self):
923
- return self._guidance_scale
924
-
925
- @property
926
- def attention_kwargs(self):
927
- return self._attention_kwargs
928
-
929
- @property
930
- def num_timesteps(self):
931
- return self._num_timesteps
932
-
933
- @property
934
- def current_timestep(self):
935
- return self._current_timestep
936
-
937
- @property
938
- def interrupt(self):
939
- return self._interrupt
940
-
941
- @torch.no_grad()
942
- @replace_example_docstring(EXAMPLE_DOC_STRING)
943
- def __call__(
944
- self,
945
- image: Optional[PipelineImageInput] = None,
946
- prompt: Union[str, List[str]] = None,
947
- negative_prompt: Union[str, List[str]] = None,
948
- true_cfg_scale: float = 4.0,
949
- height: Optional[int] = None,
950
- width: Optional[int] = None,
951
- num_inference_steps: int = 50,
952
- sigmas: Optional[List[float]] = None,
953
- guidance_scale: Optional[float] = None,
954
- num_images_per_prompt: int = 1,
955
- generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
956
- latents: Optional[torch.Tensor] = None,
957
- prompt_embeds: Optional[torch.Tensor] = None,
958
- prompt_embeds_mask: Optional[torch.Tensor] = None,
959
- negative_prompt_embeds: Optional[torch.Tensor] = None,
960
- negative_prompt_embeds_mask: Optional[torch.Tensor] = None,
961
- output_type: Optional[str] = "pil",
962
- return_dict: bool = True,
963
- attention_kwargs: Optional[Dict[str, Any]] = None,
964
- callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
965
- callback_on_step_end_tensor_inputs: List[str] = ["latents"],
966
- max_sequence_length: int = 512,
967
- region_guidance: Optional[List[Dict]] = None, # [{'bbox': [x1,y1,x2,y2], 'hint': '...'}, ...]
968
- mask_main_prompt_influence: bool = False,
969
- symmetric_masking: bool = False,
970
- delete_main_prompt: bool = False,
971
- attention_rules: Optional[Dict] = None,
972
- enable_flex_attn: bool = True,
973
- flex_attn_use_bitmask: bool = True,
974
- ):
975
- r"""
976
- Function invoked when calling the pipeline for generation.
977
-
978
- Args:
979
- image (`torch.Tensor`, `PIL.Image.Image`, `np.ndarray`, `List[torch.Tensor]`, `List[PIL.Image.Image]`, or `List[np.ndarray]`):
980
- `Image`, numpy array or tensor representing an image batch to be used as the starting point. For both
981
- numpy array and pytorch tensor, the expected value range is between `[0, 1]` If it's a tensor or a list
982
- or tensors, the expected shape should be `(B, C, H, W)` or `(C, H, W)`. If it is a numpy array or a
983
- list of arrays, the expected shape should be `(B, H, W, C)` or `(H, W, C)` It can also accept image
984
- latents as `image`, but if passing latents directly it is not encoded again.
985
- prompt (`str` or `List[str]`, *optional*):
986
- The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
987
- instead.
988
- negative_prompt (`str` or `List[str]`, *optional*):
989
- The prompt or prompts not to guide the image generation. If not defined, one has to pass
990
- `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `true_cfg_scale` is
991
- not greater than `1`).
992
- true_cfg_scale (`float`, *optional*, defaults to 1.0):
993
- true_cfg_scale (`float`, *optional*, defaults to 1.0): Guidance scale as defined in [Classifier-Free
994
- Diffusion Guidance](https://huggingface.co/papers/2207.12598). `true_cfg_scale` is defined as `w` of
995
- equation 2. of [Imagen Paper](https://huggingface.co/papers/2205.11487). Classifier-free guidance is
996
- enabled by setting `true_cfg_scale > 1` and a provided `negative_prompt`. Higher guidance scale
997
- encourages to generate images that are closely linked to the text `prompt`, usually at the expense of
998
- lower image quality.
999
- height (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
1000
- The height in pixels of the generated image. This is set to 1024 by default for the best results.
1001
- width (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
1002
- The width in pixels of the generated image. This is set to 1024 by default for the best results.
1003
- num_inference_steps (`int`, *optional*, defaults to 50):
1004
- The number of denoising steps. More denoising steps usually lead to a higher quality image at the
1005
- expense of slower inference.
1006
- sigmas (`List[float]`, *optional*):
1007
- Custom sigmas to use for the denoising process with schedulers which support a `sigmas` argument in
1008
- their `set_timesteps` method. If not defined, the default behavior when `num_inference_steps` is passed
1009
- will be used.
1010
- guidance_scale (`float`, *optional*, defaults to None):
1011
- A guidance scale value for guidance distilled models. Unlike the traditional classifier-free guidance
1012
- where the guidance scale is applied during inference through noise prediction rescaling, guidance
1013
- distilled models take the guidance scale directly as an input parameter during forward pass. Guidance
1014
- scale is enabled by setting `guidance_scale > 1`. Higher guidance scale encourages to generate images
1015
- that are closely linked to the text `prompt`, usually at the expense of lower image quality. This
1016
- parameter in the pipeline is there to support future guidance-distilled models when they come up. It is
1017
- ignored when not using guidance distilled models. To enable traditional classifier-free guidance,
1018
- please pass `true_cfg_scale > 1.0` and `negative_prompt` (even an empty negative prompt like " " should
1019
- enable classifier-free guidance computations).
1020
- num_images_per_prompt (`int`, *optional*, defaults to 1):
1021
- The number of images to generate per prompt.
1022
- generator (`torch.Generator` or `List[torch.Generator]`, *optional*):
1023
- One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)
1024
- to make generation deterministic.
1025
- latents (`torch.Tensor`, *optional*):
1026
- Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image
1027
- generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
1028
- tensor will be generated by sampling using the supplied random `generator`.
1029
- prompt_embeds (`torch.Tensor`, *optional*):
1030
- Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
1031
- provided, text embeddings will be generated from `prompt` input argument.
1032
- negative_prompt_embeds (`torch.Tensor`, *optional*):
1033
- Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
1034
- weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
1035
- argument.
1036
- output_type (`str`, *optional*, defaults to `"pil"`):
1037
- The output format of the generate image. Choose between
1038
- [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`.
1039
- return_dict (`bool`, *optional*, defaults to `True`):
1040
- Whether or not to return a [`~pipelines.qwenimage.QwenImagePipelineOutput`] instead of a plain tuple.
1041
- attention_kwargs (`dict`, *optional*):
1042
- A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under
1043
- `self.processor` in
1044
- [diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
1045
- callback_on_step_end (`Callable`, *optional*):
1046
- A function that calls at the end of each denoising steps during the inference. The function is called
1047
- with the following arguments: `callback_on_step_end(self: DiffusionPipeline, step: int, timestep: int,
1048
- callback_kwargs: Dict)`. `callback_kwargs` will include a list of all tensors as specified by
1049
- `callback_on_step_end_tensor_inputs`.
1050
- callback_on_step_end_tensor_inputs (`List`, *optional*):
1051
- The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
1052
- will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
1053
- `._callback_tensor_inputs` attribute of your pipeline class.
1054
- max_sequence_length (`int` defaults to 512): Maximum sequence length to use with the `prompt`.
1055
-
1056
- Examples:
1057
-
1058
- Returns:
1059
- [`~pipelines.qwenimage.QwenImagePipelineOutput`] or `tuple`:
1060
- [`~pipelines.qwenimage.QwenImagePipelineOutput`] if `return_dict` is True, otherwise a `tuple`. When
1061
- returning a tuple, the first element is a list with the generated images.
1062
- """
1063
- image_size = image[-1].size if isinstance(image, list) else image.size
1064
- original_height, original_width = height or image_size[1], width or image_size[0]
1065
- calculated_width, calculated_height = calculate_dimensions(1024 * 1024, image_size[0] / image_size[1])
1066
- height = height or calculated_height
1067
- width = width or calculated_width
1068
-
1069
- multiple_of = self.vae_scale_factor * 2
1070
- width = width // multiple_of * multiple_of
1071
- height = height // multiple_of * multiple_of
1072
-
1073
- # 1. Check inputs. Raise error if not correct
1074
- self.check_inputs(
1075
- prompt,
1076
- height,
1077
- width,
1078
- negative_prompt=negative_prompt,
1079
- prompt_embeds=prompt_embeds,
1080
- negative_prompt_embeds=negative_prompt_embeds,
1081
- prompt_embeds_mask=prompt_embeds_mask,
1082
- negative_prompt_embeds_mask=negative_prompt_embeds_mask,
1083
- callback_on_step_end_tensor_inputs=callback_on_step_end_tensor_inputs,
1084
- max_sequence_length=max_sequence_length,
1085
- )
1086
-
1087
- self._guidance_scale = guidance_scale
1088
- self._attention_kwargs = attention_kwargs
1089
- self._current_timestep = None
1090
- self._interrupt = False
1091
-
1092
- # 2. Define call parameters
1093
- if prompt is not None and isinstance(prompt, str):
1094
- batch_size = 1
1095
- elif prompt is not None and isinstance(prompt, list):
1096
- batch_size = len(prompt)
1097
- else:
1098
- batch_size = prompt_embeds.shape[0]
1099
-
1100
- # QwenImageEditPlusPipeline does not currently support batch_size > 1
1101
- if batch_size > 1:
1102
- raise ValueError(
1103
- f"QwenImageEditPlusPipeline currently only supports batch_size=1, but received batch_size={batch_size}. "
1104
- "Please process prompts one at a time."
1105
- )
1106
-
1107
- device = self._execution_device
1108
- # 3. Preprocess image
1109
- if image is not None and not (isinstance(image, torch.Tensor) and image.size(1) == self.latent_channels):
1110
- if not isinstance(image, list):
1111
- image = [image]
1112
- condition_image_sizes = []
1113
- condition_images = []
1114
- vae_image_sizes = []
1115
- vae_images = []
1116
- for img in image:
1117
- image_width, image_height = img.size
1118
- condition_width, condition_height = calculate_dimensions(
1119
- CONDITION_IMAGE_SIZE, image_width / image_height
1120
- )
1121
- vae_width, vae_height = calculate_dimensions(VAE_IMAGE_SIZE, image_width / image_height)
1122
- condition_image_sizes.append((condition_width, condition_height))
1123
- vae_image_sizes.append((vae_width, vae_height))
1124
- condition_images.append(self.image_processor.resize(img, condition_height, condition_width))
1125
- vae_images.append(self.image_processor.preprocess(img, vae_height, vae_width).unsqueeze(2))
1126
-
1127
- has_neg_prompt = negative_prompt is not None or (
1128
- negative_prompt_embeds is not None and negative_prompt_embeds_mask is not None
1129
- )
1130
-
1131
- if true_cfg_scale > 1 and not has_neg_prompt:
1132
- logger.warning(
1133
- f"true_cfg_scale is passed as {true_cfg_scale}, but classifier-free guidance is not enabled since no negative_prompt is provided."
1134
- )
1135
- elif true_cfg_scale <= 1 and has_neg_prompt:
1136
- logger.warning(
1137
- " negative_prompt is passed but classifier-free guidance is not enabled since true_cfg_scale <= 1"
1138
- )
1139
-
1140
- do_true_cfg = true_cfg_scale > 1 and has_neg_prompt
1141
- attention_mask = None
1142
- extra_attention_kwargs: Dict[str, Any] = {}
1143
- indices_map: Optional[Dict[str, List[int]]] = None
1144
- total_seq_len: Optional[int] = None
1145
-
1146
- # Step-wise attention rules: allow passing `{step_idx: rules_dict, ...}` like `qwen_image.py`.
1147
- step_attention_rules: Dict[int, Dict] = {}
1148
- base_attention_rules = attention_rules
1149
- if isinstance(attention_rules, dict) and any(isinstance(k, int) for k in attention_rules.keys()):
1150
- step_attention_rules = attention_rules # type: ignore[assignment]
1151
- base_attention_rules = None
1152
-
1153
- if region_guidance:
1154
- # Latent stream layout for EditPlus is: [noise_patches] + [cond_image_patches_0] + ... + [cond_image_patches_N]
1155
- grid_h = height // self.vae_scale_factor // 2
1156
- grid_w = width // self.vae_scale_factor // 2
1157
- num_noise_patches = grid_h * grid_w
1158
- num_img_patches_list = [
1159
- (vh // self.vae_scale_factor // 2) * (vw // self.vae_scale_factor // 2) for (vw, vh) in vae_image_sizes
1160
- ]
1161
- main_image_idx = len(num_img_patches_list) - 1
1162
-
1163
- (
1164
- prompt_embeds,
1165
- prompt_embeds_mask,
1166
- attention_mask,
1167
- extra_attention_kwargs,
1168
- indices_map,
1169
- total_seq_len,
1170
- ) = self.process_region_guidance(
1171
- main_prompt=prompt,
1172
- prompt_image=condition_images,
1173
- region_guidance=region_guidance,
1174
- width=width,
1175
- height=height,
1176
- original_width=original_width,
1177
- original_height=original_height,
1178
- device=device,
1179
- num_images_per_prompt=num_images_per_prompt,
1180
- max_sequence_length=max_sequence_length,
1181
- delete_main_prompt=delete_main_prompt,
1182
- mask_main_prompt_influence=mask_main_prompt_influence,
1183
- symmetric_masking=symmetric_masking,
1184
- attention_rules=base_attention_rules,
1185
- enable_flex_attn=enable_flex_attn,
1186
- flex_attn_use_bitmask=flex_attn_use_bitmask,
1187
- num_noise_patches=num_noise_patches,
1188
- num_img_patches_list=num_img_patches_list,
1189
- main_image_idx=main_image_idx,
1190
- )
1191
- else:
1192
- prompt_embeds, prompt_embeds_mask = self.encode_prompt(
1193
- image=condition_images,
1194
- prompt=prompt,
1195
- prompt_embeds=prompt_embeds,
1196
- prompt_embeds_mask=prompt_embeds_mask,
1197
- device=device,
1198
- num_images_per_prompt=num_images_per_prompt,
1199
- max_sequence_length=max_sequence_length,
1200
- )
1201
-
1202
- # Switch attention processor depending on flex usage.
1203
- if enable_flex_attn and region_guidance:
1204
- self.set_attn_processor(QwenFlexAttentionProcessor)
1205
- if self.attention_kwargs is None:
1206
- self._attention_kwargs = {}
1207
- self._attention_kwargs.update(extra_attention_kwargs)
1208
- else:
1209
- self.set_attn_processor(QwenDoubleStreamAttnProcessor2_0)
1210
- if do_true_cfg:
1211
- negative_prompt_embeds, negative_prompt_embeds_mask = self.encode_prompt(
1212
- image=condition_images,
1213
- prompt=negative_prompt,
1214
- prompt_embeds=negative_prompt_embeds,
1215
- prompt_embeds_mask=negative_prompt_embeds_mask,
1216
- device=device,
1217
- num_images_per_prompt=num_images_per_prompt,
1218
- max_sequence_length=max_sequence_length,
1219
- )
1220
-
1221
- # 4. Prepare latent variables
1222
- num_channels_latents = self.transformer.config.in_channels // 4
1223
- latents, image_latents = self.prepare_latents(
1224
- vae_images,
1225
- batch_size * num_images_per_prompt,
1226
- num_channels_latents,
1227
- height,
1228
- width,
1229
- prompt_embeds.dtype,
1230
- device,
1231
- generator,
1232
- latents,
1233
- )
1234
- img_shapes = [
1235
- [
1236
- (1, height // self.vae_scale_factor // 2, width // self.vae_scale_factor // 2),
1237
- *[
1238
- (1, vae_height // self.vae_scale_factor // 2, vae_width // self.vae_scale_factor // 2)
1239
- for vae_width, vae_height in vae_image_sizes
1240
- ],
1241
- ]
1242
- ] * batch_size
1243
-
1244
- # 5. Prepare timesteps
1245
- sigmas = np.linspace(1.0, 1 / num_inference_steps, num_inference_steps) if sigmas is None else sigmas
1246
- image_seq_len = latents.shape[1]
1247
- mu = calculate_shift(
1248
- image_seq_len,
1249
- self.scheduler.config.get("base_image_seq_len", 256),
1250
- self.scheduler.config.get("max_image_seq_len", 4096),
1251
- self.scheduler.config.get("base_shift", 0.5),
1252
- self.scheduler.config.get("max_shift", 1.15),
1253
- )
1254
- timesteps, num_inference_steps = retrieve_timesteps(
1255
- self.scheduler,
1256
- num_inference_steps,
1257
- device,
1258
- sigmas=sigmas,
1259
- mu=mu,
1260
- )
1261
- num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
1262
- self._num_timesteps = len(timesteps)
1263
-
1264
- # handle guidance
1265
- if self.transformer.config.guidance_embeds and guidance_scale is None:
1266
- raise ValueError("guidance_scale is required for guidance-distilled model.")
1267
- elif self.transformer.config.guidance_embeds:
1268
- guidance = torch.full([1], guidance_scale, device=device, dtype=torch.float32)
1269
- guidance = guidance.expand(latents.shape[0])
1270
- elif not self.transformer.config.guidance_embeds and guidance_scale is not None:
1271
- logger.warning(
1272
- f"guidance_scale is passed as {guidance_scale}, but ignored since the model is not guidance-distilled."
1273
- )
1274
- guidance = None
1275
- elif not self.transformer.config.guidance_embeds and guidance_scale is None:
1276
- guidance = None
1277
-
1278
- if self.attention_kwargs is None:
1279
- self._attention_kwargs = {}
1280
-
1281
- def _apply_mask_main_prompt_influence(rules: Dict) -> Dict:
1282
- if not mask_main_prompt_influence or delete_main_prompt:
1283
- return rules
1284
- # Make a shallow copy so per-step edits don't mutate the original dict.
1285
- out = dict(rules)
1286
- # Cover both logical and split component names.
1287
- bg_keys = ["Background", "Noise Background", "Image Background"]
1288
- for q in bg_keys:
1289
- out[(q, "Main Prompt")] = False
1290
- for ridx in range(len(region_guidance or [])):
1291
- for q in [f"BBox {ridx+1}", f"Noise BBox {ridx+1}", f"Image BBox {ridx+1}"]:
1292
- out[(q, "Main Prompt")] = False
1293
- return out
1294
-
1295
- # Establish the "active" rules reference for step-wise updates.
1296
- active_rules = _apply_mask_main_prompt_influence(base_attention_rules) if base_attention_rules else None
1297
- if region_guidance and active_rules is None:
1298
- # When rules are not explicitly provided, `process_region_guidance` already used defaults.
1299
- # Regenerate here for step-wise switching (only used if `step_attention_rules` is non-empty).
1300
- has_image_prompt = True # condition_images exist in edit-plus path
1301
- active_rules = _apply_mask_main_prompt_influence(
1302
- generate_default_attention_rules(
1303
- region_guidance or [],
1304
- delete_main_prompt=delete_main_prompt,
1305
- bboxes_attend_to_each_other=True,
1306
- has_image_prompt=has_image_prompt,
1307
- symmetric_masking=symmetric_masking,
1308
- )
1309
- )
1310
-
1311
- # 6. Denoising loop
1312
- self.scheduler.set_begin_index(0)
1313
- debug_seq = False
1314
- try:
1315
- debug_seq = bool((self.attention_kwargs or {}).get("debug_seq", True))
1316
- except Exception:
1317
- debug_seq = False
1318
- _printed_seq_debug = False
1319
- with self.progress_bar(total=num_inference_steps) as progress_bar:
1320
- for i, t in enumerate(timesteps):
1321
- if self.interrupt:
1322
- continue
1323
-
1324
- self._current_timestep = t
1325
-
1326
- # Step-wise attention rules update (rebuild masks only when rules change).
1327
- if region_guidance and step_attention_rules:
1328
- desired_rules = step_attention_rules.get(i, active_rules)
1329
- desired_rules = _apply_mask_main_prompt_influence(desired_rules) if desired_rules is not None else active_rules
1330
- if desired_rules is not active_rules:
1331
- if enable_flex_attn and indices_map is not None and total_seq_len is not None:
1332
- block_mask = create_flex_block_mask(
1333
- indices_map=indices_map,
1334
- total_seq_len=total_seq_len,
1335
- attention_rules=desired_rules,
1336
- device=device,
1337
- use_bitmask=flex_attn_use_bitmask,
1338
- )
1339
- self._attention_kwargs["flex_block_mask"] = block_mask
1340
- attention_mask = None
1341
- else:
1342
- # Materialize a 4D attention_mask from indices_map for the fallback path.
1343
- attn = torch.zeros(
1344
- latents.shape[0],
1345
- self.transformer.config.num_attention_heads,
1346
- total_seq_len,
1347
- total_seq_len,
1348
- device=device,
1349
- dtype=torch.bool,
1350
- )
1351
- for (q_comp, k_comp), allowed in desired_rules.items():
1352
- if not allowed:
1353
- continue
1354
- q_idx = (indices_map or {}).get(q_comp, [])
1355
- k_idx = (indices_map or {}).get(k_comp, [])
1356
- if not q_idx or not k_idx:
1357
- continue
1358
- q_t = torch.tensor(q_idx, device=device, dtype=torch.long)
1359
- k_t = torch.tensor(k_idx, device=device, dtype=torch.long)
1360
- attn[:, :, q_t.view(-1, 1), k_t.view(1, -1)] = True
1361
- attention_mask = attn
1362
- active_rules = desired_rules
1363
-
1364
- latent_model_input = latents
1365
- if image_latents is not None:
1366
- latent_model_input = torch.cat([latents, image_latents], dim=1)
1367
-
1368
- # broadcast to batch dimension in a way that's compatible with ONNX/Core ML
1369
- timestep = t.expand(latents.shape[0]).to(latents.dtype)
1370
- current_attention_kwargs = self.attention_kwargs.copy() if self.attention_kwargs else {}
1371
- if attention_mask is not None:
1372
- current_attention_kwargs["attention_mask"] = attention_mask
1373
-
1374
- # Sequence sanity check for FlexAttention
1375
- # joint_len_actual = seq_txt(actual) + seq_img(actual hidden_states len)
1376
- if enable_flex_attn and region_guidance:
1377
- seq_txt_actual = int(prompt_embeds.shape[1]) if prompt_embeds is not None else 0
1378
- seq_img_actual = int(latent_model_input.shape[1])
1379
- joint_len_actual = seq_txt_actual + seq_img_actual
1380
-
1381
- flex_bm = current_attention_kwargs.get("flex_block_mask", None)
1382
- if flex_bm is not None and hasattr(flex_bm, "shape"):
1383
- try:
1384
- bm_q = int(flex_bm.shape[-2])
1385
- bm_kv = int(flex_bm.shape[-1])
1386
- except Exception:
1387
- bm_q = bm_kv = -1
1388
-
1389
- if (bm_q != -1 and bm_kv != -1) and (bm_q != joint_len_actual or bm_kv != joint_len_actual):
1390
- # This indicates a mismatch in how we composed the sequence length for the mask vs what
1391
- # the model actually feeds into attention. Adjust to keep FlexAttention from erroring
1392
- # and log the exact breakdown for debugging.
1393
- if hasattr(flex_bm, "adjust"):
1394
- try:
1395
- current_attention_kwargs["flex_block_mask"] = flex_bm.adjust(
1396
- joint_len_actual, joint_len_actual
1397
- )
1398
- except Exception:
1399
- pass
1400
- logger.warning(
1401
- "FlexAttention block_mask len mismatch: "
1402
- f"block_mask=({bm_q},{bm_kv}) vs actual_joint=({joint_len_actual},{joint_len_actual}) "
1403
- f"[seq_txt={seq_txt_actual}, seq_img={seq_img_actual}, latents={int(latents.shape[1])}, "
1404
- f"img_latents={(int(image_latents.shape[1]) if image_latents is not None else 0)}]"
1405
- )
1406
-
1407
- if debug_seq and not _printed_seq_debug:
1408
- _printed_seq_debug = True
1409
- logger.warning(
1410
- "Seq debug (first step): "
1411
- f"prompt_embeds={tuple(prompt_embeds.shape) if prompt_embeds is not None else None}, "
1412
- f"latent_model_input={tuple(latent_model_input.shape)}, "
1413
- f"latents={tuple(latents.shape)}, "
1414
- f"image_latents={(tuple(image_latents.shape) if image_latents is not None else None)}, "
1415
- f"max_sequence_length={max_sequence_length}"
1416
- )
1417
- with self.transformer.cache_context("cond"):
1418
- noise_pred = self.transformer(
1419
- hidden_states=latent_model_input,
1420
- timestep=timestep / 1000,
1421
- guidance=guidance,
1422
- encoder_hidden_states_mask=prompt_embeds_mask,
1423
- encoder_hidden_states=prompt_embeds,
1424
- img_shapes=img_shapes,
1425
- attention_kwargs=current_attention_kwargs,
1426
- return_dict=False,
1427
- )[0]
1428
- noise_pred = noise_pred[:, : latents.size(1)]
1429
-
1430
- if do_true_cfg:
1431
- with self.transformer.cache_context("uncond"):
1432
- neg_noise_pred = self.transformer(
1433
- hidden_states=latent_model_input,
1434
- timestep=timestep / 1000,
1435
- guidance=guidance,
1436
- encoder_hidden_states_mask=negative_prompt_embeds_mask,
1437
- encoder_hidden_states=negative_prompt_embeds,
1438
- img_shapes=img_shapes,
1439
- # IMPORTANT: Do not reuse region guidance masks for the unconditional branch.
1440
- # Negative prompts can have different text sequence lengths, which would make
1441
- # any precomputed FlexAttention block mask length-mismatched.
1442
- attention_kwargs={},
1443
- return_dict=False,
1444
- )[0]
1445
- neg_noise_pred = neg_noise_pred[:, : latents.size(1)]
1446
- comb_pred = neg_noise_pred + true_cfg_scale * (noise_pred - neg_noise_pred)
1447
-
1448
- cond_norm = torch.norm(noise_pred, dim=-1, keepdim=True)
1449
- noise_norm = torch.norm(comb_pred, dim=-1, keepdim=True)
1450
- noise_pred = comb_pred * (cond_norm / noise_norm)
1451
-
1452
- # compute the previous noisy sample x_t -> x_t-1
1453
- latents_dtype = latents.dtype
1454
- latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
1455
-
1456
- if latents.dtype != latents_dtype:
1457
- if torch.backends.mps.is_available():
1458
- # some platforms (eg. apple mps) misbehave due to a pytorch bug: https://github.com/pytorch/pytorch/pull/99272
1459
- latents = latents.to(latents_dtype)
1460
-
1461
- if callback_on_step_end is not None:
1462
- callback_kwargs = {}
1463
- for k in callback_on_step_end_tensor_inputs:
1464
- callback_kwargs[k] = locals()[k]
1465
- callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
1466
-
1467
- latents = callback_outputs.pop("latents", latents)
1468
- prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
1469
-
1470
- # call the callback, if provided
1471
- if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
1472
- progress_bar.update()
1473
-
1474
- if XLA_AVAILABLE:
1475
- xm.mark_step()
1476
-
1477
- self._current_timestep = None
1478
- if output_type == "latent":
1479
- image = latents
1480
- else:
1481
- latents = self._unpack_latents(latents, height, width, self.vae_scale_factor)
1482
- latents = latents.to(self.vae.dtype)
1483
- latents_mean = (
1484
- torch.tensor(self.vae.config.latents_mean)
1485
- .view(1, self.vae.config.z_dim, 1, 1, 1)
1486
- .to(latents.device, latents.dtype)
1487
- )
1488
- latents_std = 1.0 / torch.tensor(self.vae.config.latents_std).view(1, self.vae.config.z_dim, 1, 1, 1).to(
1489
- latents.device, latents.dtype
1490
- )
1491
- latents = latents / latents_std + latents_mean
1492
- image = self.vae.decode(latents, return_dict=False)[0][:, :, 0]
1493
- image = self.image_processor.postprocess(image, output_type=output_type)
1494
-
1495
- # Offload all models
1496
- self.maybe_free_model_hooks()
1497
-
1498
- if not return_dict:
1499
- return (image,)
1500
-
1501
- return QwenImagePipelineOutput(images=image)