omer11a commited on
Commit
de34da3
1 Parent(s): 9fc1299

Upload app

Browse files
Files changed (6) hide show
  1. app.py +354 -0
  2. bounded_attention.py +522 -0
  3. injection_utils.py +234 -0
  4. pipeline_stable_diffusion_xl_opt.py +968 -0
  5. requirements.txt +22 -0
  6. utils.py +231 -0
app.py ADDED
@@ -0,0 +1,354 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from diffusers import DDIMScheduler
2
+ from pipeline_stable_diffusion_xl_opt import StableDiffusionXLPipeline
3
+ from injection_utils import regiter_attention_editor_diffusers
4
+ from bounded_attention import BoundedAttention
5
+ from pytorch_lightning import seed_everything
6
+
7
+ import gradio as gr
8
+ import torch
9
+ import numpy as np
10
+ from PIL import Image, ImageDraw
11
+
12
+ from functools import partial
13
+
14
+ RESOLUTION = 512
15
+ MIN_SIZE = 0.01
16
+ COLORS = ["red", "blue", "green", "orange", "purple", "turquoise", "olive"]
17
+
18
+
19
+ def inference(
20
+ device,
21
+ model,
22
+ boxes,
23
+ prompts,
24
+ subject_token_indices,
25
+ filter_token_indices,
26
+ num_tokens,
27
+ init_step_size,
28
+ final_step_size,
29
+ num_clusters_per_subject,
30
+ cross_loss_scale,
31
+ self_loss_scale,
32
+ classifier_free_guidance_scale,
33
+ num_iterations,
34
+ loss_threshold,
35
+ num_guidance_steps,
36
+ seed,
37
+ ):
38
+ seed_everything(seed)
39
+ start_code = torch.randn([len(prompts), 4, 128, 128], device=device)
40
+ editor = BoundedAttention(
41
+ boxes,
42
+ prompts,
43
+ subject_token_indices,
44
+ list(range(70, 82)),
45
+ list(range(70, 82)),
46
+ eos_token_index=num_tokens + 1,
47
+ cross_loss_coef=cross_loss_scale,
48
+ self_loss_coef=self_loss_scale,
49
+ filter_token_indices=filter_token_indices,
50
+ max_guidance_iter=num_guidance_steps,
51
+ max_guidance_iter_per_step=num_iterations,
52
+ start_step_size=init_step_size,
53
+ end_step_size=final_step_size,
54
+ loss_stopping_value=loss_threshold,
55
+ num_clusters_per_box=num_clusters_per_subject,
56
+ debug=False,
57
+ )
58
+
59
+ regiter_attention_editor_diffusers(model, editor)
60
+ return model(prompts, latents=start_code, guidance_scale=classifier_free_guidance_scale).images
61
+
62
+
63
+ def generate(
64
+ device,
65
+ model,
66
+ prompt,
67
+ subject_token_indices,
68
+ filter_token_indices,
69
+ num_tokens,
70
+ init_step_size,
71
+ final_step_size,
72
+ num_clusters_per_subject,
73
+ cross_loss_scale,
74
+ self_loss_scale,
75
+ classifier_free_guidance_scale,
76
+ batch_size,
77
+ num_iterations,
78
+ loss_threshold,
79
+ num_guidance_steps,
80
+ seed,
81
+ boxes
82
+ ):
83
+ if 'boxes' not in boxes:
84
+ boxes['boxes'] = []
85
+
86
+ boxes = boxes['boxes']
87
+ subject_token_indices = convert_token_indices(subject_token_indices, nested=True)
88
+ if len(boxes) != len(subject_token_indices):
89
+ raise ValueError("""
90
+ The number of boxes should be equal to the number of subject token indices.
91
+ Number of boxes drawn: {}, number of grounding tokens: {}.
92
+ """.format(len(boxes), len(subject_token_indices)))
93
+
94
+ filter_token_indices = convert_token_indices(filter_token_indices) if len(filter_token_indices.strip()) > 0 else None
95
+ num_tokens = int(num_tokens) if len(num_tokens.strip()) > 0 else None
96
+ prompts = [prompt.strip('.').strip(',').strip()] * batch_size
97
+
98
+ images = inference(
99
+ device, model, boxes, prompts, subject_token_indices, filter_token_indices, num_tokens, init_step_size,
100
+ final_step_size, num_clusters_per_subject, cross_loss_scale, self_loss_scale, classifier_free_guidance_scale,
101
+ num_iterations, loss_threshold, num_guidance_steps, seed)
102
+
103
+ blank_samples = batch_size % 2 if batch_size > 1 else 0
104
+ images = [gr.Image.update(value=x, visible=True) for i, x in enumerate(images)] \
105
+ + [gr.Image.update(value=None, visible=True) for _ in range(blank_samples)] \
106
+ + [gr.Image.update(value=None, visible=False) for _ in range(4 - batch_size - blank_samples)]
107
+
108
+ return images
109
+
110
+
111
+ def convert_token_indices(token_indices, nested=False):
112
+ if nested:
113
+ return [convert_token_indices(indices, nested=False) for indices in token_indices.split(';')]
114
+
115
+ return [int(index.strip()) for index in token_indices.split(',') if len(index.strip()) > 0]
116
+
117
+
118
+ def draw(boxes, mask):
119
+ print('Called draw')
120
+ print('before boxes', boxes)
121
+ if mask.ndim == 3:
122
+ mask = 255 - mask[..., 0]
123
+
124
+ mask = (mask != 0).astype('uint8') * 255
125
+ if mask.sum() > 0:
126
+ x1x2 = np.where(mask.max(0) != 0)[0] / RESOLUTION
127
+ y1y2 = np.where(mask.max(1) != 0)[0] / RESOLUTION
128
+ y1, y2 = y1y2.min(), y1y2.max()
129
+ x1, x2 = x1x2.min(), x1x2.max()
130
+
131
+ if (x2 - x1 > MIN_SIZE) and (y2 - y1 > MIN_SIZE):
132
+ boxes.append((x1, y1, x2, y2))
133
+ layout_image = draw_boxes(np.array(boxes) * RESOLUTION)
134
+
135
+ print('after boxes', boxes)
136
+ return [boxes, None, layout_image]
137
+
138
+
139
+ def draw_boxes(boxes):
140
+ if len(boxes) == 0:
141
+ return None
142
+
143
+ image = Image.new('RGB', (RESOLUTION, RESOLUTION), (255, 255, 255))
144
+ drawing = ImageDraw.Draw(image)
145
+ print(boxes)
146
+ for i, box in enumerate(boxes):
147
+ drawing.rectangle(box, outline=COLORS[i % len(COLORS)], width=4)
148
+
149
+ return image
150
+
151
+
152
+ def clear(batch_size):
153
+ blank_samples = batch_size % 2 if batch_size > 1 else 0
154
+ out_images = [gr.Image.update(value=None, visible=True) for i in range(batch_size)]
155
+ return [[], None, None] + out_images
156
+
157
+
158
+ def main():
159
+ css = """
160
+ #paper-info a {
161
+ color:#008AD7;
162
+ text-decoration: none;
163
+ }
164
+ #paper-info a:hover {
165
+ cursor: pointer;
166
+ text-decoration: none;
167
+ }
168
+
169
+ .tooltip {
170
+ color: #555;
171
+ position: relative;
172
+ display: inline-block;
173
+ cursor: pointer;
174
+ }
175
+
176
+ .tooltip .tooltiptext {
177
+ visibility: hidden;
178
+ width: 400px;
179
+ background-color: #555;
180
+ color: #fff;
181
+ text-align: center;
182
+ padding: 5px;
183
+ border-radius: 5px;
184
+ position: absolute;
185
+ z-index: 1; /* Set z-index to 1 */
186
+ left: 10px;
187
+ top: 100%;
188
+ opacity: 0;
189
+ transition: opacity 0.3s;
190
+ }
191
+
192
+ .tooltip:hover .tooltiptext {
193
+ visibility: visible;
194
+ opacity: 1;
195
+ z-index: 9999; /* Set a high z-index value when hovering */
196
+ }
197
+ """
198
+
199
+ device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
200
+ model_path = "stabilityai/stable-diffusion-xl-base-1.0"
201
+ scheduler = DDIMScheduler(beta_start=0.00085, beta_end=0.012, beta_schedule="scaled_linear", clip_sample=False, set_alpha_to_one=False)
202
+ model = StableDiffusionXLPipeline.from_pretrained(model_path, scheduler=scheduler, torch_dtype=torch.float16).to(device)
203
+ model.unet.set_default_attn_processor()
204
+ model.enable_xformers_memory_efficient_attention()
205
+ model.enable_sequential_cpu_offload()
206
+
207
+ with gr.Blocks(
208
+ css=css,
209
+ title="Bounded Attention demo",
210
+ ) as demo:
211
+ description = """<p style="text-align: center; font-weight: bold;">
212
+ <span style="font-size: 28px">Bounded Attention</span>
213
+ <br>
214
+ <span style="font-size: 18px" id="paper-info">
215
+ [<a href="https://omer11a.github.io/bounded-attention/" target="_blank">Project Page</a>]
216
+ [<a href=" " target="_blank">Paper</a>]
217
+ [<a href="https://github.com/omer11a/bounded-attention" target="_blank">GitHub</a>]
218
+ </span>
219
+ </p>
220
+ """
221
+ gr.HTML(description)
222
+ with gr.Column():
223
+ prompt = gr.Textbox(
224
+ label="Text prompt",
225
+ )
226
+
227
+ subject_token_indices = gr.Textbox(
228
+ label="The token indices of each subject (separate indices for the same subject with commas, and between different subjects with semicolons)",
229
+ )
230
+
231
+ filter_token_indices = gr.Textbox(
232
+ label="The token indices to filter, i.e. conjunctions, number, postional relations, etc. (if left empty, this will be automatically inferred)",
233
+ )
234
+
235
+ num_tokens = gr.Textbox(
236
+ label="The number of tokens in the prompt (can be left empty, but we recommend filling this, so we can verify your input, as sometimes rare words are split into more than one token)",
237
+ )
238
+
239
+ with gr.Row():
240
+ sketch_pad = gr.Sketchpad(label="Sketch Pad", shape=(RESOLUTION, RESOLUTION))
241
+ layout_image = gr.Image(type="pil", label="Bounding Boxes")
242
+ out_images = gr.Image(type="pil", visible=True, label="Generated Image")
243
+
244
+ with gr.Row():
245
+ clear_button = gr.Button(value='Clear')
246
+ generate_button = gr.Button(value='Generate')
247
+
248
+ with gr.Accordion("Advanced Options", open=False):
249
+ with gr.Column():
250
+ description = """
251
+ <div class="tooltip">Batch size &#9432
252
+ <span class="tooltiptext">The number of images to generate.</span>
253
+ </div>
254
+ <div class="tooltip">Initial step size &#9432
255
+ <span class="tooltiptext">The initial step size of the linear step size scheduler when performing guidance.</span>
256
+ </div>
257
+ <div class="tooltip">Final step size &#9432
258
+ <span class="tooltiptext">The final step size of the linear step size scheduler when performing guidance.</span>
259
+ </div>
260
+ <div class="tooltip">Number of self-attention clusters per subject &#9432
261
+ <span class="tooltiptext">Determines the number of clusters when clustering the self-attention maps (#clusters = #subject x #clusters_per_subject). Changing this value might improve semantics (adherence to the prompt), especially when the subjects exceed their bounding boxes.</span>
262
+ </div>
263
+ <div class="tooltip">Cross-attention loss scale factor &#9432
264
+ <span class="tooltiptext">The scale factor of the cross-attention loss term. Increasing it will improve semantic control (adherence to the prompt), but may reduce image quality.</span>
265
+ </div>
266
+ <div class="tooltip">Self-attention loss scale factor &#9432
267
+ <span class="tooltiptext">The scale factor of the self-attention loss term. Increasing it will improve layout control (adherence to the bounding boxes), but may reduce image quality.</span>
268
+ </div>
269
+ <div class="tooltip">Classifier-free guidance scale &#9432
270
+ <span class="tooltiptext">The scale factor of classifier-free guidance.</span>
271
+ </div>
272
+ <div class="tooltip" >Number of Gradient Descent iterations per timestep &#9432
273
+ <span class="tooltiptext">The number of Gradient Descent iterations for each timestep when performing guidance.</span>
274
+ </div>
275
+ <div class="tooltip" >Loss Threshold &#9432
276
+ <span class="tooltiptext">If the loss is below the threshold, Gradient Descent stops for that timestep. </span>
277
+ </div>
278
+ <div class="tooltip" >Number of guidance steps &#9432
279
+ <span class="tooltiptext">The number of timesteps in which to perform guidance.</span>
280
+ </div>
281
+ """
282
+ gr.HTML(description)
283
+ batch_size = gr.Slider(minimum=1, maximum=5, step=1, value=1, label="Number of samples")
284
+ init_step_size = gr.Slider(minimum=0, maximum=50, step=0.5, value=25, label="Initial step size")
285
+ final_step_size = gr.Slider(minimum=0, maximum=20, step=0.5, value=10, label="Final step size")
286
+ num_clusters_per_subject = gr.Slider(minimum=0, maximum=5, step=0.5, value=3, label="Number of clusters per subject")
287
+ cross_loss_scale = gr.Slider(minimum=0, maximum=2, step=0.1, value=1, label="Cross-attention loss scale factor")
288
+ self_loss_scale = gr.Slider(minimum=0, maximum=2, step=0.1, value=1, label="Self-attention loss scale factor")
289
+ classifier_free_guidance_scale = gr.Slider(minimum=0, maximum=50, step=0.5, value=7.5, label="Classifier-free guidance Scale")
290
+ num_iterations = gr.Slider(minimum=0, maximum=10, step=1, value=5, label="Number of Gradient Descent iterations")
291
+ loss_threshold = gr.Slider(minimum=0, maximum=1, step=0.1, value=0.2, label="Loss threshold")
292
+ num_guidance_steps = gr.Slider(minimum=10, maximum=20, step=1, value=15, label="Number of timesteps to perform guidance")
293
+ seed = gr.Slider(minimum=0, maximum=1000, step=1, value=445, label="Random Seed")
294
+
295
+ boxes = gr.State([])
296
+
297
+ demo.load(
298
+ clear,
299
+ inputs=[batch_size],
300
+ outputs=[boxes, sketch_pad, layout_image, out_images],
301
+ queue=False
302
+ )
303
+
304
+ sketch_pad.edit(
305
+ draw,
306
+ inputs=[boxes, sketch_pad],
307
+ outputs=[boxes, sketch_pad, layout_image],
308
+ queue=False,
309
+ )
310
+
311
+ clear_button.click(
312
+ clear,
313
+ inputs=[batch_size],
314
+ outputs=[boxes, sketch_pad, layout_image, out_images],
315
+ queue=False,
316
+ )
317
+
318
+ generate_button.click(
319
+ fn=partial(generate, device, model),
320
+ inputs=[
321
+ prompt, subject_token_indices, filter_token_indices, num_tokens,
322
+ init_step_size, final_step_size, num_clusters_per_subject, cross_loss_scale, self_loss_scale,
323
+ classifier_free_guidance_scale, batch_size, num_iterations, loss_threshold, num_guidance_steps,
324
+ seed,
325
+ boxes,
326
+ ],
327
+ outputs=[out_images],
328
+ queue=True,
329
+ )
330
+
331
+ #with gr.Column():
332
+ # gr.Examples(
333
+ # examples=[
334
+ # [
335
+ # [[0.35, 0.4, 0.65, 0.9], [0, 0.6, 0.3, 0.9], [0.7, 0.55, 1, 0.85]],
336
+ # "3D Pixar animation of a cute unicorn and a pink hedgehog and a nerdy owl traveling in a magical forest",
337
+ # "7,8,17;11,12,17;15,16,17",
338
+ # "5,6,9,10,13,14,18,19",
339
+ # 286,
340
+ # ],
341
+ # ],
342
+ # inputs=[boxes, prompt, subject_token_indices, filter_token_indices, seed],
343
+ # outputs=None,
344
+ # fn=None,
345
+ # cache_examples=False,
346
+ # )
347
+ description = """<p> The source code of this demo is based on the <a href="https://huggingface.co/spaces/gligen/demo/tree/main">GLIGEN demo</a>.</p>"""
348
+ gr.HTML(description)
349
+
350
+ demo.queue(concurrency_count=1, api_open=False)
351
+ demo.launch(share=False, show_api=False, show_error=True)
352
+
353
+ if __name__ == '__main__':
354
+ main()
bounded_attention.py ADDED
@@ -0,0 +1,522 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import nltk
2
+ import einops
3
+ import torch
4
+ import torch.nn.functional as F
5
+ import torchvision.utils
6
+ from torch_kmeans import KMeans
7
+
8
+ import os
9
+
10
+ import injection_utils
11
+ import utils
12
+
13
+
14
+ class BoundedAttention(injection_utils.AttentionBase):
15
+ EPSILON = 1e-5
16
+ FILTER_TAGS = {
17
+ 'CC', 'CD', 'DT', 'EX', 'IN', 'LS', 'MD', 'PDT', 'POS', 'PRP', 'PRP$', 'RP', 'TO', 'UH', 'WDT', 'WP', 'WRB'}
18
+ TAG_RULES = {'left': 'IN', 'right': 'IN', 'top': 'IN', 'bottom': 'IN'}
19
+
20
+ def __init__(
21
+ self,
22
+ boxes,
23
+ prompts,
24
+ subject_token_indices,
25
+ cross_loss_layers,
26
+ self_loss_layers,
27
+ cross_mask_layers=None,
28
+ self_mask_layers=None,
29
+ eos_token_index=None,
30
+ filter_token_indices=None,
31
+ leading_token_indices=None,
32
+ mask_cross_during_guidance=True,
33
+ mask_eos=True,
34
+ cross_loss_coef=1,
35
+ self_loss_coef=1,
36
+ max_guidance_iter=15,
37
+ max_guidance_iter_per_step=5,
38
+ start_step_size=30,
39
+ end_step_size=10,
40
+ loss_stopping_value=0.2,
41
+ cross_mask_threshold=0.2,
42
+ self_mask_threshold=0.2,
43
+ delta_refine_mask_steps=5,
44
+ pca_rank=None,
45
+ num_clusters=None,
46
+ num_clusters_per_box=3,
47
+ map_dir=None,
48
+ debug=False,
49
+ delta_debug_attention_steps=20,
50
+ delta_debug_mask_steps=5,
51
+ debug_layers=None,
52
+ saved_resolution=64,
53
+ ):
54
+ super().__init__()
55
+ self.boxes = boxes
56
+ self.prompts = prompts
57
+ self.subject_token_indices = subject_token_indices
58
+ self.cross_loss_layers = set(cross_loss_layers)
59
+ self.self_loss_layers = set(self_loss_layers)
60
+ self.cross_mask_layers = self.cross_loss_layers if cross_mask_layers is None else set(cross_mask_layers)
61
+ self.self_mask_layers = self.self_loss_layers if self_mask_layers is None else set(self_mask_layers)
62
+
63
+ self.eos_token_index = eos_token_index
64
+ self.filter_token_indices = filter_token_indices
65
+ self.leading_token_indices = leading_token_indices
66
+ self.mask_cross_during_guidance = mask_cross_during_guidance
67
+ self.mask_eos = mask_eos
68
+ self.cross_loss_coef = cross_loss_coef
69
+ self.self_loss_coef = self_loss_coef
70
+ self.max_guidance_iter = max_guidance_iter
71
+ self.max_guidance_iter_per_step = max_guidance_iter_per_step
72
+ self.start_step_size = start_step_size
73
+ self.step_size_coef = (end_step_size - start_step_size) / max_guidance_iter
74
+ self.loss_stopping_value = loss_stopping_value
75
+ self.cross_mask_threshold = cross_mask_threshold
76
+ self.self_mask_threshold = self_mask_threshold
77
+
78
+ self.delta_refine_mask_steps = delta_refine_mask_steps
79
+ self.pca_rank = pca_rank
80
+ num_clusters = len(boxes) * num_clusters_per_box if num_clusters is None else num_clusters
81
+ self.clustering = KMeans(n_clusters=num_clusters, num_init=100)
82
+ self.centers = None
83
+
84
+ self.map_dir = map_dir
85
+ self.debug = debug
86
+ self.delta_debug_attention_steps = delta_debug_attention_steps
87
+ self.delta_debug_mask_steps = delta_debug_mask_steps
88
+ self.debug_layers = self.cross_loss_layers | self.self_loss_layers if debug_layers is None else debug_layers
89
+ self.saved_resolution = saved_resolution
90
+
91
+ self.optimized = False
92
+ self.cross_foreground_values = []
93
+ self.self_foreground_values = []
94
+ self.cross_background_values = []
95
+ self.self_background_values = []
96
+ self.cross_maps = []
97
+ self.self_maps = []
98
+ self.self_masks = None
99
+
100
+ def clear_values(self, include_maps=False):
101
+ lists = (
102
+ self.cross_foreground_values,
103
+ self.self_foreground_values,
104
+ self.cross_background_values,
105
+ self.self_background_values,
106
+ )
107
+
108
+ if include_maps:
109
+ lists = (
110
+ *all_values,
111
+ self.cross_maps,
112
+ self.self_maps,
113
+ )
114
+
115
+ for values in lists:
116
+ values.clear()
117
+
118
+ def before_step(self):
119
+ self.clear_values()
120
+ if self.cur_step == 0:
121
+ self._determine_tokens()
122
+
123
+ def reset(self):
124
+ self.clear_values(include_maps=True)
125
+ super().reset()
126
+
127
+ def forward(self, q, k, v, sim, attn, is_cross, place_in_unet, num_heads, **kwargs):
128
+ self._display_attention_maps(attn, is_cross, num_heads)
129
+
130
+ _, n, d = sim.shape
131
+ sim_u, sim_c = sim.reshape(-1, num_heads, n, d).chunk(2) # b h n d
132
+ if is_cross:
133
+ sim_c = self._hide_other_subjects_from_tokens(sim_c)
134
+ else:
135
+ sim_u = self._hide_other_subjects_from_subjects(sim_u)
136
+ sim_c = self._hide_other_subjects_from_subjects(sim_c)
137
+
138
+ sim = torch.cat((sim_u, sim_c)).reshape(-1, n, d)
139
+ attn = sim.softmax(-1)
140
+ self._save(attn, is_cross, num_heads)
141
+ self._display_attention_maps(attn, is_cross, num_heads, prefix='masked')
142
+ self._debug_hook(q, k, v, sim, attn, is_cross, place_in_unet, num_heads, **kwargs)
143
+
144
+ out = torch.bmm(attn, v)
145
+ out = einops.rearrange(out, '(b h) n d -> b n (h d)', h=num_heads)
146
+ return out
147
+
148
+ def update_loss(self, forward_pass, latents, i):
149
+ if i >= self.max_guidance_iter:
150
+ return latents
151
+
152
+ step_size = self.start_step_size + self.step_size_coef * i
153
+ updated_latents = latents
154
+
155
+ self.optimized = True
156
+ normalized_loss = torch.tensor(10000)
157
+ with torch.enable_grad():
158
+ latents = updated_latents = updated_latents.clone().detach().requires_grad_(True)
159
+ for guidance_iter in range(self.max_guidance_iter_per_step):
160
+ if normalized_loss < self.loss_stopping_value:
161
+ break
162
+
163
+ latent_model_input = torch.cat([latents] * 2)
164
+ cur_step = self.cur_step
165
+ forward_pass(latent_model_input)
166
+ self.cur_step = cur_step
167
+
168
+ loss, normalized_loss = self._compute_loss()
169
+ grad_cond = torch.autograd.grad(loss, [updated_latents])[0]
170
+ latents = updated_latents = updated_latents - step_size * grad_cond
171
+ if self.debug:
172
+ print(f'Loss at step={i}, iter={guidance_iter}: {normalized_loss}')
173
+ grad_norms = grad_cond.flatten(start_dim=2).norm(dim=1)
174
+ grad_norms = grad_norms / grad_norms.max(dim=1, keepdim=True)[0]
175
+ self._save_maps(grad_norms, 'grad_norms')
176
+
177
+ self.optimized = False
178
+ return latents
179
+
180
+ def _tokenize(self):
181
+ ids = self.model.tokenizer.encode(self.prompts[0])
182
+ tokens = self.model.tokenizer.convert_ids_to_tokens(ids, skip_special_tokens=True)
183
+ return [token[:-4] for token in tokens] # remove ending </w>
184
+
185
+ def _tag_tokens(self):
186
+ tagged_tokens = nltk.pos_tag(self._tokenize())
187
+ return [type(self).TAG_RULES.get(token, tag) for token, tag in tagged_tokens]
188
+
189
+ def _determine_eos_token(self):
190
+ tokens = self._tokenize()
191
+ eos_token_index = len(tokens) + 1
192
+ if self.eos_token_index is None:
193
+ self.eos_token_index = eos_token_index
194
+ elif eos_token_index != self.eos_token_index:
195
+ raise ValueError(f'Wrong EOS token index. Tokens are: {tokens}.')
196
+
197
+ def _determine_filter_tokens(self):
198
+ if self.filter_token_indices is not None:
199
+ return
200
+
201
+ tags = self._tag_tokens()
202
+ self.filter_token_indices = [i + 1 for i, tag in enumerate(tags) if tag in type(self).FILTER_TAGS]
203
+
204
+ def _determine_leading_tokens(self):
205
+ if self.leading_token_indices is not None:
206
+ return
207
+
208
+ tags = self._tag_tokens()
209
+ leading_token_indices = []
210
+ for indices in self.subject_token_indices:
211
+ subject_noun_indices = [i for i in indices if tags[i - 1].startswith('NN')]
212
+ leading_token_candidates = subject_noun_indices if len(subject_noun_indices) > 0 else indices
213
+ leading_token_indices.append(leading_token_candidates[-1])
214
+
215
+ self.leading_token_indices = leading_token_indices
216
+
217
+ def _determine_tokens(self):
218
+ self._determine_eos_token()
219
+ self._determine_filter_tokens()
220
+ self._determine_leading_tokens()
221
+
222
+ def _split_references(self, tensor, num_heads):
223
+ tensor = tensor.reshape(-1, num_heads, *tensor.shape[1:])
224
+ unconditional, conditional = tensor.chunk(2)
225
+
226
+ num_subjects = len(self.boxes)
227
+ batch_unconditional = unconditional[:-num_subjects]
228
+ references_unconditional = unconditional[-num_subjects:]
229
+ batch_conditional = conditional[:-num_subjects]
230
+ references_conditional = conditional[-num_subjects:]
231
+
232
+ batch = torch.cat((batch_unconditional, batch_conditional))
233
+ references = torch.cat((references_unconditional, references_conditional))
234
+ batch = batch.reshape(-1, *batch_unconditional.shape[2:])
235
+ references = references.reshape(-1, *references_unconditional.shape[2:])
236
+ return batch, references
237
+
238
+ def _hide_other_subjects_from_tokens(self, sim): # b h i j
239
+ dtype = sim.dtype
240
+ device = sim.device
241
+ batch_size = sim.size(0)
242
+ resolution = int(sim.size(2) ** 0.5)
243
+ subject_masks, background_masks = self._obtain_masks(resolution, batch_size=batch_size, device=device) # b s n
244
+ include_background = self.optimized or (not self.mask_cross_during_guidance and self.cur_step < self.max_guidance_iter_per_step)
245
+ subject_masks = torch.logical_or(subject_masks, background_masks.unsqueeze(1)) if include_background else subject_masks
246
+ min_value = torch.finfo(sim.dtype).min
247
+ sim_masks = torch.zeros_like(sim[:, 0, :, :]) # b i j
248
+ for token_indices in (*self.subject_token_indices, self.filter_token_indices):
249
+ sim_masks[:, :, token_indices] = min_value
250
+
251
+ for batch_index in range(batch_size):
252
+ for subject_mask, token_indices in zip(subject_masks[batch_index], self.subject_token_indices):
253
+ for token_index in token_indices:
254
+ sim_masks[batch_index, subject_mask, token_index] = 0
255
+
256
+ if self.mask_eos and not include_background:
257
+ for batch_index, background_mask in zip(range(batch_size), background_masks):
258
+ sim_masks[batch_index, background_mask, self.eos_token_index] = min_value
259
+
260
+ return sim + sim_masks.unsqueeze(1)
261
+
262
+ def _hide_other_subjects_from_subjects(self, sim): # b h i j
263
+ dtype = sim.dtype
264
+ device = sim.device
265
+ batch_size = sim.size(0)
266
+ resolution = int(sim.size(2) ** 0.5)
267
+ subject_masks, background_masks = self._obtain_masks(resolution, batch_size=batch_size, device=device) # b s n
268
+ min_value = torch.finfo(dtype).min
269
+ sim_masks = torch.zeros_like(sim[:, 0, :, :]) # b i j
270
+ for batch_index, background_mask in zip(range(batch_size), background_masks):
271
+ sim_masks[batch_index, ~background_mask, ~background_mask] = min_value
272
+
273
+ for batch_index in range(batch_size):
274
+ for subject_mask in subject_masks[batch_index]:
275
+ subject_sim_mask = sim_masks[batch_index, subject_mask]
276
+ condition = torch.logical_or(subject_sim_mask == 0, subject_mask.unsqueeze(0))
277
+ sim_masks[batch_index, subject_mask] = torch.where(condition, 0, min_value).to(dtype=dtype)
278
+
279
+ return sim + sim_masks.unsqueeze(1)
280
+
281
+ def _save(self, attn, is_cross, num_heads):
282
+ _, attn = attn.chunk(2)
283
+ attn = attn.reshape(-1, num_heads, *attn.shape[-2:]) # b h n k
284
+
285
+ self._save_mask_maps(attn, is_cross)
286
+ self._save_loss_values(attn, is_cross)
287
+
288
+ def _save_mask_maps(self, attn, is_cross):
289
+ if (
290
+ (self.optimized) or
291
+ (is_cross and self.cur_att_layer not in self.cross_mask_layers) or
292
+ ((not is_cross) and (self.cur_att_layer not in self.self_mask_layers))
293
+ ):
294
+ return
295
+
296
+ if is_cross:
297
+ attn = attn[..., self.leading_token_indices]
298
+ mask_maps = self.cross_maps
299
+ else:
300
+ mask_maps = self.self_maps
301
+
302
+ mask_maps.append(attn.mean(dim=1)) # mean over heads
303
+ if self.cur_step > 0:
304
+ mask_maps = mask_maps[1:] # throw away old maps
305
+
306
+ def _save_loss_values(self, attn, is_cross):
307
+ if (
308
+ (not self.optimized) or
309
+ (is_cross and (self.cur_att_layer not in self.cross_loss_layers)) or
310
+ ((not is_cross) and (self.cur_att_layer not in self.self_loss_layers))
311
+ ):
312
+ return
313
+
314
+ resolution = int(attn.size(2) ** 0.5)
315
+ boxes = self._convert_boxes_to_masks(resolution, device=attn.device) # s n
316
+ background_mask = boxes.sum(dim=0) == 0
317
+
318
+ if is_cross:
319
+ saved_foreground_values = self.cross_foreground_values
320
+ saved_background_values = self.cross_background_values
321
+ contexts = [indices + [self.eos_token_index] for indices in self.subject_token_indices] # TODO: fix EOS loss term
322
+ else:
323
+ saved_foreground_values = self.self_foreground_values
324
+ saved_background_values = self.self_background_values
325
+ contexts = boxes
326
+
327
+ foreground_values = []
328
+ background_values = []
329
+ for i, (box, context) in enumerate(zip(boxes, contexts)):
330
+ context_attn = attn[:, :, :, context]
331
+
332
+ # sum over heads, pixels and contexts
333
+ foreground_values.append(context_attn[:, :, box].sum(dim=(1, 2, 3)))
334
+ background_values.append(context_attn[:, :, background_mask].sum(dim=(1, 2, 3)))
335
+
336
+ saved_foreground_values.append(torch.stack(foreground_values, dim=1))
337
+ saved_background_values.append(torch.stack(background_values, dim=1))
338
+
339
+ def _compute_loss(self):
340
+ cross_losses = self._compute_loss_term(self.cross_foreground_values, self.cross_background_values)
341
+ self_losses = self._compute_loss_term(self.self_foreground_values, self.self_background_values)
342
+ b, s = cross_losses.shape
343
+
344
+ # sum over samples and subjects
345
+ total_cross_loss = cross_losses.sum()
346
+ total_self_loss = self_losses.sum()
347
+
348
+ loss = self.cross_loss_coef * total_cross_loss + self.self_loss_coef * total_self_loss
349
+ normalized_loss = loss / b / s
350
+ return loss, normalized_loss
351
+
352
+ def _compute_loss_term(self, foreground_values, background_values):
353
+ # mean over layers
354
+ mean_foreground = torch.stack(foreground_values).mean(dim=0)
355
+ mean_background = torch.stack(background_values).mean(dim=0)
356
+ iou = mean_foreground / (mean_foreground + len(self.boxes) * mean_background)
357
+ return (1 - iou) ** 2
358
+
359
+ def _obtain_masks(self, resolution, return_boxes=False, return_existing=False, batch_size=None, device=None):
360
+ return_boxes = return_boxes or (return_existing and self.self_masks is None)
361
+ if return_boxes or self.cur_step < self.max_guidance_iter:
362
+ masks = self._convert_boxes_to_masks(resolution, device=device).unsqueeze(0)
363
+ if batch_size is not None:
364
+ masks = masks.expand(batch_size, *masks.shape[1:])
365
+ else:
366
+ masks = self._obtain_self_masks(resolution, return_existing=return_existing)
367
+ if device is not None:
368
+ masks = masks.to(device=device)
369
+
370
+ background_mask = masks.sum(dim=1) == 0
371
+ return masks, background_mask
372
+
373
+ def _convert_boxes_to_masks(self, resolution, device=None): # s n
374
+ boxes = torch.zeros(len(self.boxes), resolution, resolution, dtype=bool, device=device)
375
+ for i, box in enumerate(self.boxes):
376
+ x0, x1 = box[0] * resolution, box[2] * resolution
377
+ y0, y1 = box[1] * resolution, box[3] * resolution
378
+
379
+ boxes[i, round(y0) : round(y1), round(x0) : round(x1)] = True
380
+
381
+ return boxes.flatten(start_dim=1)
382
+
383
+ def _obtain_self_masks(self, resolution, return_existing=False):
384
+ if (
385
+ (self.self_masks is None) or
386
+ (
387
+ (self.cur_step % self.delta_refine_mask_steps == 0) and
388
+ (self.cur_att_layer == 0) and
389
+ (not return_existing)
390
+ )
391
+ ):
392
+ self.self_masks = self._fix_zero_masks(self._build_self_masks())
393
+
394
+ b, s, n = self.self_masks.shape
395
+ mask_resolution = int(n ** 0.5)
396
+ self_masks = self.self_masks.reshape(b, s, mask_resolution, mask_resolution).float()
397
+ self_masks = F.interpolate(self_masks, resolution, mode='nearest-exact')
398
+ return self_masks.flatten(start_dim=2).bool()
399
+
400
+ def _cluster_self_maps(self): # b s n
401
+ self_maps = self._aggregate_maps(self.self_maps) # b n m
402
+ if self.pca_rank is not None:
403
+ dtype = self_maps.dtype
404
+ _, _, eigen_vectors = torch.pca_lowrank(self_maps.float(), self.pca_rank)
405
+ self_maps = torch.matmul(self_maps, eigen_vectors.to(dtype=dtype))
406
+
407
+ clustering_results = self.clustering(self_maps, centers=self.centers)
408
+ self.clustering.num_init = 1 # clustering is deterministic after the first time
409
+ self.centers = clustering_results.centers
410
+ clusters = clustering_results.labels
411
+ num_clusters = self.clustering.n_clusters
412
+ self._save_maps(clusters / num_clusters, f'clusters')
413
+ return num_clusters, clusters
414
+
415
+ def _build_self_masks(self):
416
+ c, clusters = self._cluster_self_maps() # b n
417
+ cluster_masks = torch.stack([(clusters == cluster_index) for cluster_index in range(c)], dim=2) # b n c
418
+ cluster_area = cluster_masks.sum(dim=1, keepdim=True) # b 1 c
419
+
420
+ n = clusters.size(1)
421
+ resolution = int(n ** 0.5)
422
+ cross_masks = self._obtain_cross_masks(resolution) # b s n
423
+ cross_mask_area = cross_masks.sum(dim=2, keepdim=True) # b s 1
424
+
425
+ intersection = torch.bmm(cross_masks.float(), cluster_masks.float()) # b s c
426
+ min_area = torch.minimum(cross_mask_area, cluster_area) # b s c
427
+ score_per_cluster, subject_per_cluster = torch.max(intersection / min_area, dim=1) # b c
428
+ subjects = torch.gather(subject_per_cluster, 1, clusters) # b n
429
+ scores = torch.gather(score_per_cluster, 1, clusters) # b n
430
+
431
+ s = cross_masks.size(1)
432
+ self_masks = torch.stack([(subjects == subject_index) for subject_index in range(s)], dim=1) # b s n
433
+ scores = scores.unsqueeze(1).expand(-1 ,s, n) # b s n
434
+ self_masks[scores < self.self_mask_threshold] = False
435
+ self._save_maps(self_masks, 'self_masks')
436
+ return self_masks
437
+
438
+ def _obtain_cross_masks(self, resolution, scale=10):
439
+ maps = self._aggregate_maps(self.cross_maps, resolution=resolution) # b n k
440
+ maps = F.sigmoid(scale * (maps - self.cross_mask_threshold))
441
+ maps = self._normalize_maps(maps, reduce_min=True)
442
+ maps = maps.transpose(1, 2) # b k n
443
+ existing_masks, _ = self._obtain_masks(
444
+ resolution, return_existing=True, batch_size=maps.size(0), device=maps.device)
445
+ maps = maps * existing_masks.to(dtype=maps.dtype)
446
+ self._save_maps(maps, 'cross_masks')
447
+ return maps
448
+
449
+ def _fix_zero_masks(self, masks):
450
+ b, s, n = masks.shape
451
+ resolution = int(n ** 0.5)
452
+ boxes = self._convert_boxes_to_masks(resolution, device=masks.device) # s n
453
+
454
+ for i in range(b):
455
+ for j in range(s):
456
+ if masks[i, j].sum() == 0:
457
+ print('******Found a zero mask!******')
458
+ for k in range(s):
459
+ masks[i, k] = boxes[j] if (k == j) else masks[i, k].logical_and(~boxes[j])
460
+
461
+ return masks
462
+
463
+ def _aggregate_maps(self, maps, resolution=None): # b n k
464
+ maps = torch.stack(maps).mean(0) # mean over layers
465
+ if resolution is not None:
466
+ b, n, k = maps.shape
467
+ original_resolution = int(n ** 0.5)
468
+ maps = maps.transpose(1, 2).reshape(b, k, original_resolution, original_resolution)
469
+ maps = F.interpolate(maps, resolution, mode='bilinear', antialias=True)
470
+ maps = maps.reshape(b, k, -1).transpose(1, 2)
471
+
472
+ maps = self._normalize_maps(maps)
473
+ return maps
474
+
475
+ @classmethod
476
+ def _normalize_maps(cls, maps, reduce_min=False): # b n k
477
+ max_values = maps.max(dim=1, keepdim=True)[0]
478
+ min_values = maps.min(dim=1, keepdim=True)[0] if reduce_min else 0
479
+ numerator = maps - min_values
480
+ denominator = max_values - min_values + cls.EPSILON
481
+ return numerator / denominator
482
+
483
+ def _save_maps(self, maps, prefix):
484
+ if self.map_dir is None or self.cur_step % self.delta_debug_mask_steps != 0:
485
+ return
486
+
487
+ resolution = int(maps.size(-1) ** 0.5)
488
+ maps = maps.reshape(-1, 1, resolution, resolution).float()
489
+ maps = F.interpolate(maps, self.saved_resolution, mode='bilinear', antialias=True)
490
+ path = os.path.join(self.map_dir, f'map_{prefix}_{self.cur_step}_{self.cur_att_layer}.png')
491
+ torchvision.utils.save_image(maps, path)
492
+
493
+ def _display_attention_maps(self, attention_maps, is_cross, num_heads, prefix=None):
494
+ if (not self.debug) or (self.cur_step == 0) or (self.cur_step % self.delta_debug_attention_steps > 0) or (self.cur_att_layer not in self.debug_layers):
495
+ return
496
+
497
+ dir_name = self.map_dir
498
+ if prefix is not None:
499
+ splits = list(os.path.split(dir_name))
500
+ splits[-1] = '_'.join((prefix, splits[-1]))
501
+ dir_name = os.path.join(*splits)
502
+
503
+ resolution = int(attention_maps.size(-2) ** 0.5)
504
+ if is_cross:
505
+ attention_maps = einops.rearrange(attention_maps, 'b (r1 r2) k -> b k r1 r2', r1=resolution)
506
+ attention_maps = F.interpolate(attention_maps, self.saved_resolution, mode='bilinear', antialias=True)
507
+ attention_maps = einops.rearrange(attention_maps, 'b k r1 r2 -> b (r1 r2) k')
508
+
509
+ utils.display_attention_maps(
510
+ attention_maps,
511
+ is_cross,
512
+ num_heads,
513
+ self.model.tokenizer,
514
+ self.prompts,
515
+ dir_name,
516
+ self.cur_step,
517
+ self.cur_att_layer,
518
+ resolution,
519
+ )
520
+
521
+ def _debug_hook(self, q, k, v, sim, attn, is_cross, place_in_unet, num_heads, **kwargs):
522
+ pass
injection_utils.py ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import numpy as np
3
+ import torch
4
+ import torch.nn as nn
5
+ import torch.nn.functional as F
6
+
7
+ from typing import Optional, Union, Tuple, List, Callable, Dict
8
+
9
+ from torchvision.utils import save_image
10
+ from einops import rearrange, repeat
11
+
12
+
13
+ class AttentionBase:
14
+ def __init__(self):
15
+ self.cur_step = 0
16
+ self.num_att_layers = -1
17
+ self.cur_att_layer = 0
18
+
19
+ def before_step(self):
20
+ pass
21
+
22
+ def after_step(self):
23
+ pass
24
+
25
+ def __call__(self, q, k, v, sim, attn, is_cross, place_in_unet, num_heads, **kwargs):
26
+ if self.cur_att_layer == 0:
27
+ self.before_step()
28
+
29
+ out = self.forward(q, k, v, sim, attn, is_cross, place_in_unet, num_heads, **kwargs)
30
+ self.cur_att_layer += 1
31
+ if self.cur_att_layer == self.num_att_layers:
32
+ self.cur_att_layer = 0
33
+ self.cur_step += 1
34
+ # after step
35
+ self.after_step()
36
+ return out
37
+
38
+ def forward(self, q, k, v, sim, attn, is_cross, place_in_unet, num_heads, **kwargs):
39
+ out = torch.einsum('b i j, b j d -> b i d', attn, v)
40
+ out = rearrange(out, '(b h) n d -> b n (h d)', h=num_heads)
41
+ return out
42
+
43
+ def reset(self):
44
+ self.cur_step = 0
45
+ self.cur_att_layer = 0
46
+
47
+
48
+ class AttentionStore(AttentionBase):
49
+ def __init__(self, res=[32], min_step=0, max_step=1000):
50
+ super().__init__()
51
+ self.res = res
52
+ self.min_step = min_step
53
+ self.max_step = max_step
54
+ self.valid_steps = 0
55
+
56
+ self.self_attns = [] # store the all attns
57
+ self.cross_attns = []
58
+
59
+ self.self_attns_step = [] # store the attns in each step
60
+ self.cross_attns_step = []
61
+
62
+ def after_step(self):
63
+ if self.cur_step > self.min_step and self.cur_step < self.max_step:
64
+ self.valid_steps += 1
65
+ if len(self.self_attns) == 0:
66
+ self.self_attns = self.self_attns_step
67
+ self.cross_attns = self.cross_attns_step
68
+ else:
69
+ for i in range(len(self.self_attns)):
70
+ self.self_attns[i] += self.self_attns_step[i]
71
+ self.cross_attns[i] += self.cross_attns_step[i]
72
+ self.self_attns_step.clear()
73
+ self.cross_attns_step.clear()
74
+
75
+ def forward(self, q, k, v, sim, attn, is_cross, place_in_unet, num_heads, **kwargs):
76
+ if attn.shape[1] <= 64 ** 2: # avoid OOM
77
+ if is_cross:
78
+ self.cross_attns_step.append(attn)
79
+ else:
80
+ self.self_attns_step.append(attn)
81
+ return super().forward(q, k, v, sim, attn, is_cross, place_in_unet, num_heads, **kwargs)
82
+
83
+
84
+ def regiter_attention_editor_diffusers(model, editor: AttentionBase):
85
+ """
86
+ Register a attention editor to Diffuser Pipeline, refer from [Prompt-to-Prompt]
87
+ """
88
+ def ca_forward(self, place_in_unet):
89
+ def forward(x, encoder_hidden_states=None, attention_mask=None, context=None, mask=None):
90
+ """
91
+ The attention is similar to the original implementation of LDM CrossAttention class
92
+ except adding some modifications on the attention
93
+ """
94
+ if encoder_hidden_states is not None:
95
+ context = encoder_hidden_states
96
+ if attention_mask is not None:
97
+ mask = attention_mask
98
+
99
+ to_out = self.to_out
100
+ if isinstance(to_out, nn.modules.container.ModuleList):
101
+ to_out = self.to_out[0]
102
+ else:
103
+ to_out = self.to_out
104
+
105
+ h = self.heads
106
+ q = self.to_q(x)
107
+ is_cross = context is not None
108
+ context = context if is_cross else x
109
+ k = self.to_k(context)
110
+ v = self.to_v(context)
111
+ q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> (b h) n d', h=h), (q, k, v))
112
+
113
+ sim = torch.einsum('b i d, b j d -> b i j', q, k) * self.scale
114
+
115
+ if mask is not None:
116
+ mask = rearrange(mask, 'b ... -> b (...)')
117
+ max_neg_value = -torch.finfo(sim.dtype).max
118
+ mask = repeat(mask, 'b j -> (b h) () j', h=h)
119
+ mask = mask[:, None, :].repeat(h, 1, 1)
120
+ sim.masked_fill_(~mask, max_neg_value)
121
+
122
+ attn = sim.softmax(dim=-1)
123
+ # the only difference
124
+ out = editor(
125
+ q, k, v, sim, attn, is_cross, place_in_unet,
126
+ self.heads, scale=self.scale)
127
+
128
+ return to_out(out)
129
+
130
+ return forward
131
+
132
+ #def conv_forward(self):
133
+ # def forward(x):
134
+ # return editor.conv(self, x)
135
+
136
+ # return forward
137
+
138
+ def register_editor(net, count, place_in_unet, prefix=''):
139
+ #print(prefix + f'Found net: {net.__class__.__name__}')
140
+ #if 'Conv' in net.__class__.__name__:
141
+ # print(prefix + f'Found conv with kernel size: {net.kernel_size}')
142
+ # net.old_forward = net.forward
143
+ # net.forward = conv_forward(net)
144
+ for name, subnet in net.named_children():
145
+ if net.__class__.__name__ == 'Attention': # spatial Transformer layer
146
+ net.forward = ca_forward(net, place_in_unet)
147
+ return count + 1
148
+ elif hasattr(net, 'children'):
149
+ count = register_editor(subnet, count, place_in_unet, prefix=prefix + '\t')
150
+ return count
151
+
152
+ cross_att_count = 0
153
+ for net_name, net in model.unet.named_children():
154
+ if "down" in net_name:
155
+ cross_att_count += register_editor(net, 0, "down")
156
+ #print(f'Down number of attention layers {cross_att_count}!')
157
+ elif "mid" in net_name:
158
+ cross_att_count += register_editor(net, 0, "mid")
159
+ #print(f'Mid number of attention layers {cross_att_count}!')
160
+ elif "up" in net_name:
161
+ cross_att_count += register_editor(net, 0, "up")
162
+ #print(f'Up number of attention layers {cross_att_count}!')
163
+ print(f'Number of attention layers {cross_att_count}!')
164
+ editor.num_att_layers = cross_att_count
165
+ editor.model = model
166
+ model.editor = editor
167
+
168
+
169
+ def regiter_attention_editor_ldm(model, editor: AttentionBase):
170
+ """
171
+ Register a attention editor to Stable Diffusion model, refer from [Prompt-to-Prompt]
172
+ """
173
+ def ca_forward(self, place_in_unet):
174
+ def forward(x, encoder_hidden_states=None, attention_mask=None, context=None, mask=None):
175
+ """
176
+ The attention is similar to the original implementation of LDM CrossAttention class
177
+ except adding some modifications on the attention
178
+ """
179
+ if encoder_hidden_states is not None:
180
+ context = encoder_hidden_states
181
+ if attention_mask is not None:
182
+ mask = attention_mask
183
+
184
+ to_out = self.to_out
185
+ if isinstance(to_out, nn.modules.container.ModuleList):
186
+ to_out = self.to_out[0]
187
+ else:
188
+ to_out = self.to_out
189
+
190
+ h = self.heads
191
+ q = self.to_q(x)
192
+ is_cross = context is not None
193
+ context = context if is_cross else x
194
+ k = self.to_k(context)
195
+ v = self.to_v(context)
196
+ q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> (b h) n d', h=h), (q, k, v))
197
+
198
+ sim = torch.einsum('b i d, b j d -> b i j', q, k) * self.scale
199
+
200
+ if mask is not None:
201
+ mask = rearrange(mask, 'b ... -> b (...)')
202
+ max_neg_value = -torch.finfo(sim.dtype).max
203
+ mask = repeat(mask, 'b j -> (b h) () j', h=h)
204
+ mask = mask[:, None, :].repeat(h, 1, 1)
205
+ sim.masked_fill_(~mask, max_neg_value)
206
+
207
+ attn = sim.softmax(dim=-1)
208
+ # the only difference
209
+ out = editor(
210
+ q, k, v, sim, attn, is_cross, place_in_unet,
211
+ self.heads, scale=self.scale)
212
+
213
+ return to_out(out)
214
+
215
+ return forward
216
+
217
+ def register_editor(net, count, place_in_unet):
218
+ for name, subnet in net.named_children():
219
+ if net.__class__.__name__ == 'CrossAttention': # spatial Transformer layer
220
+ net.forward = ca_forward(net, place_in_unet)
221
+ return count + 1
222
+ elif hasattr(net, 'children'):
223
+ count = register_editor(subnet, count, place_in_unet)
224
+ return count
225
+
226
+ cross_att_count = 0
227
+ for net_name, net in model.model.diffusion_model.named_children():
228
+ if "input" in net_name:
229
+ cross_att_count += register_editor(net, 0, "input")
230
+ elif "middle" in net_name:
231
+ cross_att_count += register_editor(net, 0, "middle")
232
+ elif "output" in net_name:
233
+ cross_att_count += register_editor(net, 0, "output")
234
+ editor.num_att_layers = cross_att_count
pipeline_stable_diffusion_xl_opt.py ADDED
@@ -0,0 +1,968 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2023 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 os
17
+ from typing import Any, Callable, Dict, List, Optional, Tuple, Union
18
+
19
+ import torch
20
+ from transformers import CLIPTextModel, CLIPTextModelWithProjection, CLIPTokenizer
21
+
22
+ from diffusers.image_processor import VaeImageProcessor
23
+ from diffusers.loaders import FromSingleFileMixin, LoraLoaderMixin, TextualInversionLoaderMixin
24
+ from diffusers.models import AutoencoderKL, UNet2DConditionModel
25
+ from diffusers.models.attention_processor import (
26
+ AttnProcessor2_0,
27
+ LoRAAttnProcessor2_0,
28
+ LoRAXFormersAttnProcessor,
29
+ XFormersAttnProcessor,
30
+ )
31
+ from diffusers.schedulers import KarrasDiffusionSchedulers
32
+ from diffusers.utils import (
33
+ is_accelerate_available,
34
+ is_accelerate_version,
35
+ is_invisible_watermark_available,
36
+ logging,
37
+ randn_tensor,
38
+ replace_example_docstring,
39
+ )
40
+ from diffusers.pipeline_utils import DiffusionPipeline
41
+ from diffusers.pipelines.stable_diffusion_xl import StableDiffusionXLPipelineOutput
42
+
43
+
44
+ if is_invisible_watermark_available():
45
+ from .watermark import StableDiffusionXLWatermarker
46
+
47
+
48
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
49
+
50
+ EXAMPLE_DOC_STRING = """
51
+ Examples:
52
+ ```py
53
+ >>> import torch
54
+ >>> from diffusers import StableDiffusionXLPipeline
55
+
56
+ >>> pipe = StableDiffusionXLPipeline.from_pretrained(
57
+ ... "stabilityai/stable-diffusion-xl-base-1.0", torch_dtype=torch.float16
58
+ ... )
59
+ >>> pipe = pipe.to("cuda")
60
+
61
+ >>> prompt = "a photo of an astronaut riding a horse on mars"
62
+ >>> image = pipe(prompt).images[0]
63
+ ```
64
+ """
65
+
66
+
67
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.rescale_noise_cfg
68
+ def rescale_noise_cfg(noise_cfg, noise_pred_text, guidance_rescale=0.0):
69
+ """
70
+ Rescale `noise_cfg` according to `guidance_rescale`. Based on findings of [Common Diffusion Noise Schedules and
71
+ Sample Steps are Flawed](https://arxiv.org/pdf/2305.08891.pdf). See Section 3.4
72
+ """
73
+ std_text = noise_pred_text.std(dim=list(range(1, noise_pred_text.ndim)), keepdim=True)
74
+ std_cfg = noise_cfg.std(dim=list(range(1, noise_cfg.ndim)), keepdim=True)
75
+ # rescale the results from guidance (fixes overexposure)
76
+ noise_pred_rescaled = noise_cfg * (std_text / std_cfg)
77
+ # mix with the original results from guidance by factor guidance_rescale to avoid "plain looking" images
78
+ noise_cfg = guidance_rescale * noise_pred_rescaled + (1 - guidance_rescale) * noise_cfg
79
+ return noise_cfg
80
+
81
+
82
+ class StableDiffusionXLPipeline(DiffusionPipeline, FromSingleFileMixin, LoraLoaderMixin):
83
+ r"""
84
+ Pipeline for text-to-image generation using Stable Diffusion XL.
85
+
86
+ This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods the
87
+ library implements for all the pipelines (such as downloading or saving, running on a particular device, etc.)
88
+
89
+ In addition the pipeline inherits the following loading methods:
90
+ - *LoRA*: [`StableDiffusionXLPipeline.load_lora_weights`]
91
+ - *Ckpt*: [`loaders.FromSingleFileMixin.from_single_file`]
92
+
93
+ as well as the following saving methods:
94
+ - *LoRA*: [`loaders.StableDiffusionXLPipeline.save_lora_weights`]
95
+
96
+ Args:
97
+ vae ([`AutoencoderKL`]):
98
+ Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representations.
99
+ text_encoder ([`CLIPTextModel`]):
100
+ Frozen text-encoder. Stable Diffusion XL uses the text portion of
101
+ [CLIP](https://huggingface.co/docs/transformers/model_doc/clip#transformers.CLIPTextModel), specifically
102
+ the [clip-vit-large-patch14](https://huggingface.co/openai/clip-vit-large-patch14) variant.
103
+ text_encoder_2 ([` CLIPTextModelWithProjection`]):
104
+ Second frozen text-encoder. Stable Diffusion XL uses the text and pool portion of
105
+ [CLIP](https://huggingface.co/docs/transformers/model_doc/clip#transformers.CLIPTextModelWithProjection),
106
+ specifically the
107
+ [laion/CLIP-ViT-bigG-14-laion2B-39B-b160k](https://huggingface.co/laion/CLIP-ViT-bigG-14-laion2B-39B-b160k)
108
+ variant.
109
+ tokenizer (`CLIPTokenizer`):
110
+ Tokenizer of class
111
+ [CLIPTokenizer](https://huggingface.co/docs/transformers/v4.21.0/en/model_doc/clip#transformers.CLIPTokenizer).
112
+ tokenizer_2 (`CLIPTokenizer`):
113
+ Second Tokenizer of class
114
+ [CLIPTokenizer](https://huggingface.co/docs/transformers/v4.21.0/en/model_doc/clip#transformers.CLIPTokenizer).
115
+ unet ([`UNet2DConditionModel`]): Conditional U-Net architecture to denoise the encoded image latents.
116
+ scheduler ([`SchedulerMixin`]):
117
+ A scheduler to be used in combination with `unet` to denoise the encoded image latents. Can be one of
118
+ [`DDIMScheduler`], [`LMSDiscreteScheduler`], or [`PNDMScheduler`].
119
+ """
120
+
121
+ def __init__(
122
+ self,
123
+ vae: AutoencoderKL,
124
+ text_encoder: CLIPTextModel,
125
+ text_encoder_2: CLIPTextModelWithProjection,
126
+ tokenizer: CLIPTokenizer,
127
+ tokenizer_2: CLIPTokenizer,
128
+ unet: UNet2DConditionModel,
129
+ scheduler: KarrasDiffusionSchedulers,
130
+ force_zeros_for_empty_prompt: bool = True,
131
+ add_watermarker: Optional[bool] = None,
132
+ ):
133
+ super().__init__()
134
+
135
+ self.register_modules(
136
+ vae=vae,
137
+ text_encoder=text_encoder,
138
+ text_encoder_2=text_encoder_2,
139
+ tokenizer=tokenizer,
140
+ tokenizer_2=tokenizer_2,
141
+ unet=unet,
142
+ scheduler=scheduler,
143
+ )
144
+ self.register_to_config(force_zeros_for_empty_prompt=force_zeros_for_empty_prompt)
145
+ self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
146
+ self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor)
147
+ self.default_sample_size = self.unet.config.sample_size
148
+
149
+ add_watermarker = add_watermarker if add_watermarker is not None else is_invisible_watermark_available()
150
+
151
+ if add_watermarker:
152
+ self.watermark = StableDiffusionXLWatermarker()
153
+ else:
154
+ self.watermark = None
155
+
156
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.enable_vae_slicing
157
+ def enable_vae_slicing(self):
158
+ r"""
159
+ Enable sliced VAE decoding. When this option is enabled, the VAE will split the input tensor in slices to
160
+ compute decoding in several steps. This is useful to save some memory and allow larger batch sizes.
161
+ """
162
+ self.vae.enable_slicing()
163
+
164
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.disable_vae_slicing
165
+ def disable_vae_slicing(self):
166
+ r"""
167
+ Disable sliced VAE decoding. If `enable_vae_slicing` was previously enabled, this method will go back to
168
+ computing decoding in one step.
169
+ """
170
+ self.vae.disable_slicing()
171
+
172
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.enable_vae_tiling
173
+ def enable_vae_tiling(self):
174
+ r"""
175
+ Enable tiled VAE decoding. When this option is enabled, the VAE will split the input tensor into tiles to
176
+ compute decoding and encoding in several steps. This is useful for saving a large amount of memory and to allow
177
+ processing larger images.
178
+ """
179
+ self.vae.enable_tiling()
180
+
181
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.disable_vae_tiling
182
+ def disable_vae_tiling(self):
183
+ r"""
184
+ Disable tiled VAE decoding. If `enable_vae_tiling` was previously enabled, this method will go back to
185
+ computing decoding in one step.
186
+ """
187
+ self.vae.disable_tiling()
188
+
189
+ def enable_model_cpu_offload(self, gpu_id=0):
190
+ r"""
191
+ Offloads all models to CPU using accelerate, reducing memory usage with a low impact on performance. Compared
192
+ to `enable_sequential_cpu_offload`, this method moves one whole model at a time to the GPU when its `forward`
193
+ method is called, and the model remains in GPU until the next model runs. Memory savings are lower than with
194
+ `enable_sequential_cpu_offload`, but performance is much better due to the iterative execution of the `unet`.
195
+ """
196
+ if is_accelerate_available() and is_accelerate_version(">=", "0.17.0.dev0"):
197
+ from accelerate import cpu_offload_with_hook
198
+ else:
199
+ raise ImportError("`enable_model_cpu_offload` requires `accelerate v0.17.0` or higher.")
200
+
201
+ device = torch.device(f"cuda:{gpu_id}")
202
+
203
+ if self.device.type != "cpu":
204
+ self.to("cpu", silence_dtype_warnings=True)
205
+ torch.cuda.empty_cache() # otherwise we don't see the memory savings (but they probably exist)
206
+
207
+ model_sequence = (
208
+ [self.text_encoder, self.text_encoder_2] if self.text_encoder is not None else [self.text_encoder_2]
209
+ )
210
+ model_sequence.extend([self.unet, self.vae])
211
+
212
+ hook = None
213
+ for cpu_offloaded_model in model_sequence:
214
+ _, hook = cpu_offload_with_hook(cpu_offloaded_model, device, prev_module_hook=hook)
215
+
216
+ # We'll offload the last model manually.
217
+ self.final_offload_hook = hook
218
+
219
+ def encode_prompt(
220
+ self,
221
+ prompt: str,
222
+ prompt_2: Optional[str] = None,
223
+ device: Optional[torch.device] = None,
224
+ num_images_per_prompt: int = 1,
225
+ do_classifier_free_guidance: bool = True,
226
+ negative_prompt: Optional[str] = None,
227
+ negative_prompt_2: Optional[str] = None,
228
+ prompt_embeds: Optional[torch.FloatTensor] = None,
229
+ negative_prompt_embeds: Optional[torch.FloatTensor] = None,
230
+ pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
231
+ negative_pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
232
+ lora_scale: Optional[float] = None,
233
+ ):
234
+ r"""
235
+ Encodes the prompt into text encoder hidden states.
236
+
237
+ Args:
238
+ prompt (`str` or `List[str]`, *optional*):
239
+ prompt to be encoded
240
+ prompt_2 (`str` or `List[str]`, *optional*):
241
+ The prompt or prompts to be sent to the `tokenizer_2` and `text_encoder_2`. If not defined, `prompt` is
242
+ used in both text-encoders
243
+ device: (`torch.device`):
244
+ torch device
245
+ num_images_per_prompt (`int`):
246
+ number of images that should be generated per prompt
247
+ do_classifier_free_guidance (`bool`):
248
+ whether to use classifier free guidance or not
249
+ negative_prompt (`str` or `List[str]`, *optional*):
250
+ The prompt or prompts not to guide the image generation. If not defined, one has to pass
251
+ `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
252
+ less than `1`).
253
+ negative_prompt_2 (`str` or `List[str]`, *optional*):
254
+ The prompt or prompts not to guide the image generation to be sent to `tokenizer_2` and
255
+ `text_encoder_2`. If not defined, `negative_prompt` is used in both text-encoders
256
+ prompt_embeds (`torch.FloatTensor`, *optional*):
257
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
258
+ provided, text embeddings will be generated from `prompt` input argument.
259
+ negative_prompt_embeds (`torch.FloatTensor`, *optional*):
260
+ Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
261
+ weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
262
+ argument.
263
+ pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
264
+ Pre-generated pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting.
265
+ If not provided, pooled text embeddings will be generated from `prompt` input argument.
266
+ negative_pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
267
+ Pre-generated negative pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
268
+ weighting. If not provided, pooled negative_prompt_embeds will be generated from `negative_prompt`
269
+ input argument.
270
+ lora_scale (`float`, *optional*):
271
+ A lora scale that will be applied to all LoRA layers of the text encoder if LoRA layers are loaded.
272
+ """
273
+ device = device or self._execution_device
274
+
275
+ # set lora scale so that monkey patched LoRA
276
+ # function of text encoder can correctly access it
277
+ if lora_scale is not None and isinstance(self, LoraLoaderMixin):
278
+ self._lora_scale = lora_scale
279
+
280
+ if prompt is not None and isinstance(prompt, str):
281
+ batch_size = 1
282
+ elif prompt is not None and isinstance(prompt, list):
283
+ batch_size = len(prompt)
284
+ else:
285
+ batch_size = prompt_embeds.shape[0]
286
+
287
+ # Define tokenizers and text encoders
288
+ tokenizers = [self.tokenizer, self.tokenizer_2] if self.tokenizer is not None else [self.tokenizer_2]
289
+ text_encoders = (
290
+ [self.text_encoder, self.text_encoder_2] if self.text_encoder is not None else [self.text_encoder_2]
291
+ )
292
+
293
+ if prompt_embeds is None:
294
+ prompt_2 = prompt_2 or prompt
295
+ # textual inversion: procecss multi-vector tokens if necessary
296
+ prompt_embeds_list = []
297
+ prompts = [prompt, prompt_2]
298
+ for prompt, tokenizer, text_encoder in zip(prompts, tokenizers, text_encoders):
299
+ if isinstance(self, TextualInversionLoaderMixin):
300
+ prompt = self.maybe_convert_prompt(prompt, tokenizer)
301
+
302
+ text_inputs = tokenizer(
303
+ prompt,
304
+ padding="max_length",
305
+ max_length=tokenizer.model_max_length,
306
+ truncation=True,
307
+ return_tensors="pt",
308
+ )
309
+
310
+ text_input_ids = text_inputs.input_ids
311
+ untruncated_ids = tokenizer(prompt, padding="longest", return_tensors="pt").input_ids
312
+
313
+ if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(
314
+ text_input_ids, untruncated_ids
315
+ ):
316
+ removed_text = tokenizer.batch_decode(untruncated_ids[:, tokenizer.model_max_length - 1 : -1])
317
+ logger.warning(
318
+ "The following part of your input was truncated because CLIP can only handle sequences up to"
319
+ f" {tokenizer.model_max_length} tokens: {removed_text}"
320
+ )
321
+
322
+ prompt_embeds = text_encoder(
323
+ text_input_ids.to(device),
324
+ output_hidden_states=True,
325
+ )
326
+
327
+ # We are only ALWAYS interested in the pooled output of the final text encoder
328
+ pooled_prompt_embeds = prompt_embeds[0]
329
+ ### TODO: remove
330
+ null_text_inputs = tokenizer(
331
+ ['a realistic photo of an empty background'] * batch_size,
332
+ padding="max_length",
333
+ max_length=tokenizer.model_max_length,
334
+ truncation=True,
335
+ return_tensors="pt",
336
+ )
337
+ null_input_ids = null_text_inputs.input_ids
338
+ null_prompt_embeds = text_encoder(
339
+ null_input_ids.to(device),
340
+ output_hidden_states=True,
341
+ )
342
+ pooled_prompt_embeds = null_prompt_embeds[0]
343
+ ### TODO: remove
344
+ prompt_embeds = prompt_embeds.hidden_states[-2]
345
+
346
+ prompt_embeds_list.append(prompt_embeds)
347
+
348
+ prompt_embeds = torch.concat(prompt_embeds_list, dim=-1)
349
+
350
+ # get unconditional embeddings for classifier free guidance
351
+ zero_out_negative_prompt = negative_prompt is None and self.config.force_zeros_for_empty_prompt
352
+ if do_classifier_free_guidance and negative_prompt_embeds is None and zero_out_negative_prompt:
353
+ negative_prompt_embeds = torch.zeros_like(prompt_embeds)
354
+ negative_pooled_prompt_embeds = torch.zeros_like(pooled_prompt_embeds)
355
+ elif do_classifier_free_guidance and negative_prompt_embeds is None:
356
+ negative_prompt = negative_prompt or ""
357
+ negative_prompt_2 = negative_prompt_2 or negative_prompt
358
+
359
+ uncond_tokens: List[str]
360
+ if prompt is not None and type(prompt) is not type(negative_prompt):
361
+ raise TypeError(
362
+ f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
363
+ f" {type(prompt)}."
364
+ )
365
+ elif isinstance(negative_prompt, str):
366
+ uncond_tokens = [negative_prompt, negative_prompt_2]
367
+ elif batch_size != len(negative_prompt):
368
+ raise ValueError(
369
+ f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
370
+ f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
371
+ " the batch size of `prompt`."
372
+ )
373
+ else:
374
+ uncond_tokens = [negative_prompt, negative_prompt_2]
375
+
376
+ negative_prompt_embeds_list = []
377
+ for negative_prompt, tokenizer, text_encoder in zip(uncond_tokens, tokenizers, text_encoders):
378
+ if isinstance(self, TextualInversionLoaderMixin):
379
+ negative_prompt = self.maybe_convert_prompt(negative_prompt, tokenizer)
380
+
381
+ max_length = prompt_embeds.shape[1]
382
+ uncond_input = tokenizer(
383
+ negative_prompt,
384
+ padding="max_length",
385
+ max_length=max_length,
386
+ truncation=True,
387
+ return_tensors="pt",
388
+ )
389
+
390
+ negative_prompt_embeds = text_encoder(
391
+ uncond_input.input_ids.to(device),
392
+ output_hidden_states=True,
393
+ )
394
+ # We are only ALWAYS interested in the pooled output of the final text encoder
395
+ negative_pooled_prompt_embeds = negative_prompt_embeds[0]
396
+ negative_prompt_embeds = negative_prompt_embeds.hidden_states[-2]
397
+
398
+ negative_prompt_embeds_list.append(negative_prompt_embeds)
399
+
400
+ negative_prompt_embeds = torch.concat(negative_prompt_embeds_list, dim=-1)
401
+
402
+ prompt_embeds = prompt_embeds.to(dtype=self.text_encoder_2.dtype, device=device)
403
+ bs_embed, seq_len, _ = prompt_embeds.shape
404
+ # duplicate text embeddings for each generation per prompt, using mps friendly method
405
+ prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
406
+ prompt_embeds = prompt_embeds.view(bs_embed * num_images_per_prompt, seq_len, -1)
407
+
408
+ if do_classifier_free_guidance:
409
+ # duplicate unconditional embeddings for each generation per prompt, using mps friendly method
410
+ seq_len = negative_prompt_embeds.shape[1]
411
+ negative_prompt_embeds = negative_prompt_embeds.to(dtype=self.text_encoder_2.dtype, device=device)
412
+ negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_images_per_prompt, 1)
413
+ negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
414
+
415
+ pooled_prompt_embeds = pooled_prompt_embeds.repeat(1, num_images_per_prompt).view(
416
+ bs_embed * num_images_per_prompt, -1
417
+ )
418
+ if do_classifier_free_guidance:
419
+ negative_pooled_prompt_embeds = negative_pooled_prompt_embeds.repeat(1, num_images_per_prompt).view(
420
+ bs_embed * num_images_per_prompt, -1
421
+ )
422
+
423
+ return prompt_embeds, negative_prompt_embeds, pooled_prompt_embeds, negative_pooled_prompt_embeds
424
+
425
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_extra_step_kwargs
426
+ def prepare_extra_step_kwargs(self, generator, eta):
427
+ # prepare extra kwargs for the scheduler step, since not all schedulers have the same signature
428
+ # eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers.
429
+ # eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502
430
+ # and should be between [0, 1]
431
+
432
+ accepts_eta = "eta" in set(inspect.signature(self.scheduler.step).parameters.keys())
433
+ extra_step_kwargs = {}
434
+ if accepts_eta:
435
+ extra_step_kwargs["eta"] = eta
436
+
437
+ # check if the scheduler accepts generator
438
+ accepts_generator = "generator" in set(inspect.signature(self.scheduler.step).parameters.keys())
439
+ if accepts_generator:
440
+ extra_step_kwargs["generator"] = generator
441
+ return extra_step_kwargs
442
+
443
+ def check_inputs(
444
+ self,
445
+ prompt,
446
+ prompt_2,
447
+ height,
448
+ width,
449
+ callback_steps,
450
+ negative_prompt=None,
451
+ negative_prompt_2=None,
452
+ prompt_embeds=None,
453
+ negative_prompt_embeds=None,
454
+ pooled_prompt_embeds=None,
455
+ negative_pooled_prompt_embeds=None,
456
+ ):
457
+ if height % 8 != 0 or width % 8 != 0:
458
+ raise ValueError(f"`height` and `width` have to be divisible by 8 but are {height} and {width}.")
459
+
460
+ if (callback_steps is None) or (
461
+ callback_steps is not None and (not isinstance(callback_steps, int) or callback_steps <= 0)
462
+ ):
463
+ raise ValueError(
464
+ f"`callback_steps` has to be a positive integer but is {callback_steps} of type"
465
+ f" {type(callback_steps)}."
466
+ )
467
+
468
+ if prompt is not None and prompt_embeds is not None:
469
+ raise ValueError(
470
+ f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
471
+ " only forward one of the two."
472
+ )
473
+ elif prompt_2 is not None and prompt_embeds is not None:
474
+ raise ValueError(
475
+ f"Cannot forward both `prompt_2`: {prompt_2} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
476
+ " only forward one of the two."
477
+ )
478
+ elif prompt is None and prompt_embeds is None:
479
+ raise ValueError(
480
+ "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
481
+ )
482
+ elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
483
+ raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
484
+ elif prompt_2 is not None and (not isinstance(prompt_2, str) and not isinstance(prompt_2, list)):
485
+ raise ValueError(f"`prompt_2` has to be of type `str` or `list` but is {type(prompt_2)}")
486
+
487
+ if negative_prompt is not None and negative_prompt_embeds is not None:
488
+ raise ValueError(
489
+ f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:"
490
+ f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
491
+ )
492
+ elif negative_prompt_2 is not None and negative_prompt_embeds is not None:
493
+ raise ValueError(
494
+ f"Cannot forward both `negative_prompt_2`: {negative_prompt_2} and `negative_prompt_embeds`:"
495
+ f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
496
+ )
497
+
498
+ if prompt_embeds is not None and negative_prompt_embeds is not None:
499
+ if prompt_embeds.shape != negative_prompt_embeds.shape:
500
+ raise ValueError(
501
+ "`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but"
502
+ f" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`"
503
+ f" {negative_prompt_embeds.shape}."
504
+ )
505
+
506
+ if prompt_embeds is not None and pooled_prompt_embeds is None:
507
+ raise ValueError(
508
+ "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`."
509
+ )
510
+
511
+ if negative_prompt_embeds is not None and negative_pooled_prompt_embeds is None:
512
+ raise ValueError(
513
+ "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`."
514
+ )
515
+
516
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_latents
517
+ def prepare_latents(self, batch_size, num_channels_latents, height, width, dtype, device, generator, latents=None):
518
+ shape = (batch_size, num_channels_latents, height // self.vae_scale_factor, width // self.vae_scale_factor)
519
+ if isinstance(generator, list) and len(generator) != batch_size:
520
+ raise ValueError(
521
+ f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
522
+ f" size of {batch_size}. Make sure the batch size matches the length of the generators."
523
+ )
524
+
525
+ if latents is None:
526
+ latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
527
+ else:
528
+ latents = latents.to(device)
529
+
530
+ # scale the initial noise by the standard deviation required by the scheduler
531
+ latents = latents * self.scheduler.init_noise_sigma
532
+ return latents
533
+
534
+ def _get_add_time_ids(self, original_size, crops_coords_top_left, target_size, dtype):
535
+ add_time_ids = list(original_size + crops_coords_top_left + target_size)
536
+
537
+ passed_add_embed_dim = (
538
+ self.unet.config.addition_time_embed_dim * len(add_time_ids) + self.text_encoder_2.config.projection_dim
539
+ )
540
+ expected_add_embed_dim = self.unet.add_embedding.linear_1.in_features
541
+
542
+ if expected_add_embed_dim != passed_add_embed_dim:
543
+ raise ValueError(
544
+ f"Model expects an added time embedding vector of length {expected_add_embed_dim}, but a vector of {passed_add_embed_dim} was created. The model has an incorrect config. Please check `unet.config.time_embedding_type` and `text_encoder_2.config.projection_dim`."
545
+ )
546
+
547
+ add_time_ids = torch.tensor([add_time_ids], dtype=dtype)
548
+ return add_time_ids
549
+
550
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_upscale.StableDiffusionUpscalePipeline.upcast_vae
551
+ def upcast_vae(self):
552
+ dtype = self.vae.dtype
553
+ self.vae.to(dtype=torch.float32)
554
+ use_torch_2_0_or_xformers = isinstance(
555
+ self.vae.decoder.mid_block.attentions[0].processor,
556
+ (
557
+ AttnProcessor2_0,
558
+ XFormersAttnProcessor,
559
+ LoRAXFormersAttnProcessor,
560
+ LoRAAttnProcessor2_0,
561
+ ),
562
+ )
563
+ # if xformers or torch_2_0 is used attention block does not need
564
+ # to be in float32 which can save lots of memory
565
+ if use_torch_2_0_or_xformers:
566
+ self.vae.post_quant_conv.to(dtype)
567
+ self.vae.decoder.conv_in.to(dtype)
568
+ self.vae.decoder.mid_block.to(dtype)
569
+
570
+ def update_loss(self, latents, i, t, prompt_embeds, cross_attention_kwargs, add_text_embeds, add_time_ids):
571
+ def forward_pass(latent_model_input):
572
+ latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)
573
+ added_cond_kwargs = {"text_embeds": add_text_embeds, "time_ids": add_time_ids}
574
+ self.unet(
575
+ latent_model_input,
576
+ t,
577
+ encoder_hidden_states=prompt_embeds,
578
+ cross_attention_kwargs=cross_attention_kwargs,
579
+ added_cond_kwargs=added_cond_kwargs,
580
+ return_dict=False,
581
+ )
582
+ self.unet.zero_grad()
583
+
584
+ return self.editor.update_loss(forward_pass, latents, i)
585
+
586
+ @torch.no_grad()
587
+ @replace_example_docstring(EXAMPLE_DOC_STRING)
588
+ def __call__(
589
+ self,
590
+ prompt: Union[str, List[str]] = None,
591
+ prompt_2: Optional[Union[str, List[str]]] = None,
592
+ height: Optional[int] = None,
593
+ width: Optional[int] = None,
594
+ num_inference_steps: int = 50,
595
+ denoising_end: Optional[float] = None,
596
+ guidance_scale: float = 5.0,
597
+ negative_prompt: Optional[Union[str, List[str]]] = None,
598
+ negative_prompt_2: Optional[Union[str, List[str]]] = None,
599
+ num_images_per_prompt: Optional[int] = 1,
600
+ eta: float = 0.0,
601
+ generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
602
+ latents: Optional[torch.FloatTensor] = None,
603
+ prompt_embeds: Optional[torch.FloatTensor] = None,
604
+ negative_prompt_embeds: Optional[torch.FloatTensor] = None,
605
+ pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
606
+ negative_pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
607
+ output_type: Optional[str] = "pil",
608
+ return_dict: bool = True,
609
+ callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None,
610
+ callback_steps: int = 1,
611
+ cross_attention_kwargs: Optional[Dict[str, Any]] = None,
612
+ guidance_rescale: float = 0.0,
613
+ original_size: Optional[Tuple[int, int]] = None,
614
+ crops_coords_top_left: Tuple[int, int] = (0, 0),
615
+ target_size: Optional[Tuple[int, int]] = None,
616
+ ):
617
+ r"""
618
+ Function invoked when calling the pipeline for generation.
619
+
620
+ Args:
621
+ prompt (`str` or `List[str]`, *optional*):
622
+ The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
623
+ instead.
624
+ prompt_2 (`str` or `List[str]`, *optional*):
625
+ The prompt or prompts to be sent to the `tokenizer_2` and `text_encoder_2`. If not defined, `prompt` is
626
+ used in both text-encoders
627
+ height (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
628
+ The height in pixels of the generated image.
629
+ width (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
630
+ The width in pixels of the generated image.
631
+ num_inference_steps (`int`, *optional*, defaults to 50):
632
+ The number of denoising steps. More denoising steps usually lead to a higher quality image at the
633
+ expense of slower inference.
634
+ denoising_end (`float`, *optional*):
635
+ When specified, determines the fraction (between 0.0 and 1.0) of the total denoising process to be
636
+ completed before it is intentionally prematurely terminated. As a result, the returned sample will
637
+ still retain a substantial amount of noise as determined by the discrete timesteps selected by the
638
+ scheduler. The denoising_end parameter should ideally be utilized when this pipeline forms a part of a
639
+ "Mixture of Denoisers" multi-pipeline setup, as elaborated in [**Refining the Image
640
+ Output**](https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/stable_diffusion_xl#refining-the-image-output)
641
+ guidance_scale (`float`, *optional*, defaults to 5.0):
642
+ Guidance scale as defined in [Classifier-Free Diffusion Guidance](https://arxiv.org/abs/2207.12598).
643
+ `guidance_scale` is defined as `w` of equation 2. of [Imagen
644
+ Paper](https://arxiv.org/pdf/2205.11487.pdf). Guidance scale is enabled by setting `guidance_scale >
645
+ 1`. Higher guidance scale encourages to generate images that are closely linked to the text `prompt`,
646
+ usually at the expense of lower image quality.
647
+ negative_prompt (`str` or `List[str]`, *optional*):
648
+ The prompt or prompts not to guide the image generation. If not defined, one has to pass
649
+ `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
650
+ less than `1`).
651
+ negative_prompt_2 (`str` or `List[str]`, *optional*):
652
+ The prompt or prompts not to guide the image generation to be sent to `tokenizer_2` and
653
+ `text_encoder_2`. If not defined, `negative_prompt` is used in both text-encoders
654
+ num_images_per_prompt (`int`, *optional*, defaults to 1):
655
+ The number of images to generate per prompt.
656
+ eta (`float`, *optional*, defaults to 0.0):
657
+ Corresponds to parameter eta (η) in the DDIM paper: https://arxiv.org/abs/2010.02502. Only applies to
658
+ [`schedulers.DDIMScheduler`], will be ignored for others.
659
+ generator (`torch.Generator` or `List[torch.Generator]`, *optional*):
660
+ One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)
661
+ to make generation deterministic.
662
+ latents (`torch.FloatTensor`, *optional*):
663
+ Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image
664
+ generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
665
+ tensor will ge generated by sampling using the supplied random `generator`.
666
+ prompt_embeds (`torch.FloatTensor`, *optional*):
667
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
668
+ provided, text embeddings will be generated from `prompt` input argument.
669
+ negative_prompt_embeds (`torch.FloatTensor`, *optional*):
670
+ Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
671
+ weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
672
+ argument.
673
+ pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
674
+ Pre-generated pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting.
675
+ If not provided, pooled text embeddings will be generated from `prompt` input argument.
676
+ negative_pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
677
+ Pre-generated negative pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
678
+ weighting. If not provided, pooled negative_prompt_embeds will be generated from `negative_prompt`
679
+ input argument.
680
+ output_type (`str`, *optional*, defaults to `"pil"`):
681
+ The output format of the generate image. Choose between
682
+ [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`.
683
+ return_dict (`bool`, *optional*, defaults to `True`):
684
+ Whether or not to return a [`~pipelines.stable_diffusion_xl.StableDiffusionXLPipelineOutput`] instead
685
+ of a plain tuple.
686
+ callback (`Callable`, *optional*):
687
+ A function that will be called every `callback_steps` steps during inference. The function will be
688
+ called with the following arguments: `callback(step: int, timestep: int, latents: torch.FloatTensor)`.
689
+ callback_steps (`int`, *optional*, defaults to 1):
690
+ The frequency at which the `callback` function will be called. If not specified, the callback will be
691
+ called at every step.
692
+ cross_attention_kwargs (`dict`, *optional*):
693
+ A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under
694
+ `self.processor` in
695
+ [diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
696
+ guidance_rescale (`float`, *optional*, defaults to 0.7):
697
+ Guidance rescale factor proposed by [Common Diffusion Noise Schedules and Sample Steps are
698
+ Flawed](https://arxiv.org/pdf/2305.08891.pdf) `guidance_scale` is defined as `φ` in equation 16. of
699
+ [Common Diffusion Noise Schedules and Sample Steps are Flawed](https://arxiv.org/pdf/2305.08891.pdf).
700
+ Guidance rescale factor should fix overexposure when using zero terminal SNR.
701
+ original_size (`Tuple[int]`, *optional*, defaults to (1024, 1024)):
702
+ If `original_size` is not the same as `target_size` the image will appear to be down- or upsampled.
703
+ `original_size` defaults to `(width, height)` if not specified. Part of SDXL's micro-conditioning as
704
+ explained in section 2.2 of
705
+ [https://huggingface.co/papers/2307.01952](https://huggingface.co/papers/2307.01952).
706
+ crops_coords_top_left (`Tuple[int]`, *optional*, defaults to (0, 0)):
707
+ `crops_coords_top_left` can be used to generate an image that appears to be "cropped" from the position
708
+ `crops_coords_top_left` downwards. Favorable, well-centered images are usually achieved by setting
709
+ `crops_coords_top_left` to (0, 0). Part of SDXL's micro-conditioning as explained in section 2.2 of
710
+ [https://huggingface.co/papers/2307.01952](https://huggingface.co/papers/2307.01952).
711
+ target_size (`Tuple[int]`, *optional*, defaults to (1024, 1024)):
712
+ For most cases, `target_size` should be set to the desired height and width of the generated image. If
713
+ not specified it will default to `(width, height)`. Part of SDXL's micro-conditioning as explained in
714
+ section 2.2 of [https://huggingface.co/papers/2307.01952](https://huggingface.co/papers/2307.01952).
715
+
716
+ Examples:
717
+
718
+ Returns:
719
+ [`~pipelines.stable_diffusion_xl.StableDiffusionXLPipelineOutput`] or `tuple`:
720
+ [`~pipelines.stable_diffusion_xl.StableDiffusionXLPipelineOutput`] if `return_dict` is True, otherwise a
721
+ `tuple`. When returning a tuple, the first element is a list with the generated images.
722
+ """
723
+ # 0. Default height and width to unet
724
+ height = height or self.default_sample_size * self.vae_scale_factor
725
+ width = width or self.default_sample_size * self.vae_scale_factor
726
+
727
+ original_size = original_size or (height, width)
728
+ target_size = target_size or (height, width)
729
+
730
+ # 1. Check inputs. Raise error if not correct
731
+ self.check_inputs(
732
+ prompt,
733
+ prompt_2,
734
+ height,
735
+ width,
736
+ callback_steps,
737
+ negative_prompt,
738
+ negative_prompt_2,
739
+ prompt_embeds,
740
+ negative_prompt_embeds,
741
+ pooled_prompt_embeds,
742
+ negative_pooled_prompt_embeds,
743
+ )
744
+
745
+ # 2. Define call parameters
746
+ if prompt is not None and isinstance(prompt, str):
747
+ batch_size = 1
748
+ elif prompt is not None and isinstance(prompt, list):
749
+ batch_size = len(prompt)
750
+ else:
751
+ batch_size = prompt_embeds.shape[0]
752
+
753
+ device = self._execution_device
754
+
755
+ # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2)
756
+ # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1`
757
+ # corresponds to doing no classifier free guidance.
758
+ do_classifier_free_guidance = guidance_scale > 1.0
759
+
760
+ # 3. Encode input prompt
761
+ text_encoder_lora_scale = (
762
+ cross_attention_kwargs.get("scale", None) if cross_attention_kwargs is not None else None
763
+ )
764
+ (
765
+ prompt_embeds,
766
+ negative_prompt_embeds,
767
+ pooled_prompt_embeds,
768
+ negative_pooled_prompt_embeds,
769
+ ) = self.encode_prompt(
770
+ prompt=prompt,
771
+ prompt_2=prompt_2,
772
+ device=device,
773
+ num_images_per_prompt=num_images_per_prompt,
774
+ do_classifier_free_guidance=do_classifier_free_guidance,
775
+ negative_prompt=negative_prompt,
776
+ negative_prompt_2=negative_prompt_2,
777
+ prompt_embeds=prompt_embeds,
778
+ negative_prompt_embeds=negative_prompt_embeds,
779
+ pooled_prompt_embeds=pooled_prompt_embeds,
780
+ negative_pooled_prompt_embeds=negative_pooled_prompt_embeds,
781
+ lora_scale=text_encoder_lora_scale,
782
+ )
783
+
784
+ # 4. Prepare timesteps
785
+ self.scheduler.set_timesteps(num_inference_steps, device=device)
786
+
787
+ timesteps = self.scheduler.timesteps
788
+
789
+ # 5. Prepare latent variables
790
+ num_channels_latents = self.unet.config.in_channels
791
+ latents = self.prepare_latents(
792
+ batch_size * num_images_per_prompt,
793
+ num_channels_latents,
794
+ height,
795
+ width,
796
+ prompt_embeds.dtype,
797
+ device,
798
+ generator,
799
+ latents,
800
+ )
801
+
802
+ # 6. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline
803
+ extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
804
+
805
+ # 7. Prepare added time ids & embeddings
806
+ add_text_embeds = pooled_prompt_embeds
807
+ add_time_ids = self._get_add_time_ids(
808
+ original_size, crops_coords_top_left, target_size, dtype=prompt_embeds.dtype
809
+ )
810
+
811
+ if do_classifier_free_guidance:
812
+ prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds], dim=0)
813
+ add_text_embeds = torch.cat([negative_pooled_prompt_embeds, add_text_embeds], dim=0)
814
+ add_time_ids = torch.cat([add_time_ids, add_time_ids], dim=0)
815
+
816
+ prompt_embeds = prompt_embeds.to(device)
817
+ add_text_embeds = add_text_embeds.to(device)
818
+ add_time_ids = add_time_ids.to(device).repeat(batch_size * num_images_per_prompt, 1)
819
+
820
+ # 8. Denoising loop
821
+ num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
822
+
823
+ # 7.1 Apply denoising_end
824
+ if denoising_end is not None and type(denoising_end) == float and denoising_end > 0 and denoising_end < 1:
825
+ discrete_timestep_cutoff = int(
826
+ round(
827
+ self.scheduler.config.num_train_timesteps
828
+ - (denoising_end * self.scheduler.config.num_train_timesteps)
829
+ )
830
+ )
831
+ num_inference_steps = len(list(filter(lambda ts: ts >= discrete_timestep_cutoff, timesteps)))
832
+ timesteps = timesteps[:num_inference_steps]
833
+
834
+ latents = latents.half()
835
+ prompt_embeds = prompt_embeds.half()
836
+ with self.progress_bar(total=num_inference_steps) as progress_bar:
837
+ for i, t in enumerate(timesteps):
838
+ latents = self.update_loss(latents, i, t, prompt_embeds, cross_attention_kwargs, add_text_embeds, add_time_ids)
839
+
840
+ # expand the latents if we are doing classifier free guidance
841
+ latent_model_input = torch.cat([latents] * 2) if do_classifier_free_guidance else latents
842
+
843
+ latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)
844
+
845
+ # predict the noise residual
846
+ added_cond_kwargs = {"text_embeds": add_text_embeds, "time_ids": add_time_ids}
847
+ noise_pred = self.unet(
848
+ latent_model_input,
849
+ t,
850
+ encoder_hidden_states=prompt_embeds,
851
+ cross_attention_kwargs=cross_attention_kwargs,
852
+ added_cond_kwargs=added_cond_kwargs,
853
+ return_dict=False,
854
+ )[0]
855
+
856
+ # perform guidance
857
+ if do_classifier_free_guidance:
858
+ noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
859
+ noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)
860
+
861
+ if do_classifier_free_guidance and guidance_rescale > 0.0:
862
+ # Based on 3.4. in https://arxiv.org/pdf/2305.08891.pdf
863
+ noise_pred = rescale_noise_cfg(noise_pred, noise_pred_text, guidance_rescale=guidance_rescale)
864
+
865
+ # compute the previous noisy sample x_t -> x_t-1
866
+ latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs, return_dict=False)[0]
867
+
868
+ # call the callback, if provided
869
+ if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
870
+ progress_bar.update()
871
+ if callback is not None and i % callback_steps == 0:
872
+ callback(i, t, latents)
873
+
874
+ # make sure the VAE is in float32 mode, as it overflows in float16
875
+ if self.vae.dtype == torch.float16 and self.vae.config.force_upcast:
876
+ self.upcast_vae()
877
+ latents = latents.to(next(iter(self.vae.post_quant_conv.parameters())).dtype)
878
+
879
+ if not output_type == "latent":
880
+ image = self.vae.decode(latents / self.vae.config.scaling_factor, return_dict=False)[0]
881
+ else:
882
+ image = latents
883
+ return StableDiffusionXLPipelineOutput(images=image)
884
+
885
+ # apply watermark if available
886
+ if self.watermark is not None:
887
+ image = self.watermark.apply_watermark(image)
888
+
889
+ image = self.image_processor.postprocess(image, output_type=output_type)
890
+
891
+ # Offload last model to CPU
892
+ if hasattr(self, "final_offload_hook") and self.final_offload_hook is not None:
893
+ self.final_offload_hook.offload()
894
+
895
+ if not return_dict:
896
+ return (image,)
897
+
898
+ return StableDiffusionXLPipelineOutput(images=image)
899
+
900
+ # Overrride to properly handle the loading and unloading of the additional text encoder.
901
+ def load_lora_weights(self, pretrained_model_name_or_path_or_dict: Union[str, Dict[str, torch.Tensor]], **kwargs):
902
+ # We could have accessed the unet config from `lora_state_dict()` too. We pass
903
+ # it here explicitly to be able to tell that it's coming from an SDXL
904
+ # pipeline.
905
+ state_dict, network_alphas = self.lora_state_dict(
906
+ pretrained_model_name_or_path_or_dict,
907
+ unet_config=self.unet.config,
908
+ **kwargs,
909
+ )
910
+ self.load_lora_into_unet(state_dict, network_alphas=network_alphas, unet=self.unet)
911
+
912
+ text_encoder_state_dict = {k: v for k, v in state_dict.items() if "text_encoder." in k}
913
+ if len(text_encoder_state_dict) > 0:
914
+ self.load_lora_into_text_encoder(
915
+ text_encoder_state_dict,
916
+ network_alphas=network_alphas,
917
+ text_encoder=self.text_encoder,
918
+ prefix="text_encoder",
919
+ lora_scale=self.lora_scale,
920
+ )
921
+
922
+ text_encoder_2_state_dict = {k: v for k, v in state_dict.items() if "text_encoder_2." in k}
923
+ if len(text_encoder_2_state_dict) > 0:
924
+ self.load_lora_into_text_encoder(
925
+ text_encoder_2_state_dict,
926
+ network_alphas=network_alphas,
927
+ text_encoder=self.text_encoder_2,
928
+ prefix="text_encoder_2",
929
+ lora_scale=self.lora_scale,
930
+ )
931
+
932
+ @classmethod
933
+ def save_lora_weights(
934
+ self,
935
+ save_directory: Union[str, os.PathLike],
936
+ unet_lora_layers: Dict[str, Union[torch.nn.Module, torch.Tensor]] = None,
937
+ text_encoder_lora_layers: Dict[str, Union[torch.nn.Module, torch.Tensor]] = None,
938
+ text_encoder_2_lora_layers: Dict[str, Union[torch.nn.Module, torch.Tensor]] = None,
939
+ is_main_process: bool = True,
940
+ weight_name: str = None,
941
+ save_function: Callable = None,
942
+ safe_serialization: bool = True,
943
+ ):
944
+ state_dict = {}
945
+
946
+ def pack_weights(layers, prefix):
947
+ layers_weights = layers.state_dict() if isinstance(layers, torch.nn.Module) else layers
948
+ layers_state_dict = {f"{prefix}.{module_name}": param for module_name, param in layers_weights.items()}
949
+ return layers_state_dict
950
+
951
+ state_dict.update(pack_weights(unet_lora_layers, "unet"))
952
+
953
+ if text_encoder_lora_layers and text_encoder_2_lora_layers:
954
+ state_dict.update(pack_weights(text_encoder_lora_layers, "text_encoder"))
955
+ state_dict.update(pack_weights(text_encoder_2_lora_layers, "text_encoder_2"))
956
+
957
+ self.write_lora_layers(
958
+ state_dict=state_dict,
959
+ save_directory=save_directory,
960
+ is_main_process=is_main_process,
961
+ weight_name=weight_name,
962
+ save_function=save_function,
963
+ safe_serialization=safe_serialization,
964
+ )
965
+
966
+ def _remove_text_encoder_monkey_patch(self):
967
+ self._remove_text_encoder_monkey_patch_classmethod(self.text_encoder)
968
+ self._remove_text_encoder_monkey_patch_classmethod(self.text_encoder_2)
requirements.txt ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ accelerate==0.21.0
2
+ diffusers==0.20.0
3
+ einops==0.6.1
4
+ lightning-utilities==0.9.0
5
+ matplotlib==3.7.3
6
+ nltk==3.8.1
7
+ numpy
8
+ opencv-python==4.8.1.78
9
+ Pillow==9.4.0
10
+ pytorch-lightning==2.0.7
11
+ scikit-image==0.22.0
12
+ scikit-learn==1.3.1
13
+ scipy==1.11.2
14
+ tokenizers==0.13.3
15
+ torch==2.0.1
16
+ torch-kmeans==0.2.0
17
+ torchaudio==2.0.2
18
+ torchmetrics==1.0.3
19
+ torchvision==0.15.2
20
+ tqdm==4.66.1
21
+ transformers==4.32.0
22
+ xformers==0.0.21
utils.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from PIL import Image, ImageDraw, ImageFont
3
+ import cv2
4
+ from sklearn.decomposition import PCA
5
+ from torchvision import transforms
6
+ import matplotlib.pyplot as plt
7
+ import torch
8
+
9
+ import os
10
+
11
+
12
+ def display_attention_maps(
13
+ attention_maps,
14
+ is_cross,
15
+ num_heads,
16
+ tokenizer,
17
+ prompts,
18
+ dir_name,
19
+ step,
20
+ layer,
21
+ resolution,
22
+ is_query=False,
23
+ is_key=False,
24
+ points=None,
25
+ image_path=None,
26
+ ):
27
+ attention_maps = attention_maps.reshape(-1, num_heads, attention_maps.size(-2), attention_maps.size(-1))
28
+ num_samples = len(attention_maps) // 2
29
+ attention_type = 'cross' if is_cross else 'self'
30
+ for i, attention_map in enumerate(attention_maps):
31
+ if is_query:
32
+ attention_type = f'{attention_type}_queries'
33
+ elif is_key:
34
+ attention_type = f'{attention_type}_keys'
35
+
36
+ cond = 'uncond' if i < num_samples else 'cond'
37
+ i = i % num_samples
38
+ cur_dir_name = f'{dir_name}/{resolution}/{attention_type}/{layer}/{cond}/{i}'
39
+ os.makedirs(cur_dir_name, exist_ok=True)
40
+
41
+ if is_cross and not is_query:
42
+ fig = show_cross_attention(attention_map, tokenizer, prompts[i % num_samples])
43
+ else:
44
+ fig = show_self_attention(attention_map)
45
+ if points is not None:
46
+ point_dir_name = f'{cur_dir_name}/points'
47
+ os.makedirs(point_dir_name, exist_ok=True)
48
+ for j, point in enumerate(points):
49
+ specific_point_dir_name = f'{point_dir_name}/{j}'
50
+ os.makedirs(specific_point_dir_name, exist_ok=True)
51
+ point_path = f'{specific_point_dir_name}/{step}.png'
52
+ point_fig = show_individual_self_attention(attention_map, point, image_path=image_path)
53
+ point_fig.save(point_path)
54
+ point_fig.close()
55
+
56
+ fig.save(f'{cur_dir_name}/{step}.png')
57
+ fig.close()
58
+
59
+
60
+ def text_under_image(image: np.ndarray, text: str, text_color: tuple[int, int, int] = (0, 0, 0)):
61
+ h, w, c = image.shape
62
+ offset = int(h * .2)
63
+ font = cv2.FONT_HERSHEY_SIMPLEX
64
+ # font = ImageFont.truetype("/usr/share/fonts/truetype/noto/NotoMono-Regular.ttf", font_size)
65
+ text_size = cv2.getTextSize(text, font, 1, 2)[0]
66
+ lines = text.splitlines()
67
+ img = np.ones((h + offset + (text_size[1] + 2) * len(lines) - 2, w, c), dtype=np.uint8) * 255
68
+ img[:h, :w] = image
69
+
70
+ for i, line in enumerate(lines):
71
+ text_size = cv2.getTextSize(line, font, 1, 2)[0]
72
+ text_x, text_y = ((w - text_size[0]) // 2, h + offset + i * (text_size[1] + 2))
73
+ cv2.putText(img, line, (text_x, text_y), font, 1, text_color, 2)
74
+
75
+ return img
76
+
77
+ def view_images(images, num_rows=1, offset_ratio=0.02):
78
+ if type(images) is list:
79
+ num_empty = len(images) % num_rows
80
+ elif images.ndim == 4:
81
+ num_empty = images.shape[0] % num_rows
82
+ else:
83
+ images = [images]
84
+ num_empty = 0
85
+
86
+ empty_images = np.ones(images[0].shape, dtype=np.uint8) * 255
87
+ images = [image.astype(np.uint8) for image in images] + [empty_images] * num_empty
88
+ num_items = len(images)
89
+
90
+ h, w, c = images[0].shape
91
+ offset = int(h * offset_ratio)
92
+ num_cols = num_items // num_rows
93
+ image_ = np.ones((h * num_rows + offset * (num_rows - 1),
94
+ w * num_cols + offset * (num_cols - 1), 3), dtype=np.uint8) * 255
95
+ for i in range(num_rows):
96
+ for j in range(num_cols):
97
+ image_[i * (h + offset): i * (h + offset) + h:, j * (w + offset): j * (w + offset) + w] = images[
98
+ i * num_cols + j]
99
+
100
+ return Image.fromarray(image_)
101
+
102
+
103
+ def show_cross_attention(attention_maps, tokenizer, prompt, k_norms=None, v_norms=None):
104
+ attention_maps = attention_maps.mean(dim=0)
105
+ res = int(attention_maps.size(-2) ** 0.5)
106
+ attention_maps = attention_maps.reshape(res, res, -1)
107
+ tokens = tokenizer.encode(prompt)
108
+ decoder = tokenizer.decode
109
+ if k_norms is not None:
110
+ k_norms = k_norms.round(decimals=1)
111
+ if v_norms is not None:
112
+ v_norms = v_norms.round(decimals=1)
113
+ images = []
114
+ for i in range(len(tokens) + 5):
115
+ image = attention_maps[:, :, i]
116
+ image = 255 * image / image.max()
117
+ image = image.unsqueeze(-1).expand(*image.shape, 3)
118
+ image = image.detach().cpu().numpy().astype(np.uint8)
119
+ image = np.array(Image.fromarray(image).resize((256, 256)))
120
+ token = tokens[i] if i < len(tokens) else tokens[-1]
121
+ text = decoder(int(token))
122
+ if k_norms is not None and v_norms is not None:
123
+ text += f'\n{k_norms[i]}\n{v_norms[i]})'
124
+ image = text_under_image(image, text)
125
+ images.append(image)
126
+ return view_images(np.stack(images, axis=0))
127
+
128
+
129
+ def show_queries_keys(queries, keys, colors, labels): # [h ni d]
130
+ num_queries = [query.size(1) for query in queries]
131
+ num_keys = [key.size(1) for key in keys]
132
+ h, _, d = queries[0].shape
133
+
134
+ data = torch.cat((*queries, *keys), dim=1) # h n d
135
+ data = data.permute(1, 0, 2) # n h d
136
+ data = data.reshape(-1, h * d).detach().cpu().numpy()
137
+ pca = PCA(n_components=2)
138
+ data = pca.fit_transform(data) # n 2
139
+
140
+ query_indices = np.array(num_queries).cumsum()
141
+ total_num_queries = query_indices[-1]
142
+ queries = np.split(data[:total_num_queries], query_indices[:-1])
143
+ if len(num_keys) == 0:
144
+ keys = [None, ] * len(labels)
145
+ else:
146
+ key_indices = np.array(num_keys).cumsum()
147
+ keys = np.split(data[total_num_queries:], key_indices[:-1])
148
+
149
+ fig, ax = plt.subplots()
150
+ marker_size = plt.rcParams['lines.markersize'] ** 2
151
+ query_size = int(1.25 * marker_size)
152
+ key_size = int(2 * marker_size)
153
+ for query, key, color, label in zip(queries, keys, colors, labels):
154
+ print(f'# queries of {label}', query.shape[0])
155
+ ax.scatter(query[:, 0], query[:, 1], s=query_size, color=color, marker='o', label=f'"{label}" queries')
156
+
157
+ if key is None:
158
+ continue
159
+
160
+ print(f'# keys of {label}', key.shape[0])
161
+ keys_label = f'"{label}" key'
162
+ if key.shape[0] > 1:
163
+ keys_label += 's'
164
+ ax.scatter(key[:, 0], key[:, 1], s=key_size, color=color, marker='x', label=keys_label)
165
+
166
+ ax.set_axis_off()
167
+ #ax.set_xlabel('X-axis')
168
+ #ax.set_ylabel('Y-axis')
169
+ #ax.set_title('Scatter Plot with Circles and Crosses')
170
+
171
+ #ax.legend()
172
+ return fig
173
+
174
+
175
+ def show_self_attention(attention_maps): # h n m
176
+ attention_maps = attention_maps.transpose(0, 1).flatten(start_dim=1).detach().cpu().numpy()
177
+ pca = PCA(n_components=3)
178
+ pca_img = pca.fit_transform(attention_maps) # N X 3
179
+ h = w = int(pca_img.shape[0] ** 0.5)
180
+ pca_img = pca_img.reshape(h, w, 3)
181
+ pca_img_min = pca_img.min(axis=(0, 1))
182
+ pca_img_max = pca_img.max(axis=(0, 1))
183
+ pca_img = (pca_img - pca_img_min) / (pca_img_max - pca_img_min)
184
+ pca_img = Image.fromarray((pca_img * 255).astype(np.uint8))
185
+ pca_img = transforms.Resize(256, interpolation=transforms.InterpolationMode.NEAREST)(pca_img)
186
+ return pca_img
187
+
188
+
189
+ def draw_box(pil_img, bboxes, colors=None, width=5):
190
+ draw = ImageDraw.Draw(pil_img)
191
+ #font = ImageFont.truetype('./FreeMono.ttf', 25)
192
+ w, h = pil_img.size
193
+ colors = ['red'] * len(bboxes) if colors is None else colors
194
+ for obj_bbox, color in zip(bboxes, colors):
195
+ x_0, y_0, x_1, y_1 = obj_bbox[0], obj_bbox[1], obj_bbox[2], obj_bbox[3]
196
+ draw.rectangle([int(x_0 * w), int(y_0 * h), int(x_1 * w), int(y_1 * h)], outline=color, width=width)
197
+ return pil_img
198
+
199
+
200
+ def show_individual_self_attention(attn, point, image_path=None):
201
+ resolution = int(attn.size(-1) ** 0.5)
202
+ attn = attn.mean(dim=0).reshape(resolution, resolution, resolution, resolution)
203
+ attn = attn[round(point[1] * resolution), round(point[0] * resolution)]
204
+ attn = (attn - attn.min()) / (attn.max() - attn.min())
205
+ image = None if image_path is None else Image.open(image_path).convert('RGB')
206
+ image = show_image_relevance(attn, image=image)
207
+ return Image.fromarray(image)
208
+
209
+
210
+ def show_image_relevance(image_relevance, image: Image.Image = None, relevnace_res=16):
211
+ # create heatmap from mask on image
212
+ def show_cam_on_image(img, mask):
213
+ img = img.resize((relevnace_res ** 2, relevnace_res ** 2))
214
+ img = np.array(img)
215
+ img = (img - img.min()) / (img.max() - img.min())
216
+ heatmap = cv2.applyColorMap(np.uint8(255 * mask), cv2.COLORMAP_JET)
217
+ heatmap = np.float32(heatmap) / 255
218
+ cam = heatmap + np.float32(img)
219
+ cam = cam / np.max(cam)
220
+ return cam
221
+
222
+ image_relevance = image_relevance.reshape(1, 1, image_relevance.shape[-1], image_relevance.shape[-1])
223
+ image_relevance = image_relevance.cuda() # because float16 precision interpolation is not supported on cpu
224
+ image_relevance = torch.nn.functional.interpolate(image_relevance, size=relevnace_res ** 2, mode='bilinear')
225
+ image_relevance = image_relevance.cpu() # send it back to cpu
226
+ image_relevance = (image_relevance - image_relevance.min()) / (image_relevance.max() - image_relevance.min())
227
+ image_relevance = image_relevance.reshape(relevnace_res ** 2, relevnace_res ** 2)
228
+ vis = image_relevance if image is None else show_cam_on_image(image, image_relevance)
229
+ vis = np.uint8(255 * vis)
230
+ vis = cv2.cvtColor(np.array(vis), cv2.COLOR_RGB2BGR)
231
+ return vis