comdoleger commited on
Commit
a157720
·
verified ·
1 Parent(s): 59502c3

Upload extensions_built_in/diffusion_models/omnigen2/src/pipelines/omnigen2/pipeline_omnigen2_chat.py with huggingface_hub

Browse files
extensions_built_in/diffusion_models/omnigen2/src/pipelines/omnigen2/pipeline_omnigen2_chat.py ADDED
@@ -0,0 +1,830 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ OmniGen2 Diffusion Pipeline
3
+
4
+ Copyright 2025 BAAI, The OmniGen2 Team and The HuggingFace Team. All rights reserved.
5
+
6
+ Licensed under the Apache License, Version 2.0 (the "License");
7
+ you may not use this file except in compliance with the License.
8
+ You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing, software
13
+ distributed under the License is distributed on an "AS IS" BASIS,
14
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ See the License for the specific language governing permissions and
16
+ limitations under the License.
17
+ """
18
+
19
+ import inspect
20
+ from typing import Any, Callable, Dict, List, Optional, Tuple, Union
21
+
22
+ import math
23
+
24
+ from PIL import Image
25
+ import numpy as np
26
+ import torch
27
+ import torch.nn.functional as F
28
+
29
+ from transformers import Qwen2_5_VLForConditionalGeneration
30
+
31
+ from diffusers.models.autoencoders import AutoencoderKL
32
+ from ...models.transformers import OmniGen2Transformer2DModel
33
+ from ...models.transformers.repo import OmniGen2RotaryPosEmbed
34
+ from diffusers.schedulers import FlowMatchEulerDiscreteScheduler
35
+ from diffusers.utils import (
36
+ is_torch_xla_available,
37
+ logging,
38
+ )
39
+ from diffusers.utils.torch_utils import randn_tensor
40
+ from diffusers.pipelines.pipeline_utils import DiffusionPipeline
41
+
42
+ from dataclasses import dataclass
43
+
44
+ import PIL.Image
45
+
46
+ from diffusers.utils import BaseOutput
47
+
48
+ from src.pipelines.image_processor import OmniGen2ImageProcessor
49
+
50
+ if is_torch_xla_available():
51
+ import torch_xla.core.xla_model as xm
52
+
53
+ XLA_AVAILABLE = True
54
+ else:
55
+ XLA_AVAILABLE = False
56
+
57
+
58
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
59
+
60
+ @dataclass
61
+ class OmniGen2PipelineOutput(BaseOutput):
62
+ """
63
+ Output class for OmniGen2 pipeline.
64
+
65
+ Args:
66
+ images (Union[List[PIL.Image.Image], np.ndarray]):
67
+ List of denoised PIL images of length `batch_size` or numpy array of shape
68
+ `(batch_size, height, width, num_channels)`. Contains the generated images.
69
+ """
70
+ text: str
71
+ images: Union[List[PIL.Image.Image], np.ndarray]
72
+
73
+
74
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
75
+ def retrieve_timesteps(
76
+ scheduler,
77
+ num_inference_steps: Optional[int] = None,
78
+ device: Optional[Union[str, torch.device]] = None,
79
+ timesteps: Optional[List[int]] = None,
80
+ **kwargs,
81
+ ):
82
+ """
83
+ Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
84
+ custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
85
+
86
+ Args:
87
+ scheduler (`SchedulerMixin`):
88
+ The scheduler to get timesteps from.
89
+ num_inference_steps (`int`):
90
+ The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
91
+ must be `None`.
92
+ device (`str` or `torch.device`, *optional*):
93
+ The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
94
+ timesteps (`List[int]`, *optional*):
95
+ Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
96
+ `num_inference_steps` and `sigmas` must be `None`.
97
+ sigmas (`List[float]`, *optional*):
98
+ Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
99
+ `num_inference_steps` and `timesteps` must be `None`.
100
+
101
+ Returns:
102
+ `Tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
103
+ second element is the number of inference steps.
104
+ """
105
+ if timesteps is not None:
106
+ accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
107
+ if not accepts_timesteps:
108
+ raise ValueError(
109
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
110
+ f" timestep schedules. Please check whether you are using the correct scheduler."
111
+ )
112
+ scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
113
+ timesteps = scheduler.timesteps
114
+ num_inference_steps = len(timesteps)
115
+ else:
116
+ scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
117
+ timesteps = scheduler.timesteps
118
+ return timesteps, num_inference_steps
119
+
120
+
121
+ class OmniGen2ChatPipeline(DiffusionPipeline):
122
+ """
123
+ Pipeline for text-to-image generation using OmniGen2.
124
+
125
+ This pipeline implements a text-to-image generation model that uses:
126
+ - Qwen2.5-VL for text encoding
127
+ - A custom transformer architecture for image generation
128
+ - VAE for image encoding/decoding
129
+ - FlowMatchEulerDiscreteScheduler for noise scheduling
130
+
131
+ Args:
132
+ transformer (OmniGen2Transformer2DModel): The transformer model for image generation.
133
+ vae (AutoencoderKL): The VAE model for image encoding/decoding.
134
+ scheduler (FlowMatchEulerDiscreteScheduler): The scheduler for noise scheduling.
135
+ text_encoder (Qwen2_5_VLModel): The text encoder model.
136
+ tokenizer (Union[Qwen2Tokenizer, Qwen2TokenizerFast]): The tokenizer for text processing.
137
+ """
138
+
139
+ model_cpu_offload_seq = "mllm->transformer->vae"
140
+ def __init__(
141
+ self,
142
+ transformer: OmniGen2Transformer2DModel,
143
+ vae: AutoencoderKL,
144
+ scheduler: FlowMatchEulerDiscreteScheduler,
145
+ mllm: Qwen2_5_VLForConditionalGeneration,
146
+ processor,
147
+ ) -> None:
148
+ """
149
+ Initialize the OmniGen2 pipeline.
150
+
151
+ Args:
152
+ transformer: The transformer model for image generation.
153
+ vae: The VAE model for image encoding/decoding.
154
+ scheduler: The scheduler for noise scheduling.
155
+ text_encoder: The text encoder model.
156
+ tokenizer: The tokenizer for text processing.
157
+ """
158
+ super().__init__()
159
+
160
+ self.register_modules(
161
+ transformer=transformer,
162
+ vae=vae,
163
+ scheduler=scheduler,
164
+ mllm=mllm,
165
+ processor=processor
166
+ )
167
+ self.vae_scale_factor = (
168
+ 2 ** (len(self.vae.config.block_out_channels) - 1) if hasattr(self, "vae") and self.vae is not None else 8
169
+ )
170
+ self.image_processor = OmniGen2ImageProcessor(vae_scale_factor=self.vae_scale_factor * 2, do_resize=True)
171
+ self.default_sample_size = 128
172
+
173
+ def prepare_latents(
174
+ self,
175
+ batch_size: int,
176
+ num_channels_latents: int,
177
+ height: int,
178
+ width: int,
179
+ dtype: torch.dtype,
180
+ device: torch.device,
181
+ generator: Optional[torch.Generator],
182
+ latents: Optional[torch.FloatTensor] = None,
183
+ ) -> torch.FloatTensor:
184
+ """
185
+ Prepare the initial latents for the diffusion process.
186
+
187
+ Args:
188
+ batch_size: The number of images to generate.
189
+ num_channels_latents: The number of channels in the latent space.
190
+ height: The height of the generated image.
191
+ width: The width of the generated image.
192
+ dtype: The data type of the latents.
193
+ device: The device to place the latents on.
194
+ generator: The random number generator to use.
195
+ latents: Optional pre-computed latents to use instead of random initialization.
196
+
197
+ Returns:
198
+ torch.FloatTensor: The prepared latents tensor.
199
+ """
200
+ height = int(height) // self.vae_scale_factor
201
+ width = int(width) // self.vae_scale_factor
202
+
203
+ shape = (batch_size, num_channels_latents, height, width)
204
+
205
+ if latents is None:
206
+ latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
207
+ else:
208
+ latents = latents.to(device)
209
+ return latents
210
+
211
+ def encode_vae(self, img: torch.FloatTensor) -> torch.FloatTensor:
212
+ """
213
+ Encode an image into the VAE latent space.
214
+
215
+ Args:
216
+ img: The input image tensor to encode.
217
+
218
+ Returns:
219
+ torch.FloatTensor: The encoded latent representation.
220
+ """
221
+ z0 = self.vae.encode(img.to(dtype=self.vae.dtype)).latent_dist.sample()
222
+ if self.vae.config.shift_factor is not None:
223
+ z0 = z0 - self.vae.config.shift_factor
224
+ if self.vae.config.scaling_factor is not None:
225
+ z0 = z0 * self.vae.config.scaling_factor
226
+ z0 = z0.to(dtype=self.vae.dtype)
227
+ return z0
228
+
229
+ def prepare_image(
230
+ self,
231
+ images: Union[List[PIL.Image.Image], PIL.Image.Image],
232
+ batch_size: int,
233
+ num_images_per_prompt: int,
234
+ max_pixels: int,
235
+ max_side_length: int,
236
+ device: torch.device,
237
+ dtype: torch.dtype,
238
+ ) -> List[Optional[torch.FloatTensor]]:
239
+ """
240
+ Prepare input images for processing by encoding them into the VAE latent space.
241
+
242
+ Args:
243
+ images: Single image or list of images to process.
244
+ batch_size: The number of images to generate per prompt.
245
+ num_images_per_prompt: The number of images to generate for each prompt.
246
+ device: The device to place the encoded latents on.
247
+ dtype: The data type of the encoded latents.
248
+
249
+ Returns:
250
+ List[Optional[torch.FloatTensor]]: List of encoded latent representations for each image.
251
+ """
252
+ if batch_size == 1:
253
+ images = [images]
254
+ latents = []
255
+ for i, img in enumerate(images):
256
+ if img is not None and len(img) > 0:
257
+ ref_latents = []
258
+ for j, img_j in enumerate(img):
259
+ img_j = self.image_processor.preprocess(img_j, max_pixels=max_pixels, max_side_length=max_side_length)
260
+ ref_latents.append(self.encode_vae(img_j.to(device=device)).squeeze(0))
261
+ else:
262
+ ref_latents = None
263
+ for _ in range(num_images_per_prompt):
264
+ latents.append(ref_latents)
265
+
266
+ return latents
267
+
268
+ def _apply_chat_template(self, prompt: str, images: List = None):
269
+ if images is not None:
270
+ prompt = "".join(
271
+ [
272
+ f"<img{i}>: <|vision_start|><|image_pad|><|vision_end|>"
273
+ for i in range(1, len(images) + 1)
274
+ ]
275
+ ) + prompt
276
+ prompt = f"<|im_start|>system\nYou are a helpful assistant that generates high-quality images based on user instructions.<|im_end|>\n<|im_start|>user\n{prompt}<|im_end|>\n<|im_start|>assistant\n"
277
+ return prompt
278
+
279
+ def _get_qwen2_prompt_embeds(
280
+ self,
281
+ prompt: Union[str, List[str]],
282
+ input_images = None,
283
+ device: Optional[torch.device] = None,
284
+ use_only_text_hidden_states: bool = True,
285
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
286
+ """
287
+ Get prompt embeddings from the Qwen2 text encoder.
288
+
289
+ Args:
290
+ prompt: The prompt or list of prompts to encode.
291
+ device: The device to place the embeddings on. If None, uses the pipeline's device.
292
+
293
+ Returns:
294
+ Tuple[torch.Tensor, torch.Tensor]: A tuple containing:
295
+ - The prompt embeddings tensor
296
+ - The attention mask tensor
297
+
298
+ Raises:
299
+ Warning: If the input text is truncated due to sequence length limitations.
300
+ """
301
+ device = device or self._execution_device
302
+ prompt = [prompt] if isinstance(prompt, str) else prompt
303
+
304
+ inputs = self.processor(
305
+ text=prompt,
306
+ images=input_images,
307
+ videos=None,
308
+ padding=True,
309
+ return_tensors="pt",
310
+ )
311
+ inputs = inputs.to(device)
312
+
313
+ prompt_embeds = self.mllm(
314
+ **inputs,
315
+ output_hidden_states=True,
316
+ ).hidden_states[-1]
317
+
318
+ text_input_ids = inputs.input_ids
319
+ text_mask = inputs.attention_mask
320
+ if use_only_text_hidden_states:
321
+ mask = text_input_ids != self.mllm.config.image_token_id
322
+ mask = mask & text_mask
323
+ mask = mask.bool()
324
+
325
+ text_l = mask.sum(dim=-1)
326
+ max_l = text_l.max()
327
+ text_batch_size = prompt_embeds.size(0)
328
+ new_prompt_embeds = torch.zeros((text_batch_size, max_l, prompt_embeds.size(-1)), device=prompt_embeds.device, dtype=prompt_embeds.dtype)
329
+ new_text_mask = torch.zeros((text_batch_size, max_l), dtype=text_mask.dtype, device=text_mask.device)
330
+ for i in range(text_batch_size):
331
+ new_prompt_embeds[i, :text_l[i]] = prompt_embeds[i, mask[i]]
332
+ new_text_mask[i, :text_l[i]] = 1
333
+
334
+ prompt_embeds = new_prompt_embeds
335
+ text_mask = new_text_mask
336
+
337
+ prompt_embeds = prompt_embeds.to(dtype=self.mllm.dtype, device=device)
338
+ return prompt_embeds, text_mask
339
+
340
+
341
+ def encode_prompt(
342
+ self,
343
+ prompt: Union[str, List[str]],
344
+ input_images: Optional[Union[str, List[str]]] = None,
345
+ do_classifier_free_guidance: bool = True,
346
+ negative_prompt: Optional[Union[str, List[str]]] = None,
347
+ num_images_per_prompt: int = 1,
348
+ device: Optional[torch.device] = None,
349
+ prompt_embeds: Optional[torch.Tensor] = None,
350
+ negative_prompt_embeds: Optional[torch.Tensor] = None,
351
+ prompt_attention_mask: Optional[torch.Tensor] = None,
352
+ negative_prompt_attention_mask: Optional[torch.Tensor] = None,
353
+ max_sequence_length: int = 256,
354
+ use_text_encoder_penultimate_layer_feats: bool = False
355
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
356
+ r"""
357
+ Encodes the prompt into text encoder hidden states.
358
+
359
+ Args:
360
+ prompt (`str` or `List[str]`, *optional*):
361
+ prompt to be encoded
362
+ negative_prompt (`str` or `List[str]`, *optional*):
363
+ The prompt not to guide the image generation. If not defined, one has to pass `negative_prompt_embeds`
364
+ instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is less than `1`). For
365
+ Lumina-T2I, this should be "".
366
+ do_classifier_free_guidance (`bool`, *optional*, defaults to `True`):
367
+ whether to use classifier free guidance or not
368
+ num_images_per_prompt (`int`, *optional*, defaults to 1):
369
+ number of images that should be generated per prompt
370
+ device: (`torch.device`, *optional*):
371
+ torch device to place the resulting embeddings on
372
+ prompt_embeds (`torch.Tensor`, *optional*):
373
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
374
+ provided, text embeddings will be generated from `prompt` input argument.
375
+ negative_prompt_embeds (`torch.Tensor`, *optional*):
376
+ Pre-generated negative text embeddings. For Lumina-T2I, it's should be the embeddings of the "" string.
377
+ max_sequence_length (`int`, defaults to `256`):
378
+ Maximum sequence length to use for the prompt.
379
+ """
380
+ device = device or self._execution_device
381
+
382
+ prompt = [prompt] if isinstance(prompt, str) else prompt
383
+
384
+ if prompt is not None:
385
+ batch_size = len(prompt)
386
+ else:
387
+ batch_size = prompt_embeds.shape[0]
388
+ if prompt_embeds is None:
389
+ prompt_embeds, prompt_attention_mask = self._get_qwen2_prompt_embeds(
390
+ prompt=prompt,
391
+ input_images=input_images,
392
+ device=device,
393
+ )
394
+
395
+ batch_size, seq_len, _ = prompt_embeds.shape
396
+ # duplicate text embeddings and attention mask for each generation per prompt, using mps friendly method
397
+ prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
398
+ prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
399
+ prompt_attention_mask = prompt_attention_mask.repeat(num_images_per_prompt, 1)
400
+ prompt_attention_mask = prompt_attention_mask.view(batch_size * num_images_per_prompt, -1)
401
+
402
+ # Get negative embeddings for classifier free guidance
403
+ negative_prompt_embeds, negative_prompt_attention_mask = None, None
404
+ if do_classifier_free_guidance and negative_prompt_embeds is None:
405
+ negative_prompt = negative_prompt if negative_prompt is not None else ""
406
+
407
+ # Normalize str to list
408
+ negative_prompt = batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
409
+ negative_prompt = [self._apply_chat_template(_negative_prompt) for _negative_prompt in negative_prompt]
410
+
411
+ if prompt is not None and type(prompt) is not type(negative_prompt):
412
+ raise TypeError(
413
+ f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
414
+ f" {type(prompt)}."
415
+ )
416
+ elif isinstance(negative_prompt, str):
417
+ negative_prompt = [negative_prompt]
418
+ elif batch_size != len(negative_prompt):
419
+ raise ValueError(
420
+ f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
421
+ f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
422
+ " the batch size of `prompt`."
423
+ )
424
+ negative_prompt_embeds, negative_prompt_attention_mask = self._get_qwen2_prompt_embeds(
425
+ prompt=negative_prompt,
426
+ device=device,
427
+ )
428
+
429
+ batch_size, seq_len, _ = negative_prompt_embeds.shape
430
+ # duplicate text embeddings and attention mask for each generation per prompt, using mps friendly method
431
+ negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_images_per_prompt, 1)
432
+ negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
433
+ negative_prompt_attention_mask = negative_prompt_attention_mask.repeat(num_images_per_prompt, 1)
434
+ negative_prompt_attention_mask = negative_prompt_attention_mask.view(
435
+ batch_size * num_images_per_prompt, -1
436
+ )
437
+
438
+ return prompt_embeds, prompt_attention_mask, negative_prompt_embeds, negative_prompt_attention_mask
439
+
440
+ @property
441
+ def num_timesteps(self):
442
+ return self._num_timesteps
443
+
444
+ @property
445
+ def text_guidance_scale(self):
446
+ return self._text_guidance_scale
447
+
448
+ @property
449
+ def image_guidance_scale(self):
450
+ return self._image_guidance_scale
451
+
452
+ @property
453
+ def cfg_range(self):
454
+ return self._cfg_range
455
+
456
+ def prepare_inputs_for_text_generation(self, prompts, input_images, device):
457
+ if isinstance(prompts, str):
458
+ prompts = [prompts]
459
+
460
+ ori_padding_side = self.processor.tokenizer.padding_side
461
+ self.processor.tokenizer.padding_side = "left"
462
+ inputs = self.processor(
463
+ text=prompts,
464
+ images=input_images,
465
+ videos=None,
466
+ padding=True,
467
+ return_tensors="pt",
468
+ ).to(device)
469
+ self.processor.tokenizer.padding_side = ori_padding_side
470
+ return inputs
471
+
472
+ def generate_text(self, prompt, input_images):
473
+ inputs = self.prepare_inputs_for_text_generation(
474
+ prompt, input_images, self.mllm.device
475
+ )
476
+ generated_ids = self.mllm.generate(
477
+ **inputs,
478
+ tokenizer=self.processor.tokenizer,
479
+ max_new_tokens=256,
480
+ stop_strings=["<|im_end|>", "<|img|>", "<|endoftext|>"],
481
+ ) # stop_words=[151643, 151645, 151665]
482
+ generated_ids_trimmed = [
483
+ out_ids[len(in_ids) :]
484
+ for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
485
+ ]
486
+ output_texts = self.processor.batch_decode(
487
+ generated_ids_trimmed,
488
+ # skip_special_tokens=True,
489
+ skip_special_tokens=False,
490
+ clean_up_tokenization_spaces=False,
491
+ )
492
+ return output_texts
493
+
494
+ def generate_image(
495
+ self,
496
+ prompt: Optional[Union[str, List[str]]] = None,
497
+ negative_prompt: Optional[Union[str, List[str]]] = None,
498
+ prompt_embeds: Optional[torch.FloatTensor] = None,
499
+ negative_prompt_embeds: Optional[torch.FloatTensor] = None,
500
+ prompt_attention_mask: Optional[torch.LongTensor] = None,
501
+ negative_prompt_attention_mask: Optional[torch.LongTensor] = None,
502
+ use_text_encoder_penultimate_layer_feats: bool = False,
503
+ max_sequence_length: Optional[int] = None,
504
+ callback_on_step_end_tensor_inputs: Optional[List[str]] = None,
505
+ input_images: Optional[List[PIL.Image.Image]] = None,
506
+ num_images_per_prompt: int = 1,
507
+ height: Optional[int] = None,
508
+ width: Optional[int] = None,
509
+ max_pixels: int = 1024 * 1024,
510
+ max_input_image_side_length: int = 1024,
511
+ align_res: bool = True,
512
+ num_inference_steps: int = 28,
513
+ text_guidance_scale: float = 4.0,
514
+ image_guidance_scale: float = 1.0,
515
+ cfg_range: Tuple[float, float] = (0.0, 1.0),
516
+ attention_kwargs: Optional[Dict[str, Any]] = None,
517
+ timesteps: List[int] = None,
518
+ generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
519
+ latents: Optional[torch.FloatTensor] = None,
520
+ output_type: Optional[str] = "pil",
521
+ return_dict: bool = True,
522
+ verbose: bool = False,
523
+ step_func=None,
524
+ ):
525
+ height = height or self.default_sample_size * self.vae_scale_factor
526
+ width = width or self.default_sample_size * self.vae_scale_factor
527
+
528
+ self._text_guidance_scale = text_guidance_scale
529
+ self._image_guidance_scale = image_guidance_scale
530
+ self._cfg_range = cfg_range
531
+ self._attention_kwargs = attention_kwargs
532
+
533
+ # 2. Define call parameters
534
+ if prompt is not None and isinstance(prompt, str):
535
+ batch_size = 1
536
+ elif prompt is not None and isinstance(prompt, list):
537
+ batch_size = len(prompt)
538
+ else:
539
+ batch_size = prompt_embeds.shape[0]
540
+
541
+ device = self._execution_device
542
+
543
+ # 3. Encode input promptb
544
+ (
545
+ prompt_embeds,
546
+ prompt_attention_mask,
547
+ negative_prompt_embeds,
548
+ negative_prompt_attention_mask,
549
+ ) = self.encode_prompt(
550
+ prompt,
551
+ input_images,
552
+ self.text_guidance_scale > 1.0,
553
+ negative_prompt=negative_prompt,
554
+ num_images_per_prompt=num_images_per_prompt,
555
+ device=device,
556
+ prompt_embeds=prompt_embeds,
557
+ negative_prompt_embeds=negative_prompt_embeds,
558
+ prompt_attention_mask=prompt_attention_mask,
559
+ negative_prompt_attention_mask=negative_prompt_attention_mask,
560
+ max_sequence_length=max_sequence_length,
561
+ use_text_encoder_penultimate_layer_feats=use_text_encoder_penultimate_layer_feats
562
+ )
563
+
564
+ dtype = self.vae.dtype
565
+ # 3. Prepare control image
566
+ ref_latents = self.prepare_image(
567
+ images=input_images,
568
+ batch_size=batch_size,
569
+ num_images_per_prompt=num_images_per_prompt,
570
+ max_pixels=max_pixels,
571
+ max_side_length=max_input_image_side_length,
572
+ device=device,
573
+ dtype=dtype,
574
+ )
575
+
576
+ if input_images is None:
577
+ input_images = []
578
+
579
+ if len(input_images) == 1 and align_res:
580
+ width, height = ref_latents[0][0].shape[-1] * self.vae_scale_factor, ref_latents[0][0].shape[-2] * self.vae_scale_factor
581
+ ori_width, ori_height = width, height
582
+ else:
583
+ ori_width, ori_height = width, height
584
+
585
+ cur_pixels = height * width
586
+ ratio = (max_pixels / cur_pixels) ** 0.5
587
+ ratio = min(ratio, 1.0)
588
+
589
+ height, width = int(height * ratio) // 16 * 16, int(width * ratio) // 16 * 16
590
+
591
+ if len(input_images) == 0:
592
+ self._image_guidance_scale = 1
593
+
594
+ # 4. Prepare latents.
595
+ latent_channels = self.transformer.config.in_channels
596
+ latents = self.prepare_latents(
597
+ batch_size * num_images_per_prompt,
598
+ latent_channels,
599
+ height,
600
+ width,
601
+ prompt_embeds.dtype,
602
+ device,
603
+ generator,
604
+ latents,
605
+ )
606
+
607
+ freqs_cis = OmniGen2RotaryPosEmbed.get_freqs_cis(
608
+ self.transformer.config.axes_dim_rope,
609
+ self.transformer.config.axes_lens,
610
+ theta=10000,
611
+ )
612
+
613
+ image = self.processing(
614
+ latents=latents,
615
+ ref_latents=ref_latents,
616
+ prompt_embeds=prompt_embeds,
617
+ freqs_cis=freqs_cis,
618
+ negative_prompt_embeds=negative_prompt_embeds,
619
+ prompt_attention_mask=prompt_attention_mask,
620
+ negative_prompt_attention_mask=negative_prompt_attention_mask,
621
+ num_inference_steps=num_inference_steps,
622
+ timesteps=timesteps,
623
+ device=device,
624
+ dtype=dtype,
625
+ verbose=verbose,
626
+ step_func=step_func,
627
+ )
628
+
629
+ image = F.interpolate(image, size=(ori_height, ori_width), mode='bilinear')
630
+
631
+ image = self.image_processor.postprocess(image, output_type=output_type)
632
+
633
+ # Offload all models
634
+ self.maybe_free_model_hooks()
635
+ return image
636
+
637
+ @torch.no_grad()
638
+ def __call__(
639
+ self,
640
+ prompt: Optional[Union[str, List[str]]] = None,
641
+ negative_prompt: Optional[Union[str, List[str]]] = None,
642
+ prompt_embeds: Optional[torch.FloatTensor] = None,
643
+ negative_prompt_embeds: Optional[torch.FloatTensor] = None,
644
+ prompt_attention_mask: Optional[torch.LongTensor] = None,
645
+ negative_prompt_attention_mask: Optional[torch.LongTensor] = None,
646
+ use_text_encoder_penultimate_layer_feats: bool = False,
647
+ max_sequence_length: Optional[int] = None,
648
+ callback_on_step_end_tensor_inputs: Optional[List[str]] = None,
649
+ input_images: Optional[List[PIL.Image.Image]] = None,
650
+ num_images_per_prompt: int = 1,
651
+ height: Optional[int] = 1024,
652
+ width: Optional[int] = 1024,
653
+ max_pixels: Optional[int] = 1024 * 1024,
654
+ max_input_image_side_length: int = 1024,
655
+ align_res: bool = True,
656
+ num_inference_steps: int = 28,
657
+ text_guidance_scale: float = 4.0,
658
+ image_guidance_scale: float = 1.0,
659
+ cfg_range: Tuple[float, float] = (0.0, 1.0),
660
+ attention_kwargs: Optional[Dict[str, Any]] = None,
661
+ timesteps: List[int] = None,
662
+ generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
663
+ latents: Optional[torch.FloatTensor] = None,
664
+ output_type: Optional[str] = "pil",
665
+ return_dict: bool = True,
666
+ verbose: bool = False,
667
+ step_func=None,
668
+ ):
669
+ assert isinstance(prompt, str), "prompt must be a string since chat mode only support one prompt per turn"
670
+
671
+ # input_images = self.preprocess_images(input_images, max_input_image_size)
672
+ prompt = self._apply_chat_template(prompt, input_images)
673
+ generated_text = self.generate_text(prompt, input_images)[0]
674
+
675
+ images = None
676
+ if generated_text.startswith("<|img|>"):
677
+ #TODO: reuse the hidden state when generate text instead of re-generating
678
+ prompt = prompt + generated_text.split("<|img|>")[0]
679
+ images = self.generate_image(
680
+ prompt=prompt,
681
+ negative_prompt=negative_prompt,
682
+ use_text_encoder_penultimate_layer_feats=use_text_encoder_penultimate_layer_feats,
683
+ max_sequence_length=max_sequence_length,
684
+ input_images=input_images,
685
+ num_images_per_prompt=num_images_per_prompt,
686
+ height=height,
687
+ width=width,
688
+ max_pixels=max_pixels,
689
+ max_input_image_side_length=max_input_image_side_length,
690
+ align_res=align_res,
691
+ num_inference_steps=num_inference_steps,
692
+ text_guidance_scale=text_guidance_scale,
693
+ image_guidance_scale=image_guidance_scale,
694
+ cfg_range=cfg_range,
695
+ timesteps=timesteps,
696
+ generator=generator,
697
+ latents=latents,
698
+ return_dict=False,
699
+ verbose=verbose,
700
+ step_func=step_func,
701
+ )
702
+
703
+ generated_text = generated_text.replace("<|im_end|>", "")
704
+ if not return_dict:
705
+ return generated_text, images
706
+ else:
707
+ return OmniGen2PipelineOutput(text=generated_text, images=images)
708
+
709
+ def processing(
710
+ self,
711
+ latents,
712
+ ref_latents,
713
+ prompt_embeds,
714
+ freqs_cis,
715
+ negative_prompt_embeds,
716
+ prompt_attention_mask,
717
+ negative_prompt_attention_mask,
718
+ num_inference_steps,
719
+ timesteps,
720
+ device,
721
+ dtype,
722
+ verbose,
723
+ step_func=None
724
+ ):
725
+ batch_size = latents.shape[0]
726
+
727
+ timesteps, num_inference_steps = retrieve_timesteps(
728
+ self.scheduler,
729
+ num_inference_steps,
730
+ device,
731
+ timesteps,
732
+ num_tokens=latents.shape[-2] * latents.shape[-1]
733
+ )
734
+ num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
735
+ self._num_timesteps = len(timesteps)
736
+
737
+ with self.progress_bar(total=num_inference_steps) as progress_bar:
738
+ for i, t in enumerate(timesteps):
739
+ model_pred = self.predict(
740
+ t=t,
741
+ latents=latents,
742
+ prompt_embeds=prompt_embeds,
743
+ freqs_cis=freqs_cis,
744
+ prompt_attention_mask=prompt_attention_mask,
745
+ ref_image_hidden_states=ref_latents,
746
+ )
747
+
748
+ text_guidance_scale = self.text_guidance_scale if self.cfg_range[0] <= i / len(timesteps) <= self.cfg_range[1] else 1.0
749
+ image_guidance_scale = self.image_guidance_scale if self.cfg_range[0] <= i / len(timesteps) <= self.cfg_range[1] else 1.0
750
+ if text_guidance_scale > 1.0 and image_guidance_scale > 1.0:
751
+ model_pred_ref = self.predict(
752
+ t=t,
753
+ latents=latents,
754
+ prompt_embeds=negative_prompt_embeds,
755
+ freqs_cis=freqs_cis,
756
+ prompt_attention_mask=negative_prompt_attention_mask,
757
+ ref_image_hidden_states=ref_latents,
758
+ )
759
+
760
+ if image_guidance_scale != 1:
761
+ model_pred_uncond = self.predict(
762
+ t=t,
763
+ latents=latents,
764
+ prompt_embeds=negative_prompt_embeds,
765
+ freqs_cis=freqs_cis,
766
+ prompt_attention_mask=negative_prompt_attention_mask,
767
+ ref_image_hidden_states=None,
768
+ )
769
+ else:
770
+ model_pred_uncond = torch.zeros_like(model_pred)
771
+
772
+ model_pred = model_pred_uncond + image_guidance_scale * (model_pred_ref - model_pred_uncond) + \
773
+ text_guidance_scale * (model_pred - model_pred_ref)
774
+ elif text_guidance_scale > 1.0:
775
+ model_pred_uncond = self.predict(
776
+ t=t,
777
+ latents=latents,
778
+ prompt_embeds=negative_prompt_embeds,
779
+ freqs_cis=freqs_cis,
780
+ prompt_attention_mask=negative_prompt_attention_mask,
781
+ ref_image_hidden_states=None,
782
+ )
783
+ model_pred = model_pred_uncond + text_guidance_scale * (model_pred - model_pred_uncond)
784
+
785
+ latents = self.scheduler.step(model_pred, t, latents, return_dict=False)[0]
786
+
787
+ latents = latents.to(dtype=dtype)
788
+
789
+ if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
790
+ progress_bar.update()
791
+
792
+ if step_func is not None:
793
+ step_func(i, self._num_timesteps)
794
+
795
+ latents = latents.to(dtype=dtype)
796
+ if self.vae.config.scaling_factor is not None:
797
+ latents = latents / self.vae.config.scaling_factor
798
+ if self.vae.config.shift_factor is not None:
799
+ latents = latents + self.vae.config.shift_factor
800
+ image = self.vae.decode(latents, return_dict=False)[0]
801
+
802
+ return image
803
+
804
+ def predict(
805
+ self,
806
+ t,
807
+ latents,
808
+ prompt_embeds,
809
+ freqs_cis,
810
+ prompt_attention_mask,
811
+ ref_image_hidden_states,
812
+ ):
813
+ # broadcast to batch dimension in a way that's compatible with ONNX/Core ML
814
+ timestep = t.expand(latents.shape[0]).to(latents.dtype)
815
+
816
+ batch_size, num_channels_latents, height, width = latents.shape
817
+
818
+ optional_kwargs = {}
819
+ if 'ref_image_hidden_states' in set(inspect.signature(self.transformer.forward).parameters.keys()):
820
+ optional_kwargs['ref_image_hidden_states'] = ref_image_hidden_states
821
+
822
+ model_pred = self.transformer(
823
+ latents,
824
+ timestep,
825
+ prompt_embeds,
826
+ freqs_cis,
827
+ prompt_attention_mask,
828
+ **optional_kwargs
829
+ )
830
+ return model_pred